From hpk at codespeak.net Tue Feb 10 17:07:05 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 10 Feb 2009 17:07:05 +0100 (CET) Subject: [py-svn] r61698 - in py/branch/pytestplugin/py/test: plugin testing Message-ID: <20090210160705.E4DE016A0DE@codespeak.net> Author: hpk Date: Tue Feb 10 17:07:05 2009 New Revision: 61698 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/testing/test_collect.py Log: using the plugin-provided per-method tmpdir Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Tue Feb 10 17:07:05 2009 @@ -35,6 +35,9 @@ if not impname in self._plugins: self._plugins.append(impname) + def parseconfig(self, *args): + return py.test.config._reparse(list(args)) + def _writeconftest(self): p = self.tmpdir.join("conftest.py") pstring = repr(self._plugins) @@ -148,24 +151,26 @@ self.fstester = FSTester(self.tmpdir) #self.capture = py.io.StdCapture() + def parseconfig(self, *args): + return py.test.config._reparse(list(args)) + #def finalize(self): # self.capture.reset() # def genitems(self, colitems): return self.session.genitems(colitems) - def parseconfig(self, *args): - return py.test.config._reparse(list(args)) - def getitem(self, source, funcname="test_func"): modcol = self.getmodulecol(source) item = modcol.join(funcname) assert item is not None, "%r item not found in module:\n%s" %(funcname, source) return item - def getmodulecol(self, source, configargs=(), withsession=False): + def getmodulecol(self, source, configargs=(), withinit=False): kw = {self.pyfuncitem.name: py.code.Source(source).strip()} path = self.fstester.makepyfile(**kw) + if withinit: + self.fstester.makepyfile(__init__ = "#") self.config = self.parseconfig(path, *configargs) self.session = self.config.initsession() return self.config.getfsnode(path) @@ -177,3 +182,7 @@ elif argname == "tsession": tsession = TSession(pyfuncitem) return tsession, None + elif argname == "fstester": + tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) + fstester = FSTester(tmpdir) + return fstester, None Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Tue Feb 10 17:07:05 2009 @@ -7,6 +7,7 @@ from test_config import getcolitems from py.__.test.pycollect import DoctestFileContent + class DummyConfig: def __init__(self): self._conftest = Conftest() @@ -18,7 +19,6 @@ return self._conftest.rget(name, fspath) def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(mod.__name__) mod.dummyconfig = DummyConfig() def test_collect_versus_item(): @@ -26,47 +26,47 @@ assert not issubclass(Collector, Item) assert not issubclass(Item, Collector) -def test_ignored_certain_directories(): - tmp = py.test.ensuretemp("ignore_certain_directories") - tmp.ensure("_darcs", 'test_notfound.py') - tmp.ensure("CVS", 'test_notfound.py') - tmp.ensure("{arch}", 'test_notfound.py') - tmp.ensure(".whatever", 'test_notfound.py') - tmp.ensure(".bzr", 'test_notfound.py') - tmp.ensure("normal", 'test_found.py') - tmp.ensure('test_found.py') +def test_ignored_certain_directories(tmpdir): + tmpdir.ensure("_darcs", 'test_notfound.py') + tmpdir.ensure("CVS", 'test_notfound.py') + tmpdir.ensure("{arch}", 'test_notfound.py') + tmpdir.ensure(".whatever", 'test_notfound.py') + tmpdir.ensure(".bzr", 'test_notfound.py') + tmpdir.ensure("normal", 'test_found.py') + tmpdir.ensure('test_found.py') - col = py.test.collect.Directory(tmp, config=dummyconfig) + col = py.test.collect.Directory(tmpdir, config=dummyconfig) items = col.collect() names = [x.name for x in items] assert len(items) == 2 assert 'normal' in names assert 'test_found.py' in names -class TestCollect(suptest.InlineCollection): - def test_failing_import(self): - modcol = self.getmodulecol("import alksdjalskdjalkjals") +class TestCollect: + def test_failing_import(self, tsession): + modcol = tsession.getmodulecol("import alksdjalskdjalkjals") py.test.raises(ImportError, modcol.collect) py.test.raises(ImportError, modcol.collect) py.test.raises(ImportError, modcol.run) - def test_syntax_error_in_module(self): - modcol = self.getmodulecol("this is a syntax error") + def test_syntax_error_in_module(self, tsession): + modcol = tsession.getmodulecol("this is a syntax error") py.test.raises(SyntaxError, modcol.collect) py.test.raises(SyntaxError, modcol.collect) py.test.raises(SyntaxError, modcol.run) - def test_listnames_and__getitembynames(self): - modcol = self.getmodulecol("pass") + def test_listnames_and__getitembynames(self, tsession): + modcol = tsession.getmodulecol("pass", withinit=True) names = modcol.listnames() + print names dircol = modcol._config.getfsnode(modcol._config.topdir) x = dircol._getitembynames(names) assert modcol.name == x.name assert modcol.name == x.name - def test_listnames_getitembynames_custom(self): - hello = self._makefile(".xxx", hello="world") - self.makepyfile(conftest=""" + def test_listnames_getitembynames_custom(self, fstester): + hello = fstester._makefile(".xxx", hello="world") + fstester.makepyfile(conftest=""" import py class CustomFile(py.test.collect.File): pass @@ -75,7 +75,7 @@ return [CustomFile(self.fspath.join("hello.xxx"), parent=self)] Directory = MyDirectory """) - config = self.parseconfig(hello) + config = fstester.parseconfig(hello) node = config.getfsnode(hello) assert isinstance(node, py.test.collect.File) assert node.name == "hello.xxx" @@ -84,16 +84,16 @@ node = dircol._getitembynames(names) assert isinstance(node, py.test.collect.File) - def test_found_certain_testfiles(self): - p1 = self.makepyfile(test_found = "pass", found_test="pass") + def test_found_certain_testfiles(self, fstester): + p1 = fstester.makepyfile(test_found = "pass", found_test="pass") col = py.test.collect.Directory(p1.dirpath(), config=dummyconfig) items = col.collect() # Directory collect returns files sorted by name assert len(items) == 2 assert items[1].name == 'test_found.py' assert items[0].name == 'found_test.py' - def test_disabled_class(self): - modcol = self.getmodulecol(""" + def test_disabled_class(self, tsession): + modcol = tsession.getmodulecol(""" class TestClass: disabled = True def test_method(self): @@ -105,8 +105,8 @@ assert isinstance(modcol, py.test.collect.Class) assert not modcol.collect() - def test_disabled_module(self): - modcol = self.getmodulecol(""" + def test_disabled_module(self, tsession): + modcol = tsession.getmodulecol(""" disabled = True def setup_module(mod): raise ValueError @@ -114,8 +114,8 @@ assert not modcol.collect() assert not modcol.run() - def test_generative_functions(self): - modcol = self.getmodulecol(""" + def test_generative_functions(self, tsession): + modcol = tsession.getmodulecol(""" def func1(arg, arg2): assert arg == arg2 @@ -134,8 +134,8 @@ assert gencolitems[0].name == '[0]' assert gencolitems[0].obj.func_name == 'func1' - def test_generative_methods(self): - modcol = self.getmodulecol(""" + def test_generative_methods(self, tsession): + modcol = tsession.getmodulecol(""" def func1(arg, arg2): assert arg == arg2 class TestGenMethods: @@ -152,8 +152,8 @@ assert gencolitems[0].name == '[0]' assert gencolitems[0].obj.func_name == 'func1' - def test_generative_functions_with_explicit_names(self): - modcol = self.getmodulecol(""" + def test_generative_functions_with_explicit_names(self, tsession): + modcol = tsession.getmodulecol(""" def func1(arg, arg2): assert arg == arg2 @@ -174,8 +174,8 @@ assert gencolitems[1].name == "['fortytwo']" assert gencolitems[1].obj.func_name == 'func1' - def test_generative_methods_with_explicit_names(self): - modcol = self.getmodulecol(""" + def test_generative_methods_with_explicit_names(self, tsession): + modcol = tsession.getmodulecol(""" def func1(arg, arg2): assert arg == arg2 class TestGenMethods: @@ -194,8 +194,8 @@ assert gencolitems[1].name == "['m2']" assert gencolitems[1].obj.func_name == 'func1' - def test_module_assertion_setup(self): - modcol = self.getmodulecol("pass") + def test_module_assertion_setup(self, tsession): + modcol = tsession.getmodulecol("pass") from py.__.magic import assertion l = [] py.magic.patch(assertion, "invoke", lambda: l.append(None)) @@ -213,8 +213,8 @@ x = l.pop() assert x is None - def test_check_equality_and_cmp_basic(self): - modcol = self.getmodulecol(""" + def test_check_equality_and_cmp_basic(self, tsession): + modcol = tsession.getmodulecol(""" def test_pass(): pass def test_fail(): assert 0 """) @@ -244,39 +244,39 @@ assert [1,2,3] != fn assert modcol != fn - def test_directory_file_sorting(self): - p1 = self.makepyfile(test_one="hello") + def test_directory_file_sorting(self, fstester): + p1 = fstester.makepyfile(test_one="hello") p1.dirpath().mkdir("x") p1.dirpath().mkdir("dir1") - self.makepyfile(test_two="hello") + fstester.makepyfile(test_two="hello") p1.dirpath().mkdir("dir2") - config = self.parseconfig() + config = fstester.parseconfig() col = config.getfsnode(p1.dirpath()) names = [x.name for x in col.collect()] assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"] - def test_collector_deprecated_run_method(self): - modcol = self.getmodulecol("pass") + def test_collector_deprecated_run_method(self, tsession): + modcol = tsession.getmodulecol("pass") res1 = py.test.deprecated_call(modcol.run) res2 = modcol.collect() assert res1 == [x.name for x in res2] - def test_allow_sane_sorting_for_decorators(self): - modcol = self.getmodulecol(""" - def dec(f): - g = lambda: f(2) - g.place_as = f - return g + def test_allow_sane_sorting_for_decorators(self, tsession): + modcol = tsession.getmodulecol(""" + def dec(f): + g = lambda: f(2) + g.place_as = f + return g - def test_a(y): - pass - test_a = dec(test_a) - - def test_b(y): - pass - test_b = dec(test_b) - """) + def test_a(y): + pass + test_a = dec(test_a) + + def test_b(y): + pass + test_b = dec(test_b) + """) colitems = modcol.collect() assert len(colitems) == 2 f1, f2 = colitems @@ -358,13 +358,13 @@ assert item.name == "hello.xxx" assert item.__class__.__name__ == "CustomItem" -def test_module_file_not_found(): +def test_module_file_not_found(tmpdir): fn = tmpdir.join('nada','no') col = py.test.collect.Module(fn, config=dummyconfig) py.test.raises(py.error.ENOENT, col.collect) -def test_order_of_execution_generator_same_codeline(): +def test_order_of_execution_generator_same_codeline(tmpdir): o = tmpdir.ensure('genorder1', dir=1) o.join("test_order1.py").write(py.code.Source(""" def test_generative_order_of_execution(): @@ -388,7 +388,7 @@ assert passed == 7 assert not skipped and not failed -def test_order_of_execution_generator_different_codeline(): +def test_order_of_execution_generator_different_codeline(tmpdir): o = tmpdir.ensure('genorder2', dir=2) o.join("test_genorder2.py").write(py.code.Source(""" def test_generative_tests_different_codeline(): @@ -419,7 +419,7 @@ assert passed == 4 assert not skipped and not failed -def test_function_equality(): +def test_function_equality(tmpdir): config = py.test.config._reparse([tmpdir]) f1 = py.test.collect.Function(name="name", config=config, args=(1,), callobj=isinstance) @@ -534,14 +534,10 @@ assert isinstance(items[0], DoctestFileContent) class TestCollector: - def setup_method(self, method): - self.tmpdir = py.test.ensuretemp("%s_%s" % - (self.__class__.__name__, method.__name__)) - - def test_totrail_and_back(self): - a = self.tmpdir.ensure("a", dir=1) - self.tmpdir.ensure("a", "__init__.py") - x = self.tmpdir.ensure("a", "trail.py") + def test_totrail_and_back(self, tmpdir): + a = tmpdir.ensure("a", dir=1) + tmpdir.ensure("a", "__init__.py") + x = tmpdir.ensure("a", "trail.py") config = py.test.config._reparse([x]) col = config.getfsnode(x) trail = col._totrail() @@ -551,8 +547,8 @@ col2 = py.test.collect.Collector._fromtrail(trail, config) assert col2.listnames() == col.listnames() - def test_totrail_topdir_and_beyond(self): - config = py.test.config._reparse([self.tmpdir]) + def test_totrail_topdir_and_beyond(self, tmpdir): + config = py.test.config._reparse([tmpdir]) col = config.getfsnode(config.topdir) trail = col._totrail() assert len(trail) == 2 From hpk at codespeak.net Wed Feb 11 11:13:42 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 11:13:42 +0100 (CET) Subject: [py-svn] r61706 - py/branch/pytestplugin/py/test/testing Message-ID: <20090211101342.689AF169EA3@codespeak.net> Author: hpk Date: Wed Feb 11 11:13:39 2009 New Revision: 61706 Added: py/branch/pytestplugin/py/test/testing/conftest.py (contents, props changed) Log: forgot to checkin file that activates plugins (you can specify plugins in a conftest.py) Added: py/branch/pytestplugin/py/test/testing/conftest.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/testing/conftest.py Wed Feb 11 11:13:39 2009 @@ -0,0 +1,2 @@ + +pytest_plugins = "pytest_pytester", "pytest_tmpdir" From hpk at codespeak.net Wed Feb 11 12:22:13 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 12:22:13 +0100 (CET) Subject: [py-svn] r61710 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090211112213.605AD169E86@codespeak.net> Author: hpk Date: Wed Feb 11 12:22:12 2009 New Revision: 61710 Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Log: - use new arg facilities for testing xfail plugin - add terminal plugin API to apicheck Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Wed Feb 11 12:22:12 2009 @@ -111,13 +111,21 @@ the finalizer (if not None) will be called after the test function has been executed (i.e. pyfuncitem.execute() returns). """ - def pytest_termreport_result(self, event): - """ return (category, short, verbose) information about the given result event. - ``category`` will be used for counting tests and - pytest_termreport_summary will be called for each category. - ``short`` will be used for printing progress info like "...F.." - ``verbose`` is used for printing verbose information. - """ + + # from pytest_terminal plugin + def pytest_report_teststatus(self, event): + """ return shortletter and verbose word. """ + + def pytest_terminal_summary(self, terminalreporter): + """ add additional section in terminal summary reporting. """ + + #def pytest_termreport_result(self, event): + # """ return (category, short, verbose) information about the given result event. + # ``category`` will be used for counting tests and + # pytest_termreport_summary will be called for each category. + # ``short`` will be used for printing progress info like "...F.." + # ``verbose`` is used for printing verbose information. + # """ # =============================================================================== # plugin tests Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Wed Feb 11 12:22:12 2009 @@ -40,26 +40,24 @@ # # =============================================================================== -from py.__.test.testing import plugintester +from py.__.test.testing.suptest import assert_lines_contain_lines -def test_generic(): - impname = "py.__.test.plugin.pytest_xfail" - plugintester.nocalls(impname) +def test_generic(plugintester): + impname = "pytest_xfail" + plugintester.apicheck(impname) -from py.__.test.testing.acceptance_test import AcceptBase, assert_lines_contain_lines - -class TestXFailAcceptance(AcceptBase): - def test_xfail(self): - p = self.makepyfile(test_one=""" - import py - @py.test.keywords(xfail=True) - def test_this(): - assert 0 - """) - self.makepyfile(conftest="""pytest_plugins='py.__.test.plugin.pytest_xfail',""") - result = self.runpytest(p) - extra = assert_lines_contain_lines(result.outlines, [ - "*XFAILURES*", - "*test_one.py:4: assert 0*", - ]) - assert result.ret == 1 +def test_xfail(plugintester, linecomp): + fstester = plugintester.fstester() + p = fstester.makepyfile(test_one=""" + import py + pytest_plugins="pytest_xfail", + @py.test.keywords(xfail=True) + def test_this(): + assert 0 + """) + result = fstester.runpytest(p) + extra = assert_lines_contain_lines(result.outlines, [ + "*XFAILURES*", + "*test_one.py:5: assert 0*", + ]) + assert result.ret == 1 From hpk at codespeak.net Wed Feb 11 13:19:12 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 13:19:12 +0100 (CET) Subject: [py-svn] r61721 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090211121912.70A3C169EC9@codespeak.net> Author: hpk Date: Wed Feb 11 13:19:12 2009 New Revision: 61721 Added: py/branch/pytestplugin/py/test/plugin/pytest_unittest.py (contents, props changed) Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Log: port unittest-support conftest to become a plugin add a bit of plugin testing support code Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Wed Feb 11 13:19:12 2009 @@ -51,7 +51,7 @@ while methods: name, method = methods.popitem() if name not in hooks: - print "definition of unknown hook: %s" % name + print "found unknown hook: %s" % name fail = True else: hook = hooks[name] @@ -112,6 +112,10 @@ function has been executed (i.e. pyfuncitem.execute() returns). """ + def pytest_pymodule_makeitem(self, modcol, name, obj): + """ return custom item/collector or None. """ + + # from pytest_terminal plugin def pytest_report_teststatus(self, event): """ return shortletter and verbose word. """ Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 11 13:19:12 2009 @@ -4,6 +4,9 @@ import py +from py.__.test import event + + class RunResult: def __init__(self, ret, outlines, errlines): self.ret = ret @@ -35,6 +38,15 @@ if not impname in self._plugins: self._plugins.append(impname) + def parse_and_run(self, *args): + config = self.parseconfig(*args) + config.pluginmanager.configure(config) + session = config.initsession() + sorter = EventSorter(config, session) + session.main() + config.pluginmanager.unconfigure(config) + return sorter + def parseconfig(self, *args): return py.test.config._reparse(list(args)) @@ -186,3 +198,63 @@ tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) fstester = FSTester(tmpdir) return fstester, None + +class EventSorter(object): + def __init__(self, config, session=None): + self.config = config + self.session = session + self.cls2events = d = {} + def app(event): + print "[event]", event + for cls in py.std.inspect.getmro(event.__class__): + if cls is not object: + d.setdefault(cls, []).append(event) + session.bus.subscribe(app) + + def get(self, cls): + return self.cls2events.get(cls, []) + + def listoutcomes(self): + passed = [] + skipped = [] + failed = [] + for ev in self.get(event.ItemTestReport): + if ev.passed: + passed.append(ev) + elif ev.skipped: + skipped.append(ev) + elif ev.failed: + failed.append(ev) + return passed, skipped, failed + + def countoutcomes(self): + return map(len, self.listoutcomes()) + + def assertoutcome(self, passed=0, skipped=0, failed=0): + realpassed, realskipped, realfailed = self.listoutcomes() + assert passed == len(realpassed) + assert skipped == len(realskipped) + assert failed == len(realfailed) + + def getfailedcollections(self): + l = [] + for ev in self.get(event.CollectionReport): + if ev.failed: + l.append(ev) + return l + + def getreport(self, inamepart): + """ return a testreport whose dotted import path matches """ + __tracebackhide__ = True + l = [] + for rep in self.get(event.ItemTestReport): + if inamepart in rep.colitem.listnames(): + l.append(rep) + if not l: + raise ValueError("could not find test report matching %r: no test reports at all!" % + (inamepart,)) + if len(l) > 1: + raise ValueError("found more than one testreport matching %r: %s" %( + inamepart, l)) + return l[0] + Added: py/branch/pytestplugin/py/test/plugin/pytest_unittest.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/plugin/pytest_unittest.py Wed Feb 11 13:19:12 2009 @@ -0,0 +1,123 @@ +""" +automatically collect and run traditional "unittest.py" style tests. + +you can mix unittest TestCase subclasses and +py.test style tests in one test module. + +XXX consider user-specified test_suite() + +this code is somewhat derived from Guido Wesdorps + + http://johnnydebris.net/svn/projects/py_unittest + +$HeadURL: https://codespeak.net/svn/py/branch/pytestplugin/contrib/py_unittest/conftest.py $ +$Id: conftest.py 60979 2009-01-14 22:29:32Z hpk $ +""" +import py + +class Unittest: + """ Plugin for discovering and integrating traditional Python Unittest tests. + """ + def pytest_pymodule_makeitem(self, modcol, name, obj): + if py.std.inspect.isclass(obj) and issubclass(obj, py.std.unittest.TestCase): + return UnitTestCase(name, parent=modcol) + +class UnitTestCase(py.test.collect.Class): + def collect(self): + return [UnitTestCaseInstance("()", self)] + + def setup(self): + pass + + def teardown(self): + pass + +_dummy = object() +class UnitTestCaseInstance(py.test.collect.Instance): + def collect(self): + loader = py.std.unittest.TestLoader() + names = loader.getTestCaseNames(self.obj.__class__) + l = [] + for name in names: + callobj = getattr(self.obj, name) + if callable(callobj): + l.append(UnitTestFunction(name, parent=self)) + return l + + def _getobj(self): + x = self.parent.obj + return self.parent.obj(methodName='run') + +class UnitTestFunction(py.test.collect.Function): + def __init__(self, name, parent, args=(), obj=_dummy, sort_value=None): + super(UnitTestFunction, self).__init__(name, parent) + self._args = args + if obj is not _dummy: + self._obj = obj + self._sort_value = sort_value + + def runtest(self): + target = self.obj + args = self._args + target(*args) + + def setup(self): + instance = self.obj.im_self + instance.setUp() + + def teardown(self): + instance = self.obj.im_self + instance.tearDown() + + +def test_generic(plugintester): + plugintester.apicheck() + +def test_simple_unittest(fstester): + test_one = fstester.makepyfile(test_one=""" + import unittest + pytest_plugins = "pytest_unittest" # XXX + class MyTestCase(unittest.TestCase): + def testpassing(self): + self.assertEquals('foo', 'foo') + def test_failing(self): + self.assertEquals('foo', 'bar') + """) + sorter = fstester.parse_and_run(test_one) + assert sorter.getreport("testpassing").passed + assert sorter.getreport("test_failing").failed + +def test_setup(fstester): + test_one = fstester.makepyfile(test_two=""" + import unittest + pytest_plugins = "pytest_unittest" # XXX + class MyTestCase(unittest.TestCase): + def setUp(self): + self.foo = 1 + def test_setUp(self): + self.assertEquals(1, self.foo) + """) + sorter = fstester.parse_and_run(test_one) + rep = sorter.getreport("test_setUp") + assert rep.passed + +def test_teardown(fstester): + testpath = fstester.makepyfile(test_three=""" + import unittest + pytest_plugins = "pytest_unittest" # XXX + class MyTestCase(unittest.TestCase): + l = [] + def test_one(self): + pass + def tearDown(self): + self.l.append(None) + class Second(unittest.TestCase): + def test_check(self): + self.assertEquals(MyTestCase.l, [None]) + """) + sorter = fstester.parse_and_run(testpath) + passed, skipped, failed = sorter.countoutcomes() + assert passed + skipped + failed == 2 + assert failed == 0, failed + assert passed == 2 + From hpk at codespeak.net Wed Feb 11 13:20:59 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 13:20:59 +0100 (CET) Subject: [py-svn] r61722 - in py/branch/pytestplugin: contrib/py_unittest py/test Message-ID: <20090211122059.9D308169ED9@codespeak.net> Author: hpk Date: Wed Feb 11 13:20:59 2009 New Revision: 61722 Removed: py/branch/pytestplugin/contrib/py_unittest/ Modified: py/branch/pytestplugin/py/test/pycollect.py Log: 61721 was missing this: inokve the plugin hook and remove the original py_unittest conftest approach from contrib Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Wed Feb 11 13:20:59 2009 @@ -137,6 +137,10 @@ return self.join(name) def makeitem(self, name, obj, usefilters=True): + res = self._config.pluginmanager.callfirst( + "pytest_pymodule_makeitem", modcol=self, name=name, obj=obj) + if res is not None: + return res if (not usefilters or self.classnamefilter(name)) and \ py.std.inspect.isclass(obj): res = self._deprecated_join(name) From hpk at codespeak.net Wed Feb 11 13:54:31 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 13:54:31 +0100 (CET) Subject: [py-svn] r61723 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090211125431.D00E9169ECD@codespeak.net> Author: hpk Date: Wed Feb 11 13:54:28 2009 New Revision: 61723 Modified: py/branch/pytestplugin/py/test/collect.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/plugin/conftest.py py/branch/pytestplugin/py/test/plugin/pytest_unittest.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/testing/test_collect.py py/branch/pytestplugin/py/test/testing/test_deprecated_api.py Log: simplify collection api by substituting the passing around of "usefilters" args with a direct check if files were specified on the cmdline Modified: py/branch/pytestplugin/py/test/collect.py ============================================================================== --- py/branch/pytestplugin/py/test/collect.py (original) +++ py/branch/pytestplugin/py/test/collect.py Wed Feb 11 13:54:28 2009 @@ -409,40 +409,47 @@ return l l = [] for path in self.fspath.listdir(sort=True): - res = self.consider(path, usefilters=True) + res = self.consider(path) if res is not None: l.append(res) return l - def consider(self, path, usefilters=True): + def consider(self, path): if path.check(file=1): - return self.consider_file(path, usefilters=usefilters) + return self.consider_file(path) elif path.check(dir=1): - return self.consider_dir(path, usefilters=usefilters) + return self.consider_dir(path) - def consider_file(self, path, usefilters=True): + def consider_file(self, path): ext = path.ext pb = path.purebasename - if not usefilters or pb.startswith("test_") or pb.endswith("_test"): + if pb.startswith("test_") or pb.endswith("_test") or \ + path in self._config.args: if ext == ".py": return self.Module(path, parent=self) elif ext == ".txt": return self.DoctestFile(path, parent=self) - def consider_dir(self, path, usefilters=True): - if not usefilters or self.recfilter(path): - # not use self.Directory here as - # dir/conftest.py shall be able to - # define Directory(dir) already - Directory = self._config.getvalue('Directory', path) - return Directory(path, parent=self) + def consider_dir(self, path): + if not self.recfilter(path): + # check if cmdline specified this dir or a subdir + for arg in self._config.args: + if path == arg or arg.relto(path): + break + else: + return + # not use self.Directory here as + # dir/conftest.py shall be able to + # define Directory(dir) already + Directory = self._config.getvalue('Directory', path) + return Directory(path, parent=self) def collect_by_name(self, name): """ get a child with the given name. """ res = super(Directory, self).collect_by_name(name) if res is None: p = self.fspath.join(name) - res = self.consider(p, usefilters=False) + res = self.consider(p) return res from py.__.test.runner import basic_run_report, forked_run_report Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Wed Feb 11 13:54:28 2009 @@ -55,7 +55,7 @@ if not args: args.append(py.std.os.getcwd()) self.topdir = gettopdir(args) - self.args = args + self.args = [py.path.local(x) for x in args] # config objects are usually pickled across system # barriers but they contain filesystem paths. Modified: py/branch/pytestplugin/py/test/plugin/conftest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/conftest.py (original) +++ py/branch/pytestplugin/py/test/plugin/conftest.py Wed Feb 11 13:54:28 2009 @@ -3,6 +3,6 @@ pytest_plugins = "pytest_pytester", "pytest_plugintester" class Directory(py.test.collect.Directory): - def consider_file(self, path, usefilters): + def consider_file(self, path): if path.basename.startswith("pytest_") and path.ext == ".py": return self.Module(path, parent=self) Modified: py/branch/pytestplugin/py/test/plugin/pytest_unittest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_unittest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_unittest.py Wed Feb 11 13:54:28 2009 @@ -74,7 +74,7 @@ plugintester.apicheck() def test_simple_unittest(fstester): - test_one = fstester.makepyfile(test_one=""" + testpath = fstester.makepyfile(test_simple_unittest=""" import unittest pytest_plugins = "pytest_unittest" # XXX class MyTestCase(unittest.TestCase): @@ -83,12 +83,12 @@ def test_failing(self): self.assertEquals('foo', 'bar') """) - sorter = fstester.parse_and_run(test_one) + sorter = fstester.parse_and_run(testpath) assert sorter.getreport("testpassing").passed assert sorter.getreport("test_failing").failed def test_setup(fstester): - test_one = fstester.makepyfile(test_two=""" + testpath = fstester.makepyfile(test_two=""" import unittest pytest_plugins = "pytest_unittest" # XXX class MyTestCase(unittest.TestCase): @@ -97,7 +97,7 @@ def test_setUp(self): self.assertEquals(1, self.foo) """) - sorter = fstester.parse_and_run(test_one) + sorter = fstester.parse_and_run(testpath) rep = sorter.getreport("test_setUp") assert rep.passed Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Wed Feb 11 13:54:28 2009 @@ -136,18 +136,18 @@ warnoldcollect() return self.join(name) - def makeitem(self, name, obj, usefilters=True): + def makeitem(self, name, obj): res = self._config.pluginmanager.callfirst( "pytest_pymodule_makeitem", modcol=self, name=name, obj=obj) if res is not None: return res - if (not usefilters or self.classnamefilter(name)) and \ + if (self.classnamefilter(name)) and \ py.std.inspect.isclass(obj): res = self._deprecated_join(name) if res is not None: return res return self.Class(name, parent=self) - elif (not usefilters or self.funcnamefilter(name)) and callable(obj): + elif self.funcnamefilter(name) and callable(obj): res = self._deprecated_join(name) if res is not None: return res Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Wed Feb 11 13:54:28 2009 @@ -12,6 +12,7 @@ def __init__(self): self._conftest = Conftest() self._setupstate = SetupState() + self.args = [] class dummyoption: nomagic = False self.option = dummyoption @@ -290,10 +291,10 @@ class MyFunction(py.test.collect.Function): pass class Directory(py.test.collect.Directory): - def consider_file(self, path, usefilters=True): + def consider_file(self, path): if path.check(fnmatch="check_*.py"): return self.Module(path, parent=self) - return super(Directory, self).consider_file(path, usefilters=usefilters) + return super(Directory, self).consider_file(path) class myfuncmixin: Function = MyFunction def funcnamefilter(self, name): @@ -340,7 +341,7 @@ def run(self): pass class Directory(py.test.collect.Directory): - def consider_file(self, fspath, usefilters=True): + def consider_file(self, fspath): if fspath.ext == ".xxx": return CustomItem(fspath.basename, parent=self) """) Modified: py/branch/pytestplugin/py/test/testing/test_deprecated_api.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_deprecated_api.py (original) +++ py/branch/pytestplugin/py/test/testing/test_deprecated_api.py Wed Feb 11 13:54:28 2009 @@ -143,10 +143,10 @@ def run(self): return [] class Directory(py.test.collect.Directory): - def consider_file(self, path, usefilters=True): + def consider_file(self, path): if path.basename == "testme.xxx": return Module(path, parent=self) - return super(Directory, self).consider_file(path, usefilters=usefilters) + return super(Directory, self).consider_file(path) """) testme = self._makefile('xxx', testme="hello") config = self.parseconfig(testme) From hpk at codespeak.net Wed Feb 11 19:08:50 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 19:08:50 +0100 (CET) Subject: [py-svn] r61748 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090211180850.9C73C169E98@codespeak.net> Author: hpk Date: Wed Feb 11 19:08:50 2009 New Revision: 61748 Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Log: extend API to makepyfile to allow for automatic naming for test modules Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Wed Feb 11 19:08:50 2009 @@ -32,8 +32,7 @@ # FSTester = self.pyfuncitem._config.pluginmanager.getpluginattr("pytester", "FSTester") from pytest_pytester import FSTester impname = self._getimpname() - tmpdir = py.test.ensuretemp(impname) - crunner = FSTester(tmpdir) + crunner = FSTester(self.pyfuncitem) crunner.ensureplugin(impname) return crunner Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 11 19:08:50 2009 @@ -29,10 +29,19 @@ return ret class FSTester(FileCreation): - def __init__(self, tmpdir): - self.tmpdir = tmpdir + def __init__(self, pyfuncitem): + self.pyfuncitem = pyfuncitem + self.tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) self._plugins = [] + def makepyfile(self, *args, **kwargs): + if args: + source = "\n".join(map(str, args)) + assert not kwargs + basename = self.pyfuncitem.name + return self._makefile('.py', **{basename: source}) + return self._makefile('.py', **kwargs) + def ensureplugin(self, impname): assert isinstance(impname, str) if not impname in self._plugins: @@ -159,8 +168,7 @@ class TSession: def __init__(self, pyfuncitem): self.pyfuncitem = pyfuncitem - self.tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) - self.fstester = FSTester(self.tmpdir) + self.fstester = FSTester(pyfuncitem) #self.capture = py.io.StdCapture() def parseconfig(self, *args): @@ -195,8 +203,7 @@ tsession = TSession(pyfuncitem) return tsession, None elif argname == "fstester": - tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) - fstester = FSTester(tmpdir) + fstester = FSTester(pyfuncitem) return fstester, None class EventSorter(object): From hpk at codespeak.net Wed Feb 11 19:09:20 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 19:09:20 +0100 (CET) Subject: [py-svn] r61750 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090211180920.E6FA5169E9C@codespeak.net> Author: hpk Date: Wed Feb 11 19:09:20 2009 New Revision: 61750 Added: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (contents, props changed) Log: add new plugin for sending failures to paste.pocoo.org Added: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Wed Feb 11 19:09:20 2009 @@ -0,0 +1,64 @@ +""" +py.test plugin for sending testing failure information to paste.pocoo.org +""" +import py + +class url: + base = "http://paste.pocoo.org" + xmlrpc = base + "/xmlrpc/" + show = base + "/show/" + +class Pocoo(object): + pytest_cmdlineoptions = [ + py.test.config.Option('--pocoo-sendfailures', + action='store_true', dest="pocoo_sendfailures", + help="send failures to %s" %(url.base,)) + ] + + def getproxy(self): + return py.std.xmlrpclib.ServerProxy(url.xmlrpc).pastes + + def pytest_terminal_summary(self, terminalreporter): + if terminalreporter.config.option.pocoo_sendfailures: + tr = terminalreporter + if "failed" in tr.status2event and tr.config.option.tbstyle != "no": + terminalreporter.write_sep("=", "Sending failures to %s" %(url.base,)) + terminalreporter.write_line("xmlrpcurl: %s" %(url.xmlrpc,)) + serverproxy = self.getproxy() + for ev in terminalreporter.status2event['failed']: + tw = py.io.TerminalWriter(stringio=True) + ev.toterminal(tw) + s = tw.stringio.getvalue() + # XXX add failure summary + assert len(s) + terminalreporter.write_line("newpaste() ...") + id = serverproxy.newPaste("python", s) + terminalreporter.write_line("%s%s\n" % (url.show, id)) + break + +def test_apicheck(plugintester): + plugintester.apicheck() + +def test_toproxy(fstester): + fstester.makepyfile(conftest="pytest_plugins='pytest_pocoo',") + testpath = fstester.makepyfile(""" + import py + def test_pass(): + pass + def test_fail(): + assert 0 + def test_skip(): + py.test.skip("") + """) + l = [] + class MockProxy: + def newPaste(self, language, code): + l.append((language, code)) + + old = Pocoo.getproxy + Pocoo.getproxy = MockProxy + try: + result = fstester.parse_and_run(testpath, "--pocoo-sendfailures") + finally: + Pocoo.getproxy = old + assert len(l) == 1 From hpk at codespeak.net Wed Feb 11 19:43:11 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 19:43:11 +0100 (CET) Subject: [py-svn] r61752 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090211184311.182CF169E47@codespeak.net> Author: hpk Date: Wed Feb 11 19:43:08 2009 New Revision: 61752 Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py py/branch/pytestplugin/py/test/plugin/pytest_unittest.py py/branch/pytestplugin/py/test/pmanage.py py/branch/pytestplugin/py/test/testing/test_pmanage.py Log: * allow to specify plugins without "pytest_" prefix * provide explicit plugin class to apicheck() Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Wed Feb 11 19:43:08 2009 @@ -27,7 +27,7 @@ # =============================================================================== def test_generic(plugintester): - plugintester.apicheck() + plugintester.apicheck(Eventlog) fstester = plugintester.fstester() fstester.makepyfile(test_one=""" Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Wed Feb 11 19:43:08 2009 @@ -20,8 +20,6 @@ for colitem in self.pyfuncitem.listchain(): if isinstance(colitem, colitem.Module): return colitem - def _getimpname(self): - return self.getmoditem().obj.__name__ def finalize(self): """ called after test function finished execution""" @@ -31,19 +29,17 @@ # XXX import differently, eg. # FSTester = self.pyfuncitem._config.pluginmanager.getpluginattr("pytester", "FSTester") from pytest_pytester import FSTester - impname = self._getimpname() crunner = FSTester(self.pyfuncitem) - crunner.ensureplugin(impname) + # + #crunner.ensureplugin(impname) return crunner - def apicheck(self, impname=None): + def apicheck(self, impclass): from py.__.test.pmanage import PluginManager - if not impname: - impname = self.getmoditem().obj.__name__ - print "loading and checking", impname + print "loading and checking", impclass fail = False pm = PluginManager() - plugin = pm.import_plugin(impname) + plugin = pm.import_plugin(impclass) methods = collectattr(plugin.__class__) hooks = collectattr(PytestPluginHooks) getargs = py.std.inspect.getargs @@ -135,4 +131,4 @@ # =============================================================================== def test_generic(plugintester): - plugintester.apicheck() + plugintester.apicheck(Plugintester) Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Wed Feb 11 19:43:08 2009 @@ -37,7 +37,7 @@ break def test_apicheck(plugintester): - plugintester.apicheck() + plugintester.apicheck(Pocoo) def test_toproxy(fstester): fstester.makepyfile(conftest="pytest_plugins='pytest_pocoo',") Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Wed Feb 11 19:43:08 2009 @@ -241,7 +241,7 @@ assert 'ValueError' in entry def test_generic(plugintester, linecomp): - plugintester.apicheck() + plugintester.apicheck(Resultlog) fstester = plugintester.fstester() fstester.makepyfile(test_one=""" import py Modified: py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py Wed Feb 11 19:43:08 2009 @@ -26,7 +26,7 @@ # =============================================================================== # def test_generic(plugintester): - plugintester.apicheck() + plugintester.apicheck(Tmpdir) def test_pyitemexecute_arg(): class Pseudoitem: Modified: py/branch/pytestplugin/py/test/plugin/pytest_unittest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_unittest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_unittest.py Wed Feb 11 19:43:08 2009 @@ -71,7 +71,7 @@ def test_generic(plugintester): - plugintester.apicheck() + plugintester.apicheck(Unittest) def test_simple_unittest(fstester): testpath = fstester.makepyfile(test_simple_unittest=""" Modified: py/branch/pytestplugin/py/test/pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/pmanage.py (original) +++ py/branch/pytestplugin/py/test/pmanage.py Wed Feb 11 19:43:08 2009 @@ -34,16 +34,17 @@ if not isinstance(importspec, basestring): return self.addpluginclass(importspec) else: - lastpart = importspec.split(".")[-1] modprefix = "pytest_" - if not lastpart.startswith(modprefix): - raise ValueError("in importspec %r: %r does not start with %r" - %(importspec, lastpart, modprefix)) + if not importspec.startswith(modprefix): + importspec = modprefix + importspec try: - mod = __import__(importspec, None, None, "__doc__") + mod = __import__(importspec) except ImportError, e: - mod = __import__("py.__.test.plugin.%s" %(importspec), None, None, '__doc__') - clsname = lastpart[len(modprefix):].capitalize() + try: + mod = __import__("py.__.test.plugin.%s" %(importspec), None, None, '__doc__') + except ImportError: + raise ImportError(importspec) + clsname = importspec[len(modprefix):].capitalize() pluginclass = getattr(mod, clsname) result = self.addpluginclass(pluginclass) self.consider_module(mod) Modified: py/branch/pytestplugin/py/test/testing/test_pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pmanage.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pmanage.py Wed Feb 11 19:43:08 2009 @@ -11,8 +11,8 @@ def test_import_plugin_importname(self): pm = PluginManager() - py.test.raises(ValueError, 'pm.import_plugin("x.y")') - py.test.raises(ValueError, 'pm.import_plugin("pytest_x.y")') + py.test.raises(ImportError, 'pm.import_plugin("x.y")') + py.test.raises(ImportError, 'pm.import_plugin("pytest_x.y")') sys.path.insert(0, str(self.tmpdir)) try: @@ -21,7 +21,8 @@ class Hello: pass """)) - pm.import_plugin(pluginname) + pm.import_plugin("hello") + pm.import_plugin("pytest_hello") plugin = pm.getplugin("hello") finally: sys.path.remove(str(self.tmpdir)) From hpk at codespeak.net Wed Feb 11 20:22:54 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 20:22:54 +0100 (CET) Subject: [py-svn] r61753 - in py/branch/pytestplugin/py: doc test test/testing Message-ID: <20090211192254.6D161169E51@codespeak.net> Author: hpk Date: Wed Feb 11 20:22:53 2009 New Revision: 61753 Modified: py/branch/pytestplugin/py/doc/impl-test.txt py/branch/pytestplugin/py/test/pmanage.py py/branch/pytestplugin/py/test/testing/test_pmanage.py Log: consider PYTEST_PLUGINS environment setting Modified: py/branch/pytestplugin/py/doc/impl-test.txt ============================================================================== --- py/branch/pytestplugin/py/doc/impl-test.txt (original) +++ py/branch/pytestplugin/py/doc/impl-test.txt Wed Feb 11 20:22:53 2009 @@ -279,9 +279,11 @@ specifying plugins for directories or test modules --------------------------------------------------------- -py.test loads plugins at tool startup and when first -importing a test module. It looks at the ``pytest_plugins`` -variable in the conftest or the test module. +py.test loads and configures plugins at tool startup and whenever +it encounters new confest or test modules which +contain a ``pytest_plugins`` definition. At tool +startup the ``PYTEST_PLUGINS`` environment variable +is considered as well. Example ++++++++++ Modified: py/branch/pytestplugin/py/test/pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/pmanage.py (original) +++ py/branch/pytestplugin/py/test/pmanage.py Wed Feb 11 20:22:53 2009 @@ -65,6 +65,14 @@ # API for interacting with registered and instantiated plugin objects # def add_cmdlineoptions(self, config): + #for mod in self.config._conftest.getconftestmodules(None): + # self.consider_module(mod) + envspec = py.std.os.environ.get("PYTEST_PLUGINS", None) + if envspec: + for spec in map(str.strip, envspec.split(",")): + if spec: + self.import_plugin(spec) + # XXX think about sorting/grouping of options from user-perspective opts = [] for name, options in self.listattr("pytest_cmdlineoptions"): Modified: py/branch/pytestplugin/py/test/testing/test_pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pmanage.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pmanage.py Wed Feb 11 20:22:53 2009 @@ -1,4 +1,4 @@ -import sys +import os, sys import py from py.__.test.pmanage import PluginManager from py.__.test.event import NOP @@ -187,3 +187,15 @@ assert opt assert opt.default == 242 + def test_addcmdlineoptions_considers_ENV(self): + pm = PluginManager() + SPEC = "PYTEST_PLUGINS" + old = os.environ.get(SPEC, None) + try: + os.environ[SPEC] = "test_addcmdlineoptions_considers_ENV" + py.test.raises(ImportError, "pm.add_cmdlineoptions(pytestConfig())") + finally: + if old is None: + del os.environ[SPEC] + else: + os.environ[SPEC] = old From hpk at codespeak.net Wed Feb 11 21:15:47 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 21:15:47 +0100 (CET) Subject: [py-svn] r61754 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090211201547.3E8E3169E3F@codespeak.net> Author: hpk Date: Wed Feb 11 21:15:45 2009 New Revision: 61754 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/pmanage.py py/branch/pytestplugin/py/test/testing/test_pmanage.py Log: smallr refactoring to allow conftests to add plugins as early as possible in order to have extended cmdlineoptions available. Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Wed Feb 11 21:15:45 2009 @@ -48,6 +48,7 @@ self._initialized = True adddefaultoptions(self) self._conftest.setinitial(args) + self.pluginmanager.setinitial(self._conftest.getconftestmodules(None)) self.pluginmanager.add_cmdlineoptions(self) args = [str(x) for x in args] cmdlineoption, args = self._parser.parse_args(args) Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 11 21:15:45 2009 @@ -13,6 +13,10 @@ self.outlines = outlines self.errlines = errlines + def assert_contain_lines(self, lines): + from py.__.test.testing.suptest import assert_lines_contain_lines + assert_lines_contain_lines(self.outlines, lines) + class FileCreation(object): def makepyfile(self, **kwargs): return self._makefile('.py', **kwargs) @@ -61,6 +65,8 @@ def _writeconftest(self): p = self.tmpdir.join("conftest.py") + if p.check(): + return # don't overwrite pstring = repr(self._plugins) p.write("import py ; pytest_plugins = %s" % pstring) #else: Modified: py/branch/pytestplugin/py/test/pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/pmanage.py (original) +++ py/branch/pytestplugin/py/test/pmanage.py Wed Feb 11 21:15:45 2009 @@ -64,15 +64,16 @@ # # API for interacting with registered and instantiated plugin objects # - def add_cmdlineoptions(self, config): - #for mod in self.config._conftest.getconftestmodules(None): - # self.consider_module(mod) + def setinitial(self, modules): + for module in modules: + self.consider_module(module) envspec = py.std.os.environ.get("PYTEST_PLUGINS", None) if envspec: for spec in map(str.strip, envspec.split(",")): if spec: self.import_plugin(spec) - + + def add_cmdlineoptions(self, config): # XXX think about sorting/grouping of options from user-perspective opts = [] for name, options in self.listattr("pytest_cmdlineoptions"): Modified: py/branch/pytestplugin/py/test/testing/test_pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pmanage.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pmanage.py Wed Feb 11 21:15:45 2009 @@ -4,7 +4,7 @@ from py.__.test.event import NOP from py.__.test.config import Config as pytestConfig -class TestPluginManager: +class TestInitialization: def setup_method(self, method): self.tmpdir = py.test.ensuretemp("%s.%s.%s" % (__name__, self.__class__.__name__, method.__name__)) @@ -107,6 +107,7 @@ myplugin2 = pm.getplugin("Plugin") assert myplugin1 is myplugin2 +class TestPluginInteraction: def test_callplugins(self): pm = PluginManager() class My: @@ -187,15 +188,34 @@ assert opt assert opt.default == 242 - def test_addcmdlineoptions_considers_ENV(self): + def test_setinitial_env(self): pm = PluginManager() - SPEC = "PYTEST_PLUGINS" - old = os.environ.get(SPEC, None) + KEY = "PYTEST_PLUGINS" + old = os.environ.get(KEY, None) try: - os.environ[SPEC] = "test_addcmdlineoptions_considers_ENV" - py.test.raises(ImportError, "pm.add_cmdlineoptions(pytestConfig())") + os.environ[KEY] = "test_setinitial" + py.test.raises(ImportError, "pm.setinitial([])") finally: if old is None: - del os.environ[SPEC] + del os.environ[KEY] else: - os.environ[SPEC] = old + os.environ[KEY] = old + + def test_conftest_specifies_plugin(self, fstester): + fstester.makepyfile( + conftest=""" + import py + class MyPlugin: + pytest_cmdlineoptions = [ + py.test.config.Option("--myplugin-option", dest="myplugin", + help="myplugin option", + ) + ] + pytest_plugins = MyPlugin + """ + ) + result = fstester.runpytest(fstester.tmpdir, '-h') + result.assert_contain_lines([ + "*--myplugin-option*", + ]) + From hpk at codespeak.net Wed Feb 11 21:19:07 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 21:19:07 +0100 (CET) Subject: [py-svn] r61755 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090211201907.BE438169E4E@codespeak.net> Author: hpk Date: Wed Feb 11 21:19:07 2009 New Revision: 61755 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Log: remove unused code Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 11 21:19:07 2009 @@ -63,24 +63,13 @@ def parseconfig(self, *args): return py.test.config._reparse(list(args)) - def _writeconftest(self): - p = self.tmpdir.join("conftest.py") - if p.check(): - return # don't overwrite - pstring = repr(self._plugins) - p.write("import py ; pytest_plugins = %s" % pstring) - #else: - # lines = p.readlines(cr=0) - # for i, line in py.builtin.enumerate(lines): - # if line.find("pytest_plugins") != -1: - # lines[i] = line + ", %s" % pstring - # break - # else: - # lines.append(pstring) - # p.write("\n".join(lines)) - def prepare(self): - self._writeconftest() + p = self.tmpdir.join("conftest.py") + if not p.check(): + pstring = repr(self._plugins) + p.write("import py ; pytest_plugins = %s" % pstring) + else: + assert not self._plugins def popen(self, cmdargs, stdout, stderr, **kw): if not hasattr(py.std, 'subprocess'): From hpk at codespeak.net Wed Feb 11 22:37:00 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 22:37:00 +0100 (CET) Subject: [py-svn] r61756 - in py/branch/pytestplugin/py/test: plugin testing Message-ID: <20090211213700.48586169E99@codespeak.net> Author: hpk Date: Wed Feb 11 22:36:58 2009 New Revision: 61756 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_xfail.py py/branch/pytestplugin/py/test/testing/acceptance_test.py py/branch/pytestplugin/py/test/testing/suptest.py py/branch/pytestplugin/py/test/testing/test_collect.py py/branch/pytestplugin/py/test/testing/test_pmanage.py Log: * removing duplicate code * using new testing args instead of inheritance Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 11 22:36:58 2009 @@ -3,19 +3,15 @@ """ import py - from py.__.test import event - class RunResult: def __init__(self, ret, outlines, errlines): self.ret = ret self.outlines = outlines self.errlines = errlines - - def assert_contain_lines(self, lines): - from py.__.test.testing.suptest import assert_lines_contain_lines - assert_lines_contain_lines(self.outlines, lines) + self.stdout = LineMatcher(outlines) + self.stderr = LineMatcher(errlines) class FileCreation(object): def makepyfile(self, **kwargs): @@ -38,13 +34,26 @@ self.tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) self._plugins = [] - def makepyfile(self, *args, **kwargs): + def _makefile(self, ext, args, kwargs): + items = kwargs.items() if args: source = "\n".join(map(str, args)) - assert not kwargs basename = self.pyfuncitem.name - return self._makefile('.py', **{basename: source}) - return self._makefile('.py', **kwargs) + items.insert(0, (basename, source)) + ret = None + for name, value in items: + p = self.tmpdir.join(name).new(ext=ext) + source = py.code.Source(value) + p.write(str(py.code.Source(value)).lstrip()) + if ret is None: + ret = p + return ret + + def makepyfile(self, *args, **kwargs): + return self._makefile('.py', args, kwargs) + + def maketxtfile(self, *args, **kwargs): + return self._makefile('.txt', args, kwargs) def ensureplugin(self, impname): assert isinstance(impname, str) @@ -128,37 +137,7 @@ val = lines1.getvalue() lines1.truncate(0) # remove what we got lines1 = val.split("\n") - if isinstance(lines2, str): - lines2 = py.code.Source(lines2) - if isinstance(lines2, py.code.Source): - lines2 = lines2.strip().lines - - extralines = [] - lines1 = lines1[:] - nextline = None - for line in lines2: - nomatchprinted = False - while lines1: - nextline = lines1.pop(0) - if line == nextline: - print "exact match:", repr(line) - break - elif fnmatch(nextline, line): - print "fnmatch:", repr(line) - print " with:", repr(nextline) - break - else: - if not nomatchprinted: - print "nomatch:", repr(line) - nomatchprinted = True - print " and:", repr(nextline) - extralines.append(nextline) - else: - if line != nextline: - #__tracebackhide__ = True - raise AssertionError("expected line not found: %r" % line) - extralines.extend(lines1) - return extralines + return LineMatcher(lines1).fnmatch_lines(lines2) class TSession: def __init__(self, pyfuncitem): @@ -260,3 +239,44 @@ inamepart, l)) return l[0] +class LineMatcher: + def __init__(self, lines): + self.lines = lines + + def str(self): + return "\n".join(self.lines) + + def fnmatch_lines(self, lines2): + if isinstance(lines2, str): + lines2 = py.code.Source(lines2) + if isinstance(lines2, py.code.Source): + lines2 = lines2.strip().lines + + from fnmatch import fnmatch + __tracebackhide__ = True + lines1 = self.lines[:] + nextline = None + extralines = [] + for line in lines2: + nomatchprinted = False + while lines1: + nextline = lines1.pop(0) + if line == nextline: + print "exact match:", repr(line) + break + elif fnmatch(nextline, line): + print "fnmatch:", repr(line) + print " with:", repr(nextline) + break + else: + if not nomatchprinted: + print "nomatch:", repr(line) + nomatchprinted = True + print " and:", repr(nextline) + extralines.append(nextline) + else: + if line != nextline: + #__tracebackhide__ = True + raise AssertionError("expected line not found: %r" % line) + extralines.extend(lines1) + return extralines Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Wed Feb 11 22:36:58 2009 @@ -40,8 +40,6 @@ # # =============================================================================== -from py.__.test.testing.suptest import assert_lines_contain_lines - def test_generic(plugintester): impname = "pytest_xfail" plugintester.apicheck(impname) @@ -56,7 +54,7 @@ assert 0 """) result = fstester.runpytest(p) - extra = assert_lines_contain_lines(result.outlines, [ + extra = result.stdout.fnmatch_lines([ "*XFAILURES*", "*test_one.py:5: assert 0*", ]) Modified: py/branch/pytestplugin/py/test/testing/acceptance_test.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/acceptance_test.py (original) +++ py/branch/pytestplugin/py/test/testing/acceptance_test.py Wed Feb 11 22:36:58 2009 @@ -1,110 +1,59 @@ import py -from suptest import assert_lines_contain_lines, FileCreation pydir = py.path.local(py.__file__).dirpath() pytestpath = pydir.join("bin", "py.test") EXPECTTIMEOUT=10.0 -def setup_module(mod): - mod.modtmpdir = py.test.ensuretemp(mod.__name__) - -class Result: - def __init__(self, ret, outlines, errlines): - self.ret = ret - self.outlines = outlines - self.errlines = errlines - -class AcceptBase(FileCreation): - def popen(self, cmdargs, stdout, stderr, **kw): - if not hasattr(py.std, 'subprocess'): - py.test.skip("no subprocess module") - return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) - - def run(self, *cmdargs): - cmdargs = map(str, cmdargs) - p1 = py.path.local("stdout") - p2 = py.path.local("stderr") - print "running", cmdargs, "curdir=", py.path.local() - popen = self.popen(cmdargs, stdout=p1.open("w"), stderr=p2.open("w")) - ret = popen.wait() - out, err = p1.readlines(cr=0), p2.readlines(cr=0) - if err: - for line in err: - print >>py.std.sys.stderr, line - return Result(ret, out, err) - - def runpybin(self, scriptname, *args): - bindir = py.path.local(py.__file__).dirpath("bin") - if py.std.sys.platform == "win32": - script = bindir.join("win32", scriptname + ".cmd") - else: - script = bindir.join(scriptname) - assert script.check() - return self.run(script, *args) - - def runpytest(self, *args): - return self.runpybin("py.test", *args) - - def setup_method(self, method): - super(AcceptBase, self).setup_method(method) - self.old = self.tmpdir.chdir() - - def teardown_method(self, method): - self.old.chdir() - -class TestPyTest(AcceptBase): - def test_assertion_magic(self): - p = self.makepyfile(test_one=""" +class TestPyTest: + def test_assertion_magic(self, fstester): + p = fstester.makepyfile(""" def test_this(): x = 0 assert x """) - result = self.runpytest(p) - extra = assert_lines_contain_lines(result.outlines, [ + result = fstester.runpytest(p) + extra = result.stdout.fnmatch_lines([ "> assert x", "E assert 0", ]) assert result.ret == 1 - - def test_collectonly_simple(self): - p = self.makepyfile(test_one=""" + def test_collectonly_simple(self, fstester): + p = fstester.makepyfile(""" def test_func1(): pass class TestClass: def test_method(self): pass """) - result = self.runpytest("--collectonly", p) - err = "".join(result.errlines) - assert err.strip().startswith("inserting into sys.path") + result = fstester.runpytest("--collectonly", p) + stderr = result.stderr.str().strip() + assert stderr.startswith("inserting into sys.path") assert result.ret == 0 - extra = assert_lines_contain_lines(result.outlines, py.code.Source(""" - + extra = result.stdout.fnmatch_lines(py.code.Source(""" + """).strip()) - def test_nested_import_error(self): - p = self.makepyfile( - test_one=""" + def test_nested_import_error(self, fstester): + p = fstester.makepyfile(""" import import_fails def test_this(): assert import_fails.a == 1 - """, - import_fails="import does_not_work" - ) - result = self.runpytest(p) - extra = assert_lines_contain_lines(result.outlines, [ + """) + fstester.makepyfile(import_fails="import does_not_work") + result = fstester.runpytest(p) + extra = result.stdout.fnmatch_lines([ "> import import_fails", "E ImportError: No module named does_not_work", ]) assert result.ret == 1 - def test_skipped_reasons(self): - p1 = self.makepyfile( + def test_skipped_reasons(self, fstester): + fstester.makepyfile( test_one=""" from conftest import doskip def setup_function(func): @@ -125,8 +74,8 @@ py.test.skip('test') """ ) - result = self.runpytest() - extra = assert_lines_contain_lines(result.outlines, [ + result = fstester.runpytest() + extra = result.stdout.fnmatch_lines([ "*test_one.py ss", "*test_two.py S", "___* skipped test summary *_", @@ -134,25 +83,25 @@ ]) assert result.ret == 0 - def test_deselected(self): - p1 = self.makepyfile(test_one=""" + def test_deselected(self, fstester): + testpath = fstester.makepyfile(""" def test_one(): pass def test_two(): pass def test_three(): pass - """, + """ ) - result = self.runpytest("-k", "test_two:") - extra = assert_lines_contain_lines(result.outlines, [ - "*test_one.py ..", + result = fstester.runpytest("-k", "test_two:", testpath) + extra = result.stdout.fnmatch_lines([ + "*test_deselected.py ..", "=* 1 test*deselected by 'test_two:'*=", ]) assert result.ret == 0 - def test_no_skip_summary_if_failure(self): - p1 = self.makepyfile(test_one=""" + def test_no_skip_summary_if_failure(self, fstester): + fstester.makepyfile(""" import py def test_ok(): pass @@ -161,12 +110,12 @@ def test_skip(): py.test.skip("dontshow") """) - result = self.runpytest() - assert str(result.outlines).find("skip test summary") == -1 + result = fstester.runpytest() + assert result.stdout.str().find("skip test summary") == -1 assert result.ret == 1 - def test_passes(self): - p1 = self.makepyfile(test_one=""" + def test_passes(self, fstester): + p1 = fstester.makepyfile(""" def test_passes(): pass class TestClass: @@ -175,32 +124,32 @@ """) old = p1.dirpath().chdir() try: - result = self.runpytest() + result = fstester.runpytest() finally: old.chdir() - extra = assert_lines_contain_lines(result.outlines, [ - "test_one.py ..", + extra = result.stdout.fnmatch_lines([ + "test_passes.py ..", "* 2 pass*", ]) assert result.ret == 0 - def test_header_trailer_info(self): - p1 = self.makepyfile(test_one=""" + def test_header_trailer_info(self, fstester): + p1 = fstester.makepyfile(""" def test_passes(): pass """) - result = self.runpytest() + result = fstester.runpytest() verinfo = ".".join(map(str, py.std.sys.version_info[:3])) - extra = assert_lines_contain_lines(result.outlines, [ + extra = result.stdout.fnmatch_lines([ "*===== test session starts ====*", "*localhost* %s %s - Python %s*" %( py.std.sys.platform, py.std.sys.executable, verinfo), - "*test_one.py .", + "*test_header_trailer_info.py .", "=* 1 passed in *.[0-9][0-9] seconds *=", ]) - def test_traceback_failure(self): - p1 = self.makepyfile(test_fail=""" + def test_traceback_failure(self, fstester): + p1 = fstester.makepyfile(""" def g(): return 2 def f(x): @@ -208,16 +157,16 @@ def test_onefails(): f(3) """) - result = self.runpytest(p1) - assert_lines_contain_lines(result.outlines, [ - "*test_fail.py F", + result = fstester.runpytest(p1) + result.stdout.fnmatch_lines([ + "*test_traceback_failure.py F", "====* FAILURES *====", "____*____", "", " def test_onefails():", "> f(3)", "", - "*test_fail.py:6: ", + "*test_*.py:6: ", "_ _ _ *", #"", " def f(x):", @@ -225,11 +174,11 @@ "E assert 3 == 2", "E + where 2 = g()", "", - "*test_fail.py:4: AssertionError" + "*test_traceback_failure.py:4: AssertionError" ]) - def test_capturing_outerr(self): - p1 = self.makepyfile(test_one=""" + def test_capturing_outerr(self, fstester): + p1 = fstester.makepyfile(""" import sys def test_capturing(): print 42 @@ -239,52 +188,51 @@ print >>sys.stderr, 2 raise ValueError """) - result = self.runpytest(p1) - assert_lines_contain_lines(result.outlines, [ - "*test_one.py .F", + result = fstester.runpytest(p1) + result.stdout.fnmatch_lines([ + "*test_capturing_outerr.py .F", "====* FAILURES *====", "____*____", - "*test_one.py:8: ValueError", + "*test_capturing_outerr.py:8: ValueError", "*--- Captured stdout ---*", "1", "*--- Captured stderr ---*", "2", ]) - def test_showlocals(self): - p1 = self.makepyfile(test_one=""" + def test_showlocals(self, fstester): + p1 = fstester.makepyfile(""" def test_showlocals(): x = 3 y = "x" * 5000 assert 0 """) - result = self.runpytest(p1, '-l') - assert_lines_contain_lines(result.outlines, [ + result = fstester.runpytest(p1, '-l') + result.stdout.fnmatch_lines([ #"_ _ * Locals *", "x* = 3", "y* = 'xxxxxx*" ]) - def test_doctest_simple_failing(self): - p = self.maketxtfile(doc=""" + def test_doctest_simple_failing(self, fstester): + p = fstester.maketxtfile(""" >>> i = 0 >>> i + 1 2 """) - result = self.runpytest(p) - assert_lines_contain_lines(result.outlines, [ + result = fstester.runpytest(p) + result.stdout.fnmatch_lines([ '001 >>> i = 0', '002 >>> i + 1', 'Expected:', " 2", "Got:", " 1", - "*doc.txt:2: DocTestFailure" + "*test_doctest_simple_failing.txt:2: DocTestFailure" ]) - def test_dist_testing(self): - p1 = self.makepyfile( - test_one=""" + def test_dist_testing(self, fstester): + p1 = fstester.makepyfile(""" import py def test_fail0(): assert 0 @@ -299,8 +247,8 @@ dist_hosts = ['localhost'] * 3 """ ) - result = self.runpytest(p1, '-d') - assert_lines_contain_lines(result.outlines, [ + result = fstester.runpytest(p1, '-d') + result.stdout.fnmatch_lines([ "HOSTUP: localhost*Python*", #"HOSTUP: localhost*Python*", #"HOSTUP: localhost*Python*", @@ -308,12 +256,11 @@ ]) assert result.ret == 1 - def test_dist_tests_with_crash(self): + def test_dist_tests_with_crash(self, fstester): if not hasattr(py.std.os, 'kill'): py.test.skip("no os.kill") - p1 = self.makepyfile( - test_one=""" + p1 = fstester.makepyfile(""" import py def test_fail0(): assert 0 @@ -333,8 +280,8 @@ dist_hosts = ['localhost'] * 3 """ ) - result = self.runpytest(p1, '-d') - assert_lines_contain_lines(result.outlines, [ + result = fstester.runpytest(p1, '-d') + result.stdout.fnmatch_lines([ "*localhost*Python*", "*localhost*Python*", "*localhost*Python*", @@ -343,23 +290,23 @@ ]) assert result.ret == 1 - def test_keyboard_interrupt(self): - p1 = self.makepyfile(test_one=""" + def test_keyboard_interrupt(self, fstester): + p1 = fstester.makepyfile(""" import py def test_fail(): raise ValueError() def test_inter(): raise KeyboardInterrupt() """) - result = self.runpytest(p1) - assert_lines_contain_lines(result.outlines, [ + result = fstester.runpytest(p1) + result.stdout.fnmatch_lines([ #"*test_inter() INTERRUPTED", "*KEYBOARD INTERRUPT*", "*1 failed*", ]) - def test_verbose_reporting(self): - p1 = self.makepyfile(test_one=""" + def test_verbose_reporting(self, fstester): + p1 = fstester.makepyfile(""" import py def test_fail(): raise ValueError() @@ -373,20 +320,20 @@ assert x == 1 yield check, 0 """) - result = self.runpytest(p1, '-v') - assert_lines_contain_lines(result.outlines, [ - "*test_one.py:2: test_fail*FAIL", - "*test_one.py:4: test_pass*PASS", - "*test_one.py:7: TestClass.test_skip*SKIP", - "*test_one.py:10: test_gen*FAIL", + result = fstester.runpytest(p1, '-v') + result.stdout.fnmatch_lines([ + "*test_verbose_reporting.py:2: test_fail*FAIL", + "*test_verbose_reporting.py:4: test_pass*PASS", + "*test_verbose_reporting.py:7: TestClass.test_skip*SKIP", + "*test_verbose_reporting.py:10: test_gen*FAIL", ]) assert result.ret == 1 -class TestInteractive(AcceptBase): - def getspawn(self): +class TestInteractive: + def getspawn(self, tmpdir): pexpect = py.test.importorskip("pexpect") def spawn(cmd): - return pexpect.spawn(cmd, logfile=self.tmpdir.join("spawn.out").open("w")) + return pexpect.spawn(cmd, logfile=tmpdir.join("spawn.out").open("w")) return spawn def requirespexpect(self, version_needed): @@ -395,17 +342,16 @@ if ver < version_needed: py.test.skip("pexpect version %s needed" %(".".join(map(str, version_needed)))) - def test_pdb_interaction(self): + def test_pdb_interaction(self, fstester): self.requirespexpect((2,3)) - spawn = self.getspawn() - self.makepyfile(test_one=""" + spawn = self.getspawn(fstester.tmpdir) + p1 = fstester.makepyfile(""" def test_1(): i = 0 assert i == 1 """) - child = spawn("%s %s --pdb test_one.py" % (py.std.sys.executable, - pytestpath)) + child = spawn("%s %s --pdb %s" % (py.std.sys.executable, pytestpath, p1)) child.timeout = EXPECTTIMEOUT #child.expect(".*def test_1.*") child.expect(".*i = 0.*") @@ -415,25 +361,24 @@ if child.isalive(): child.wait() - def test_simple_looponfailing_interaction(self): - spawn = self.getspawn() - test_one = self.makepyfile(test_one=""" + def test_simple_looponfailing_interaction(self, fstester): + spawn = self.getspawn(fstester.tmpdir) + p1 = fstester.makepyfile(""" def test_1(): assert 1 == 0 """) - test_one.setmtime(test_one.mtime() - 5.0) - child = spawn("%s %s --looponfailing test_one.py" % (py.std.sys.executable, - str(pytestpath))) + p1.setmtime(p1.mtime() - 50.0) + child = spawn("%s %s --looponfailing %s" % (py.std.sys.executable, pytestpath, p1)) child.timeout = EXPECTTIMEOUT child.expect("assert 1 == 0") - child.expect("test_one.py:") + child.expect("test_simple_looponfailing_interaction.py:") child.expect("1 failed") child.expect("waiting for changes") - test_one.write(py.code.Source(""" + p1.write(py.code.Source(""" def test_1(): assert 1 == 1 """)) - child.expect("MODIFIED.*test_one.py", timeout=4.0) + child.expect("MODIFIED.*test_simple_looponfailing_interaction.py", timeout=4.0) child.expect("1 passed", timeout=5.0) child.kill(15) Modified: py/branch/pytestplugin/py/test/testing/suptest.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/suptest.py (original) +++ py/branch/pytestplugin/py/test/testing/suptest.py Wed Feb 11 22:36:58 2009 @@ -122,43 +122,6 @@ assert len(failevents) == 1 return failevents[0],tfile -def assert_lines_contain_lines(lines1, lines2): - """ assert that lines2 are contained (linearly) in lines1. - return a list of extralines found. - """ - __tracebackhide__ = True - if isinstance(lines2, str): - lines2 = py.code.Source(lines2) - if isinstance(lines2, py.code.Source): - lines2 = lines2.strip().lines - - extralines = [] - lines1 = lines1[:] - nextline = None - for line in lines2: - nomatchprinted = False - while lines1: - nextline = lines1.pop(0) - if line == nextline: - print "exact match:", repr(line) - break - elif fnmatch(nextline, line): - print "fnmatch:", repr(line) - print " with:", repr(nextline) - break - else: - if not nomatchprinted: - print "nomatch:", repr(line) - nomatchprinted = True - print " and:", repr(nextline) - extralines.append(nextline) - else: - if line != nextline: - #__tracebackhide__ = True - raise AssertionError("expected line not found: %r" % line) - extralines.extend(lines1) - return extralines - # XXX below some code to help with inlining examples # as source code. # @@ -220,13 +183,3 @@ config.pluginmanager.unconfigure(config) return sorter -def popvalue(stringio): - value = stringio.getvalue().rstrip() - stringio.truncate(0) - return value - -def assert_stringio_contains_lines(stringio, tomatchlines): - stringio.seek(0) - l = stringio.readlines() - l = map(str.rstrip, l) - assert_lines_contain_lines(l, tomatchlines) Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Wed Feb 11 22:36:58 2009 @@ -66,7 +66,7 @@ assert modcol.name == x.name def test_listnames_getitembynames_custom(self, fstester): - hello = fstester._makefile(".xxx", hello="world") + hello = fstester._makefile(".xxx", (), {'hello':"world"}) fstester.makepyfile(conftest=""" import py class CustomFile(py.test.collect.File): Modified: py/branch/pytestplugin/py/test/testing/test_pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pmanage.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pmanage.py Wed Feb 11 22:36:58 2009 @@ -215,7 +215,7 @@ """ ) result = fstester.runpytest(fstester.tmpdir, '-h') - result.assert_contain_lines([ + result.stdout.fnmatch_lines([ "*--myplugin-option*", ]) From hpk at codespeak.net Wed Feb 11 22:58:26 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 11 Feb 2009 22:58:26 +0100 (CET) Subject: [py-svn] r61757 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090211215826.8C2D9169E47@codespeak.net> Author: hpk Date: Wed Feb 11 22:58:23 2009 New Revision: 61757 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Log: simplify internal test args and assertions Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 11 22:58:23 2009 @@ -13,22 +13,7 @@ self.stdout = LineMatcher(outlines) self.stderr = LineMatcher(errlines) -class FileCreation(object): - def makepyfile(self, **kwargs): - return self._makefile('.py', **kwargs) - def maketxtfile(self, **kwargs): - return self._makefile('.txt', **kwargs) - def _makefile(self, ext, **kwargs): - ret = None - for name, value in kwargs.iteritems(): - p = self.tmpdir.join(name).new(ext=ext) - source = py.code.Source(value) - p.write(str(py.code.Source(value)).lstrip()) - if ret is None: - ret = p - return ret - -class FSTester(FileCreation): +class FSTester: def __init__(self, pyfuncitem): self.pyfuncitem = pyfuncitem self.tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) @@ -119,26 +104,6 @@ def runpytest(self, *args): return self.runpybin("py.test", *args) -class LineComp: - - def __init__(self): - self.stringio = py.std.StringIO.StringIO() - - def assert_contains_lines(self, lines1=None, lines2=None): - """ assert that lines2 are contained (linearly) in lines1. - return a list of extralines found. - """ - from fnmatch import fnmatch - __tracebackhide__ = True - if lines2 is None: - lines2 = lines1 - lines1 = self.stringio - if hasattr(lines1, "getvalue"): - val = lines1.getvalue() - lines1.truncate(0) # remove what we got - lines1 = val.split("\n") - return LineMatcher(lines1).fnmatch_lines(lines2) - class TSession: def __init__(self, pyfuncitem): self.pyfuncitem = pyfuncitem @@ -173,6 +138,8 @@ def pytest_itemexecute_arg(self, pyfuncitem, argname): if argname == "linecomp": return LineComp(), None + elif argname == "LineMatcher": + return LineMatcher, None elif argname == "tsession": tsession = TSession(pyfuncitem) return tsession, None @@ -238,6 +205,20 @@ raise ValueError("found more than one testreport matching %r: %s" %( inamepart, l)) return l[0] + +class LineComp: + def __init__(self): + self.stringio = py.std.StringIO.StringIO() + + def assert_contains_lines(self, lines2): + """ assert that lines2 are contained (linearly) in lines1. + return a list of extralines found. + """ + __tracebackhide__ = True + val = self.stringio.getvalue() + self.stringio.truncate(0) # remove what we got + lines1 = val.split("\n") + return LineMatcher(lines1).fnmatch_lines(lines2) class LineMatcher: def __init__(self, lines): Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Wed Feb 11 22:58:23 2009 @@ -240,10 +240,10 @@ assert entry_lines[-1][0] == ' ' assert 'ValueError' in entry -def test_generic(plugintester, linecomp): +def test_generic(plugintester, LineMatcher): plugintester.apicheck(Resultlog) fstester = plugintester.fstester() - fstester.makepyfile(test_one=""" + fstester.makepyfile(""" import py def test_pass(): pass @@ -253,9 +253,8 @@ py.test.skip("") """) fstester.runpytest("--resultlog=result.log") - s = fstester.tmpdir.join("result.log").readlines(cr=0) - - linecomp.assert_contains_lines(s, [ + lines = fstester.tmpdir.join("result.log").readlines(cr=0) + LineMatcher(lines).fnmatch_lines([ ". *:test_pass", "F *:test_fail", "s *:test_skip", Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 11 22:58:23 2009 @@ -574,16 +574,16 @@ def test_func(): pass """) - stringio = py.std.cStringIO.StringIO() - rep = CollectonlyReporter(modcol._config, out=stringio) + #stringio = py.std.cStringIO.StringIO() + rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) indent = rep.indent rep.processevent(event.CollectionStart(modcol)) - linecomp.assert_contains_lines(stringio, [ + linecomp.assert_contains_lines([ "" ]) item = modcol.join("test_func") rep.processevent(event.ItemStart(item)) - linecomp.assert_contains_lines(stringio, [ + linecomp.assert_contains_lines([ " ", ]) rep.processevent(event.CollectionReport(modcol, [], excinfo=None)) @@ -594,12 +594,11 @@ import py py.test.skip("nomod") """) - stringio = py.std.cStringIO.StringIO() - rep = CollectonlyReporter(modcol._config, out=stringio) + rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) tsession.session.bus.subscribe(rep.processevent) cols = list(tsession.genitems([modcol])) assert len(cols) == 0 - linecomp.assert_contains_lines(stringio, """ + linecomp.assert_contains_lines(""" !!! Skipped: 'nomod' !!! """) @@ -608,12 +607,11 @@ modcol = tsession.getmodulecol(configargs=['--collectonly'], source=""" raise ValueError(0) """) - stringio = py.std.cStringIO.StringIO() - rep = CollectonlyReporter(modcol._config, out=stringio) + rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) tsession.session.bus.subscribe(rep.processevent) cols = list(tsession.genitems([modcol])) assert len(cols) == 0 - linecomp.assert_contains_lines(stringio, """ + linecomp.assert_contains_lines(""" !!! ValueError: 0 !!! """) From getxsick at codespeak.net Thu Feb 12 01:25:47 2009 From: getxsick at codespeak.net (getxsick at codespeak.net) Date: Thu, 12 Feb 2009 01:25:47 +0100 (CET) Subject: [py-svn] r61762 - py/dist/py Message-ID: <20090212002547.6C966169E42@codespeak.net> Author: getxsick Date: Thu Feb 12 01:25:45 2009 New Revision: 61762 Modified: py/dist/py/ (props changed) Log: pylib external repo moved from dist to trunk py.test from pylib is used as a framework for unittests From hpk at codespeak.net Thu Feb 12 01:29:38 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 01:29:38 +0100 (CET) Subject: [py-svn] r61763 - py/dist/py Message-ID: <20090212002938.E9B02169E82@codespeak.net> Author: hpk Date: Thu Feb 12 01:29:37 2009 New Revision: 61763 Modified: py/dist/py/ (props changed) Log: undo 61762 From hpk at codespeak.net Thu Feb 12 01:41:00 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 01:41:00 +0100 (CET) Subject: [py-svn] r61764 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090212004100.7608316840D@codespeak.net> Author: hpk Date: Thu Feb 12 01:40:57 2009 New Revision: 61764 Modified: py/branch/pytestplugin/py/test/defaultconftest.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py py/branch/pytestplugin/py/test/pmanage.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/testing/test_pmanage.py py/branch/pytestplugin/py/test/testing/test_runner_functional.py Log: * re-introduce apigen as a plugin, currently xfailing a test * rename "pytest_itemexecute_arg" to "pytest_pyfunc_arg" * introduce another plugin hook to execute python test functions Modified: py/branch/pytestplugin/py/test/defaultconftest.py ============================================================================== --- py/branch/pytestplugin/py/test/defaultconftest.py (original) +++ py/branch/pytestplugin/py/test/defaultconftest.py Thu Feb 12 01:40:57 2009 @@ -106,10 +106,6 @@ Option('', '--rest', action='store_true', dest="restreport", default=False, help="restructured text output reporting."), - Option('', '--apigen', - action="store", dest="apigen", - help="generate api documentation while testing (requires " - "argument pointing to a script)."), Option('', '--session', action="store", dest="session", default=None, help="lookup given sessioname in conftest.py files and use it."), Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Thu Feb 12 01:40:57 2009 @@ -4,7 +4,7 @@ import py class Plugintester: - def pytest_itemexecute_arg(self, pyfuncitem, argname): + def pytest_pyfunc_arg(self, pyfuncitem, argname): if argname == "plugintester": pt = PluginTester(pyfuncitem) else: @@ -100,17 +100,19 @@ def pytest_event(self, event): """ called for each internal py.test event. """ - def pytest_itemexecute_arg(self, pyfuncitem, argname): + def pytest_pyfunc_arg(self, pyfuncitem, argname): """ provide (value, finalizer) for an open test function argument. the finalizer (if not None) will be called after the test function has been executed (i.e. pyfuncitem.execute() returns). """ + def pytest_pyfunc_call(self, pyfuncitem, args, kwargs): + """ return True if we consumed/did the call to the python function item. """ + def pytest_pymodule_makeitem(self, modcol, name, obj): """ return custom item/collector or None. """ - # from pytest_terminal plugin def pytest_report_teststatus(self, event): """ return shortletter and verbose word. """ Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Thu Feb 12 01:40:57 2009 @@ -135,7 +135,7 @@ return self.config.getfsnode(path) class Pytester: - def pytest_itemexecute_arg(self, pyfuncitem, argname): + def pytest_pyfunc_arg(self, pyfuncitem, argname): if argname == "linecomp": return LineComp(), None elif argname == "LineMatcher": Modified: py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py Thu Feb 12 01:40:57 2009 @@ -13,7 +13,7 @@ """ pytest plugin for providing temporary directories to test functions and methods. """ - def pytest_itemexecute_arg(self, pyfuncitem, argname): + def pytest_pyfunc_arg(self, pyfuncitem, argname): if argname == "tmpdir": basename = "_".join(pyfuncitem.listnames()) # move to config @@ -28,13 +28,13 @@ def test_generic(plugintester): plugintester.apicheck(Tmpdir) -def test_pyitemexecute_arg(): +def test_pypyfunc_arg(): class Pseudoitem: def listnames(self): return ['a', 'b'] t = Tmpdir() - p, finalizer = t.pytest_itemexecute_arg(Pseudoitem(), "tmpdir") + p, finalizer = t.pytest_pyfunc_arg(Pseudoitem(), "tmpdir") assert finalizer is None assert p.check() assert p.basename == "a_b" - assert t.pytest_itemexecute_arg(Pseudoitem(), "something") is None + assert t.pytest_pyfunc_arg(Pseudoitem(), "something") is None Modified: py/branch/pytestplugin/py/test/pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/pmanage.py (original) +++ py/branch/pytestplugin/py/test/pmanage.py Thu Feb 12 01:40:57 2009 @@ -92,6 +92,12 @@ if res is not None: return res + def getfirst(self, methname): + """ call all plugins with the given method name and args. """ + l = self.listattr(methname) + if l: + return l[0][1] + def forward_event(self, event): self.callplugins("pytest_event", event=event) Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Thu Feb 12 01:40:57 2009 @@ -330,6 +330,10 @@ """ execute the given test function. """ if not self._deprecated_testexecution(): kw = self.getkwargs() + pytest_pyfunc_call = self._config.pluginmanager.getfirst("pytest_pyfunc_call") + if pytest_pyfunc_call is not None: + if pytest_pyfunc_call(pyfuncitem=self, args=self._args, kwargs=kw): + return self.obj(*self._args, **kw) def teardown(self): @@ -351,7 +355,7 @@ def fillarg(self, argname, kwargs): res = self._config.pluginmanager.callfirst( - "pytest_itemexecute_arg", pyfuncitem=self, argname=argname) + "pytest_pyfunc_arg", pyfuncitem=self, argname=argname) if res is not None: value, argfinalizer = res kwargs[argname] = value Modified: py/branch/pytestplugin/py/test/testing/test_pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pmanage.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pmanage.py Thu Feb 12 01:40:57 2009 @@ -108,6 +108,14 @@ assert myplugin1 is myplugin2 class TestPluginInteraction: + def test_getfirst(self): + pm = PluginManager() + class My1: + x = 1 + assert pm.getfirst("x") is None + pm.addpluginclass(My1) + assert pm.getfirst("x") == 1 + def test_callplugins(self): pm = PluginManager() class My: Modified: py/branch/pytestplugin/py/test/testing/test_runner_functional.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_runner_functional.py (original) +++ py/branch/pytestplugin/py/test/testing/test_runner_functional.py Thu Feb 12 01:40:57 2009 @@ -11,7 +11,7 @@ provided = [] class MyPlugin: - def pytest_itemexecute_arg(self, pyfuncitem, argname): + def pytest_pyfunc_arg(self, pyfuncitem, argname): if argname == "myxyz": res = 42 provided.append(res) @@ -34,7 +34,7 @@ provided = [] class MyPlugin: - def pytest_itemexecute_arg(self, pyfuncitem, argname): + def pytest_pyfunc_arg(self, pyfuncitem, argname): assert argname != 'self' if argname == "hello": return "world", None From hpk at codespeak.net Thu Feb 12 01:57:28 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 01:57:28 +0100 (CET) Subject: [py-svn] r61765 - in py/branch/pytestplugin/py: apigen apigen/testing bin Message-ID: <20090212005728.83B5F168465@codespeak.net> Author: hpk Date: Thu Feb 12 01:57:26 2009 New Revision: 61765 Modified: py/branch/pytestplugin/py/apigen/apigen.py py/branch/pytestplugin/py/apigen/htmlgen.py py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py py/branch/pytestplugin/py/bin/gendoc.py Log: a few tweaks to get some tests somewhat going Modified: py/branch/pytestplugin/py/apigen/apigen.py ============================================================================== --- py/branch/pytestplugin/py/apigen/apigen.py (original) +++ py/branch/pytestplugin/py/apigen/apigen.py Thu Feb 12 01:57:26 2009 @@ -26,9 +26,9 @@ def get_documentable_items(pkgdir): pkgname, pkgdict = get_documentable_items_pkgdir(pkgdir) - from py.__.execnet.channel import Channel - pkgdict['execnet.Channel'] = Channel - Channel.__apigen_hide_from_nav__ = True + #from py.__.execnet.channel import Channel + #pkgdict['execnet.Channel'] = Channel + #Channel.__apigen_hide_from_nav__ = True return pkgname, pkgdict def sourcedirfilter(p): Modified: py/branch/pytestplugin/py/apigen/htmlgen.py ============================================================================== --- py/branch/pytestplugin/py/apigen/htmlgen.py (original) +++ py/branch/pytestplugin/py/apigen/htmlgen.py Thu Feb 12 01:57:26 2009 @@ -430,8 +430,9 @@ relpath = get_rel_sourcepath(self.projroot, sourcefile, sourcefile) text = 'source: %s' % (relpath,) if is_in_pkg: - href = self.linker.get_lazyhref(sourcefile, - self.get_anchor(func)) + #href = self.linker.get_lazyhref(sourcefile, + # self.get_anchor(func)) + href = self.linker.get_lazyhref(sourcefile) # csource = H.SourceSnippet(text, href, colored) cslinks = self.build_callsites(dotted_name) snippet = H.FunctionDescription(localname, argdesc, docstring, @@ -464,8 +465,8 @@ if sourcefile[-1] in ['o', 'c']: sourcefile = sourcefile[:-1] sourcelink = H.div(H.a('view source', - href=self.linker.get_lazyhref(sourcefile, - self.get_anchor(cls)))) + href=self.linker.get_lazyhref(sourcefile) #, self.get_anchor(cls) + )) snippet = H.ClassDescription( # XXX bases HTML Modified: py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py (original) +++ py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py Thu Feb 12 01:57:26 2009 @@ -11,8 +11,6 @@ from py.__.apigen.conftest import option from py.__.path.svn.testing.svntestbase import make_test_repo -py.test.skip("apigen needs work on py.test to work again") - def run_string_sequence_test(data, seq): currpos = -1 for s in seq: @@ -238,7 +236,8 @@ self.linker.replace_dirpath(self.base, False) funchtml = self.base.join('api/main.SomeClass.html').read() print funchtml - assert funchtml.find('href="../source/pkg/someclass.py.html#SomeClass"') > -1 + #assert funchtml.find('href="../source/pkg/someclass.py.html#SomeClass"') > -1 + assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) def test_build_namespace_pages(self): Modified: py/branch/pytestplugin/py/bin/gendoc.py ============================================================================== --- py/branch/pytestplugin/py/bin/gendoc.py (original) +++ py/branch/pytestplugin/py/bin/gendoc.py Thu Feb 12 01:57:26 2009 @@ -45,7 +45,7 @@ if apigendir.check(): print apigendir, "exists, not re-generating - remove to trigger regeneration" else: - sysexec('%(env)s %(pytest)s --apigen=%(pypath)s/apigen/apigen.py py' % locals()) + sysexec('%(env)s %(pytest)s py' % locals()) print print "*" * 30, "static generation", "*" * 30 sysexec('%(env)s %(pytest)s --forcegen %(pypath)s/doc' % locals()) From hpk at codespeak.net Thu Feb 12 08:48:08 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 08:48:08 +0100 (CET) Subject: [py-svn] r61766 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090212074808.DAD54169E47@codespeak.net> Author: hpk Date: Thu Feb 12 08:48:06 2009 New Revision: 61766 Added: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py (contents, props changed) Log: add an APIGEN pytest plugin Added: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Thu Feb 12 08:48:06 2009 @@ -0,0 +1,77 @@ + +import py + +pytest_plugins = 'xfail' + +class Apigen: + pytest_cmdlineoptions = [ + py.test.config.Option( + '--apigen', + action="store_true", dest="apigen", + help="generate api documentation") + # "--apigen-script" argument pointing to a script"), + ] + + def pytest_configure(self, config): + if config.option.apigen: + from py.__.apigen.tracer.tracer import Tracer, DocStorage + self.pkgdir = py.path.local(config.args[0]).pypkgpath() + apigenscriptpath = py.path.local(py.__file__).dirpath("apigen", "apigen.py") + apigenscript = apigenscriptpath.pyimport() + if not hasattr(apigenscript, 'get_documentable_items'): + raise NotImplementedError("%r needs to provide get_documentable_items" %( + apigenscriptpath,)) + self.apigenscript = apigenscript + pkgname, items = apigenscript.get_documentable_items(self.pkgdir) + self.docstorage = DocStorage().from_dict(items, + module_name=pkgname) + self.tracer = Tracer(self.docstorage) + + def pytest_pyfunc_call(self, pyfuncitem, args, kwargs): + if hasattr(self, 'tracer'): + self.tracer.start_tracing() + try: + pyfuncitem.obj(*args, **kwargs) + finally: + self.tracer.end_tracing() + return True + + def pytest_terminal_summary(self, terminalreporter): + if hasattr(self, 'tracer'): + tr = terminalreporter + from py.__.apigen.tracer.docstorage import DocStorageAccessor + terminalreporter.write_sep("=", "apigen: building documentation") + capture = py.io.StdCaptureFD() + try: + self.apigenscript.build( + self.pkgdir, + DocStorageAccessor(self.docstorage), + capture) + finally: + capture.reset() + terminalreporter.write_line("apigen build completed") + +def test_generic(plugintester): + plugintester.apicheck(Apigen) + +def test_simple(fstester): + sub = fstester.tmpdir.mkdir("test_simple") + sub.join("__init__.py").write(py.code.Source(""" + from py import initpkg + initpkg(__name__, exportdefs={ + 'simple.f': ('./test_simple.py', 'f',), + }) + """)) + pyfile = sub.join("test_simple.py") + pyfile.write(py.code.Source(""" + def f(arg): + pass + def test_f(): + f(42) + """)) + fstester.makepyfile(conftest="pytest_plugins='apigen'") + result = fstester.runpytest(pyfile, "--apigen") + result.stdout.fnmatch_lines([ + "*apigen: building documentation*", + "apigen build completed", + ]) From hpk at codespeak.net Thu Feb 12 10:09:14 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 10:09:14 +0100 (CET) Subject: [py-svn] r61768 - in py/branch/pytestplugin/py: . cmdline/testing misc/testing test/plugin Message-ID: <20090212090914.C72A0169E15@codespeak.net> Author: hpk Date: Thu Feb 12 10:09:12 2009 New Revision: 61768 Modified: py/branch/pytestplugin/py/cmdline/testing/test_cmdline.py py/branch/pytestplugin/py/initpkg.py py/branch/pytestplugin/py/misc/testing/test_initpkg.py py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Log: remove unused code, fix some tests Modified: py/branch/pytestplugin/py/cmdline/testing/test_cmdline.py ============================================================================== --- py/branch/pytestplugin/py/cmdline/testing/test_cmdline.py (original) +++ py/branch/pytestplugin/py/cmdline/testing/test_cmdline.py Thu Feb 12 10:09:12 2009 @@ -1,18 +1,18 @@ -from py.__.test.testing import suptest -from py.__.test.testing.acceptance_test import AcceptBase -class TestPyLookup(AcceptBase): - def test_basic(self): - p = self.makepyfile(hello="def x(): pass") - result = self.runpybin("py.lookup", "pass") - suptest.assert_lines_contain_lines(result.outlines, +pytest_plugins = "pytest_pytester" + +class TestPyLookup: + def test_basic(self, fstester): + p = fstester.makepyfile(hello="def x(): pass") + result = fstester.runpybin("py.lookup", "pass") + result.stdout.fnmatch_lines( ['%s:*def x(): pass' %(p.basename)] ) - def test_search_in_filename(self): - p = self.makepyfile(hello="def x(): pass") - result = self.runpybin("py.lookup", "hello") - suptest.assert_lines_contain_lines(result.outlines, + def test_search_in_filename(self, fstester): + p = fstester.makepyfile(hello="def x(): pass") + result = fstester.runpybin("py.lookup", "hello") + result.stdout.fnmatch_lines( ['*%s:*' %(p.basename)] ) Modified: py/branch/pytestplugin/py/initpkg.py ============================================================================== --- py/branch/pytestplugin/py/initpkg.py (original) +++ py/branch/pytestplugin/py/initpkg.py Thu Feb 12 10:09:12 2009 @@ -64,12 +64,6 @@ def _resolve(self, extpyish): """ resolve a combined filesystem/python extpy-ish path. """ fspath, modpath = extpyish - if not fspath.endswith('.py'): - import py - e = py.path.local(self.implmodule.__file__) - e = e.dirpath(fspath, abs=True) - e = py.path.extpy(e, modpath) - return e.resolve() assert fspath.startswith('./'), \ "%r is not an implementation path (XXX)" % (extpyish,) implmodule = self._loadimpl(fspath[:-3]) Modified: py/branch/pytestplugin/py/misc/testing/test_initpkg.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_initpkg.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_initpkg.py Thu Feb 12 10:09:12 2009 @@ -68,7 +68,10 @@ base.join('execnet', 'script'), base.join('compat', 'testing'), ) - for p in base.visit('*.py', lambda x: x.check(dotfile=0)): + def recurse(p): + return p.check(dotfile=0) and p.basename != "attic" + + for p in base.visit('*.py', recurse): if p.basename == '__init__.py': continue relpath = p.new(ext='').relto(base) Modified: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_apigen.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Thu Feb 12 10:09:12 2009 @@ -1,7 +1,5 @@ - import py -pytest_plugins = 'xfail' class Apigen: pytest_cmdlineoptions = [ From hpk at codespeak.net Thu Feb 12 10:12:37 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 10:12:37 +0100 (CET) Subject: [py-svn] r61770 - py/branch/pytestplugin Message-ID: <20090212091237.B0E0A169E15@codespeak.net> Author: hpk Date: Thu Feb 12 10:12:37 2009 New Revision: 61770 Modified: py/branch/pytestplugin/TODO.txt Log: a random snapshot of my current TODO, need to sort what belongs to the plugin branch Modified: py/branch/pytestplugin/TODO.txt ============================================================================== --- py/branch/pytestplugin/TODO.txt (original) +++ py/branch/pytestplugin/TODO.txt Thu Feb 12 10:12:37 2009 @@ -19,19 +19,69 @@ probably only execute() is warned about or the other way round) - introduce plugin arch, port existing things to plugins: - - importorskip - - filelog - - chtmpdir per method + - OK filelog + - OK tmpdir / chtmpdir per method + - OK xfail + - OK unittest plugin + - OK in "pytest_plugins" specs allow for missing pytest prefix + - OK allow specifying pytest plugins from the environment + - OK/MOSTLY streamline assert_lines_contains_lines / result handling + - OK consider conftest files below cmdline args + - implement doctest plugin - apigen - - xfail + - plugin for more information about test environment + - plugin ordering? + - --traceconfig presenting info about conftests, plugins, ... + - consider porting py/doc/confrest to a plugin (mostly, also + consider pypy dependencies) + - ask guido to look at apigen & port javascript tests + - plugin for ReST reporting? + - put assert reinterpretation into plugin + - port py/test/web to py/test/plugin/pytest_webcheck.py + - cross-platform testing (testing from osx/linux to windows and vice versa?) + - deprecated_call and importorskip(), allow plugin-provided "py.test.*" hooks? - acceptance/assertlines - dist-testing? + - normalize/use linecomp/assert_lines_... + - statt "fstester, pytester" etc. ein "testdir" plugin: + def test_something(testdir): + path = testdir.file.py = makefile(...) # will use test method name a a filename + #testdir.makefile(...) would raise an Exception becaue a file was created already + result = testdir.subprocess_runpytest(*args) + result.stdout.contains(...) + result.stdout.assert_contains_lines(...) + result.stderr.assert_contains_lines(...) + +- Testdir + +- DONE remove need for usefilters by directly checking if the + "to-be-filtered" file is an command line arg + (seems liek the original purpose for usefilters is exactly that) + +- consider py lib plugins: + + py_path_svn + py_path_execnet + + and generalize pytest plugin calling, for example: + + py.plugins.callfirst(...) + py.plugins.calleach(...) + + and also consider a command line tool: + + py.plugin list|ls + etc. + + - introduce setuptools-style version checking, at least for py lib itself, maybe also for other packages: py.checkversion("py>=1.0") +- review/simplify py/doc/conftest.py + - generative tests: it is somewhat misleading for the classical htmlviews that generated tests are identified by numbers rather than by its parameters. unclear how @@ -125,6 +175,11 @@ refactorings ------------------ +- execnet refactoring/plugins? + * pyexecnet_PopenConnector + * pyexecnet_SshConnector + * pyexecnet_SocketConnector + - refine doctests usage (particularly skips of doctests if some imports/conditions are not satisfied) - check if it works on win32 From hpk at codespeak.net Thu Feb 12 12:36:23 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 12:36:23 +0100 (CET) Subject: [py-svn] r61774 - in py/branch/pytestplugin/py: . misc/testing Message-ID: <20090212113623.A1902169E40@codespeak.net> Author: hpk Date: Thu Feb 12 12:36:21 2009 New Revision: 61774 Modified: py/branch/pytestplugin/py/__init__.py py/branch/pytestplugin/py/initpkg.py py/branch/pytestplugin/py/misc/testing/test_initpkg.py Log: no need to hide that py lib module are a bit special. Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Thu Feb 12 12:36:21 2009 @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- """ -The py lib is a development support library featuring these tools and APIs: +The py lib is an extensible library for testing, distributed processing and +interacting with filesystems. - `py.test`_: cross-project testing tool with many advanced features - `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes @@ -37,7 +38,7 @@ author_email = "holger at merlinux.eu, py-dev at codespeak.net", long_description = globals()['__doc__'], classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", Modified: py/branch/pytestplugin/py/initpkg.py ============================================================================== --- py/branch/pytestplugin/py/initpkg.py (original) +++ py/branch/pytestplugin/py/initpkg.py Thu Feb 12 12:36:21 2009 @@ -160,15 +160,18 @@ sys.modules[modpath] = module # --------------------------------------------------- -# Virtual Module Object +# API Module Object # --------------------------------------------------- -class Module(ModuleType): +class ApiModule(ModuleType): def __init__(self, pkg, name): self.__pkg__ = pkg self.__name__ = name self.__map__ = {} + def __repr__(self): + return '' % (self.__name__,) + def __getattr__(self, name): if '*' in self.__map__: extpy = self.__map__['*'][0], name @@ -203,9 +206,6 @@ except (AttributeError, TypeError): pass - def __repr__(self): - return '' % (self.__name__, ) - def getdict(self): # force all the content of the module to be loaded when __dict__ is read dictdescr = ModuleType.__dict__['__dict__'] @@ -248,7 +248,7 @@ previous = current current += '.' + name if current not in seen: - seen[current] = mod = Module(pkg, current) + seen[current] = mod = ApiModule(pkg, current) setattr(seen[previous], name, mod) setmodule(current, mod) Modified: py/branch/pytestplugin/py/misc/testing/test_initpkg.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_initpkg.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_initpkg.py Thu Feb 12 12:36:21 2009 @@ -12,20 +12,20 @@ assert getattr(obj, '__map__') == {} def test_dir(): - from py.__.initpkg import Module + from py.__.initpkg import ApiModule for name in dir(py): if name == 'magic': # greenlets don't work everywhere, we don't care here continue if not name.startswith('_'): yield checksubpackage, name -from py.initpkg import Module +from py.initpkg import ApiModule glob = [] -class MyModule(Module): +class MyModule(ApiModule): def __init__(self, *args): glob.append(self.__dict__) assert isinstance(glob[-1], (dict, type(None))) - Module.__init__(self, *args) + ApiModule.__init__(self, *args) def test_early__dict__access(): mymod = MyModule("whatever", "myname") From hpk at codespeak.net Thu Feb 12 15:59:42 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 15:59:42 +0100 (CET) Subject: [py-svn] r61787 - in py/branch/pytestplugin/py: . doc misc misc/testing Message-ID: <20090212145942.58B92169E31@codespeak.net> Author: hpk Date: Thu Feb 12 15:59:41 2009 New Revision: 61787 Added: py/branch/pytestplugin/py/doc/plugin.txt (contents, props changed) py/branch/pytestplugin/py/misc/bus.py (contents, props changed) py/branch/pytestplugin/py/misc/plugin.py (contents, props changed) py/branch/pytestplugin/py/misc/testing/test_bus.py (contents, props changed) py/branch/pytestplugin/py/misc/testing/test_plugin.py (contents, props changed) Modified: py/branch/pytestplugin/py/__init__.py Log: experimentation towards a more generalized plugin and event system, suitable not only for py.test but also for the py lib Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Thu Feb 12 15:59:41 2009 @@ -54,6 +54,11 @@ # EXPORTED API exportdefs = { + + # py lib events and plugins + 'bus' : ('./misc/bus.py', 'bus'), + 'plugin' : ('./misc/plugin.py', 'plugin'), + # py lib cmdline tools 'cmdline.pytest' : ('./cmdline/pytest.py', 'main',), 'cmdline.pyrest' : ('./cmdline/pyrest.py', 'main',), Added: py/branch/pytestplugin/py/doc/plugin.txt ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/doc/plugin.txt Thu Feb 12 15:59:41 2009 @@ -0,0 +1,68 @@ +py lib plugins +---------------- + +you can write plugins that extend the py lib API +or implement certain hooks. + +registering a plugin +++++++++++++++++++++++++++++++++++ + +:: + >>> class MyPlugin: + def pyevent_pluginregister(self, plugin): + print "registering", plugin + ... + >>> import py + >>> py.plugin.register(MyPlugin()) + registering + +registering a plugin at import time +++++++++++++++++++++++++++++++++++++++++++ + +:: + import py + class EventLogger: + def pyevent_PluginAdded(self, event): + print "adding plugin", event.pluginobj + py.plugin.register(EventLogger()) + +explanation: on import this will add and activate a plugin that +will subsequently print information on events +whose class name is "PluginAdded". + +logging all events caused by py lib +++++++++++++++++++++++++++++++++++++++++ + +you can log all py lib generated events by setting +an environment variable, for example on linux:: + + export PYEVENT_LOG="/tmp/mylog" + +automatically importing plugins/modules ++++++++++++++++++++++++++++++++++++++++++ + +you can set the environment variable ``PY_AUTOIMPORT=myplugin`` +to ensure that a module gets imported automatically at +py lib import time. + + +py lib events +---------------- + +sending events ++++++++++++++++++ + +example sending events:: + + class myobj: + pass + py.bus.notify(myeventname=myobj) + +subscribing to all events ++++++++++++++++++++++++++++++ + +example subscribing to all send objects:: + + l = [] + py.bus.subscribe(l.append) # anonymous subscription + py.bus.subscribe(myeventname=l.append) # named subscription Added: py/branch/pytestplugin/py/misc/bus.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/misc/bus.py Thu Feb 12 15:59:41 2009 @@ -0,0 +1,36 @@ + +class EventBus(object): + """ Event Bus for distributing named and unnamed events. """ + def __init__(self): + self._kwsubscribers = {} + self._subscribers = [] + + def subscribe(self, callback=None, **kwcallback): + """ subscribe callback to bus events. """ + if callback: + self._subscribers.append(callback) + assert not kwcallback + else: + assert len(kwcallback) == 1 + name, call = kwcallback.popitem() + self._kwsubscribers.setdefault(name, []).append(call) + + def unsubscribe(self, callback=None, **kwcallback): + """ unsubscribe given callback from bus events. """ + try: + if callback is not None: + self._subscribers.remove(callback) + else: + for name, call in kwcallback.items(): + self._kwsubscribers[name].remove(call) + except ValueError, e: + raise KeyError(*e.args) + + def notify(self, **kw): + for name in kw: + for callback in self._kwsubscribers.get(name, ()): + callback(kw[name]) + for callback in self._subscribers: + callback((name, kw[name])) + +bus = EventBus() Added: py/branch/pytestplugin/py/misc/plugin.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/misc/plugin.py Thu Feb 12 15:59:41 2009 @@ -0,0 +1,37 @@ +import py +from py.__.misc.bus import bus + +class PluginManager: + def __init__(self): + self.list = [] + bus.subscribe(self._forwardevent) + + def _forwardevent(self, (name, obj)): + self.calleach("pyevent_" + name, obj) + + def register(self, plugin): + assert not isinstance(plugin, str) + self.list.append(plugin) + py.bus.notify(pluginregistered=plugin) + + def iterattr(self, attrname): + for plugin in self.list: + try: + yield getattr(plugin, attrname) + except AttributeError: + continue + + def calleach(self, methname, *args, **kwargs): + ret = [] + for call in self.iterattr(methname): + ret.append(call(*args, **kwargs)) + return ret + + def callfirst(self, methname, *args, **kwargs): + ret = [] + for call in self.iterattr(methname): + ret.append(call(*args, **kwargs)) + break + return ret + +plugin = PluginManager() Added: py/branch/pytestplugin/py/misc/testing/test_bus.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/misc/testing/test_bus.py Thu Feb 12 15:59:41 2009 @@ -0,0 +1,72 @@ + +import py +from py.__.misc.bus import EventBus + +class TestEventBus: + def test_kw_register_notify_unregister(self): + bus = EventBus() + l = [] + bus.subscribe(hello=l.append) + bus.notify(some=1) + assert not l + bus.notify(hello=5) + assert len(l) == 1 + l.remove(5) + bus.unsubscribe(hello=l.append) + bus.notify(hello=7) + assert not l + + def test_kw_unregister_non_existing(self): + bus = EventBus() + py.test.raises(KeyError, "bus.unsubscribe(hello=42)") + bus.subscribe(hello=10) + bus.unsubscribe(hello=10) + py.test.raises(KeyError, "bus.unsubscribe(hello=42)") + + def test_kw_multiregister(self): + bus = EventBus() + l1 = [] + l2 = [] + bus.subscribe(hello=l1.append) + bus.subscribe(hello=l2.append) + bus.subscribe(world=l2.append) + bus.notify(some=1) + assert not l1 and not l2 + bus.notify(hello=5) + assert len(l1) == 1 + assert len(l2) == 1 + bus.notify(world=42) + assert len(l2) == 2 + bus.unsubscribe(hello=l2.append) + bus.unsubscribe(world=l2.append) + bus.unsubscribe(hello=l1.append) + + def test_simple_anonymous(self): + bus = EventBus() + l = [] + bus.subscribe(l.append) + bus.notify(hello=1) + assert l == [("hello", 1)] + bus.unsubscribe(l.append) + bus.notify(hello=1) + assert l == [("hello", 1)] + + def test_multi_anonymous(self): + bus = EventBus() + l1 = [] + l2 = [] + bus.subscribe(l1.append) + bus.subscribe(l2.append) + bus.notify(event=1) + bus.notify(event2=2) + bus.notify(event3=3) + assert l1 == [("event", 1), ("event2", 2), ("event3", 3)] + assert l2 == [("event", 1), ("event2", 2), ("event3", 3)] + bus.unsubscribe(l1.append) + bus.unsubscribe(l2.append) + py.test.raises(KeyError, "bus.unsubscribe(l1.append)") + py.test.raises(KeyError, "bus.unsubscribe(l2.append)") + + +def test_api(): + assert isinstance(py.bus, EventBus) Added: py/branch/pytestplugin/py/misc/testing/test_plugin.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/misc/testing/test_plugin.py Thu Feb 12 15:59:41 2009 @@ -0,0 +1,64 @@ + +import py +from py.__.misc.plugin import PluginManager + +class TestPluginManager: + def test_register(self): + pm = PluginManager() + class MyPlugin: + pass + pm.register(MyPlugin) + assert MyPlugin in pm.list + + def test_onregister(self): + pm = PluginManager() + l = [] + class MyApi: + def pyevent_pluginregistered(self, plugin): + l.append(plugin) + myapi = MyApi() + pm.register(myapi) + assert len(l) == 1 + assert l[0] is myapi + + def test_calleach(self): + pm = PluginManager() + class api1: + def m(self): + return 42 + class api2: + def m(self): + return 41 + pm.register(api1()) + pm.register(api2()) + l = pm.calleach('m') + l.sort() + assert l == [41, 42] + + def test_callfirst(self): + pm = PluginManager() + class api1: + def m(self): + return 42 + class api2: + def m(self): + return 41 + pm.register(api1()) + pm.register(api2()) + l = pm.callfirst('m') + assert l == [42] + + def test_iterattr(self): + pm = PluginManager() + class api1: + x = 42 + class api2: + x = 41 + pm.register(api1()) + pm.register(api2()) + l = list(pm.iterattr('x')) + l.sort() + assert l == [41, 42] + +def test_api(): + assert isinstance(py.plugin, PluginManager) From hpk at codespeak.net Thu Feb 12 16:23:45 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 16:23:45 +0100 (CET) Subject: [py-svn] r61789 - in py/branch/pytestplugin/py: . doc misc/testing Message-ID: <20090212152345.4D210169EB5@codespeak.net> Author: hpk Date: Thu Feb 12 16:23:44 2009 New Revision: 61789 Modified: py/branch/pytestplugin/py/doc/plugin.txt py/branch/pytestplugin/py/initpkg.py py/branch/pytestplugin/py/misc/testing/test_initpkg.py Log: here is the documented support for autoimporting modules on py lib startup - it's experimental, next is now to merge with the existing py.test plugins and see how that works. Modified: py/branch/pytestplugin/py/doc/plugin.txt ============================================================================== --- py/branch/pytestplugin/py/doc/plugin.txt (original) +++ py/branch/pytestplugin/py/doc/plugin.txt Thu Feb 12 16:23:44 2009 @@ -36,14 +36,16 @@ you can log all py lib generated events by setting an environment variable, for example on linux:: - export PYEVENT_LOG="/tmp/mylog" + export PYEVENTLOG="/tmp/mylog" automatically importing plugins/modules +++++++++++++++++++++++++++++++++++++++++ -you can set the environment variable ``PY_AUTOIMPORT=myplugin`` +XXX + +you can set the environment variable ``PY_AUTOIMPORT=mymodule`` to ensure that a module gets imported automatically at -py lib import time. +py lib import time. py lib events Modified: py/branch/pytestplugin/py/initpkg.py ============================================================================== --- py/branch/pytestplugin/py/initpkg.py (original) +++ py/branch/pytestplugin/py/initpkg.py Thu Feb 12 16:23:44 2009 @@ -266,3 +266,12 @@ for mod, pypart, extpy in deferred_imports: setattr(mod, pypart, pkg._resolve(extpy)) + autoimport(pkgname) + +def autoimport(pkgname): + import py + ENVKEY = pkgname.upper() + "_AUTOIMPORT" + if ENVKEY in os.environ: + for impname in os.environ[ENVKEY].split(","): + py.bus.notify(autoimport=impname) + __import__(impname) Modified: py/branch/pytestplugin/py/misc/testing/test_initpkg.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_initpkg.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_initpkg.py Thu Feb 12 16:23:44 2009 @@ -258,3 +258,8 @@ # help(std.path) # #assert False + +def test_autoimport(): + from py.initpkg import autoimport + py.std.os.environ['AUTOTEST_AUTOIMPORT'] = "nonexistmodule" + py.test.raises(ImportError, "autoimport('autotest')") From hpk at codespeak.net Thu Feb 12 21:20:40 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 12 Feb 2009 21:20:40 +0100 (CET) Subject: [py-svn] r61812 - py/branch/pytestplugin/py/doc Message-ID: <20090212202040.EDF71169E99@codespeak.net> Author: hpk Date: Thu Feb 12 21:20:38 2009 New Revision: 61812 Modified: py/branch/pytestplugin/py/doc/plugin.txt Log: an example that passes the doctest Modified: py/branch/pytestplugin/py/doc/plugin.txt ============================================================================== --- py/branch/pytestplugin/py/doc/plugin.txt (original) +++ py/branch/pytestplugin/py/doc/plugin.txt Thu Feb 12 21:20:38 2009 @@ -9,26 +9,12 @@ :: >>> class MyPlugin: - def pyevent_pluginregister(self, plugin): - print "registering", plugin - ... + ... def pyevent_pluginregistered(self, plugin): + ... print "registering", plugin.__class__.__name__ + ... >>> import py >>> py.plugin.register(MyPlugin()) - registering - -registering a plugin at import time -++++++++++++++++++++++++++++++++++++++++++ - -:: - import py - class EventLogger: - def pyevent_PluginAdded(self, event): - print "adding plugin", event.pluginobj - py.plugin.register(EventLogger()) - -explanation: on import this will add and activate a plugin that -will subsequently print information on events -whose class name is "PluginAdded". + registering MyPlugin logging all events caused by py lib ++++++++++++++++++++++++++++++++++++++++ From hpk at codespeak.net Fri Feb 13 15:04:21 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 13 Feb 2009 15:04:21 +0100 (CET) Subject: [py-svn] r61829 - in py/branch/pytestplugin: . py py/misc py/misc/testing Message-ID: <20090213140421.BCF9C169EDB@codespeak.net> Author: hpk Date: Fri Feb 13 15:04:20 2009 New Revision: 61829 Modified: py/branch/pytestplugin/TODO.txt py/branch/pytestplugin/py/__init__.py py/branch/pytestplugin/py/misc/plugin.py py/branch/pytestplugin/py/misc/testing/test_plugin.py Log: * allowing to configure plugins through PYLIB env variable * support scanning of python modules for "pylib" value Modified: py/branch/pytestplugin/TODO.txt ============================================================================== --- py/branch/pytestplugin/TODO.txt (original) +++ py/branch/pytestplugin/TODO.txt Fri Feb 13 15:04:20 2009 @@ -26,7 +26,14 @@ - OK in "pytest_plugins" specs allow for missing pytest prefix - OK allow specifying pytest plugins from the environment - OK/MOSTLY streamline assert_lines_contains_lines / result handling - - OK consider conftest files below cmdline args + - construct Argument object for sending/receiving of args to + test methods + - py.plugin: make subscriptions weakref'ed + - Pytest functiona arguments: provide the test function + source / head when reporting a "could not find test function argument" + - plugins: additionally initialize from $HOME/.py/pylib file? + extend or add another method to PluginManager.consider_env()? + - have plugins/hook cause log events / and have a debug plugin - implement doctest plugin - apigen - plugin for more information about test environment Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Fri Feb 13 15:04:20 2009 @@ -194,3 +194,7 @@ 'compat.subprocess' : ('./compat/subprocess.py', '*'), }) +import py +py.plugin.consider_env() + + Modified: py/branch/pytestplugin/py/misc/plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/plugin.py (original) +++ py/branch/pytestplugin/py/misc/plugin.py Fri Feb 13 15:04:20 2009 @@ -2,6 +2,8 @@ from py.__.misc.bus import bus class PluginManager: + _pyspec = "pylib" + def __init__(self): self.list = [] bus.subscribe(self._forwardevent) @@ -9,6 +11,28 @@ def _forwardevent(self, (name, obj)): self.calleach("pyevent_" + name, obj) + def import_module(self, modspec): + # XXX allow modspec to specify version / lookup + modpath = modspec + py.bus.notify(importingmodule=modpath) + __import__(modpath) + + def consider_env(self): + """ consider ENV variables for loading modules. """ + val = py.std.os.environ.get(self._pyspec.upper(), None) + self._consider(val) + + def consider_module(self, mod): + speclist = getattr(mod, self._pyspec, None) + self._consider(speclist) + + def _consider(self, speclist): + if speclist is not None: + if not isinstance(speclist, (list, tuple)): + speclist = (speclist,) + for spec in speclist: + self.import_module(spec) + def register(self, plugin): assert not isinstance(plugin, str) self.list.append(plugin) Modified: py/branch/pytestplugin/py/misc/testing/test_plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_plugin.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_plugin.py Fri Feb 13 15:04:20 2009 @@ -1,5 +1,6 @@ import py +import os from py.__.misc.plugin import PluginManager class TestPluginManager: @@ -60,5 +61,50 @@ l.sort() assert l == [41, 42] + def test_consider_env(self): + # XXX write a helper for preserving os.environ + pm = PluginManager() + KEY = "PYLIB" + old = os.environ.get(KEY, None) + try: + os.environ[KEY] = "unknownconsider_env" + py.test.raises(ImportError, "pm.consider_env()") + finally: + if old is None: + del os.environ[KEY] + else: + os.environ[KEY] = old + + def test_consider_module(self): + pm = PluginManager() + mod = py.std.new.module("temp") + mod.pylib = ["xxx nomod"] + excinfo = py.test.raises(ImportError, "pm.consider_module(mod)") + mod.pylib = "os" + l = [] + py.bus.subscribe(importingmodule=l.append) + pm.consider_module(mod) + assert len(l) == 1 + assert l[0] == mod.pylib + def test_api(): assert isinstance(py.plugin, PluginManager) + +def test_subprocess_env(): + # XXX write a helper for preserving os.environ + pm = PluginManager() + KEY = "PYLIB" + old = os.environ.get(KEY, None) + try: + os.environ[KEY] = "unknownconsider_env" + excinfo = py.test.raises(py.process.cmdexec.Error, """ + py.process.cmdexec("python -c 'import py'") + """) + assert str(excinfo.value).find("ImportError") != -1 + assert str(excinfo.value).find("unknownconsider") != -1 + finally: + if old is None: + del os.environ[KEY] + else: + os.environ[KEY] = old + From hpk at codespeak.net Fri Feb 13 15:12:20 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 13 Feb 2009 15:12:20 +0100 (CET) Subject: [py-svn] r61830 - in py/branch/pytestplugin/py: . misc misc/testing Message-ID: <20090213141220.9D506169E93@codespeak.net> Author: hpk Date: Fri Feb 13 15:12:19 2009 New Revision: 61830 Added: py/branch/pytestplugin/py/misc/event.py (contents, props changed) - copied, changed from r61811, py/branch/pytestplugin/py/misc/bus.py py/branch/pytestplugin/py/misc/testing/test_event.py (contents, props changed) - copied, changed from r61811, py/branch/pytestplugin/py/misc/testing/test_bus.py Removed: py/branch/pytestplugin/py/misc/bus.py py/branch/pytestplugin/py/misc/testing/test_bus.py Modified: py/branch/pytestplugin/py/__init__.py py/branch/pytestplugin/py/initpkg.py py/branch/pytestplugin/py/misc/plugin.py py/branch/pytestplugin/py/misc/testing/test_plugin.py Log: rename py.bus to py.event for now - sounds more intuitive for my ears. Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Fri Feb 13 15:12:19 2009 @@ -56,7 +56,7 @@ exportdefs = { # py lib events and plugins - 'bus' : ('./misc/bus.py', 'bus'), + 'event' : ('./misc/event.py', 'eventbus'), 'plugin' : ('./misc/plugin.py', 'plugin'), # py lib cmdline tools Modified: py/branch/pytestplugin/py/initpkg.py ============================================================================== --- py/branch/pytestplugin/py/initpkg.py (original) +++ py/branch/pytestplugin/py/initpkg.py Fri Feb 13 15:12:19 2009 @@ -273,5 +273,5 @@ ENVKEY = pkgname.upper() + "_AUTOIMPORT" if ENVKEY in os.environ: for impname in os.environ[ENVKEY].split(","): - py.bus.notify(autoimport=impname) + py.event.notify(autoimport=impname) __import__(impname) Deleted: /py/branch/pytestplugin/py/misc/bus.py ============================================================================== --- /py/branch/pytestplugin/py/misc/bus.py Fri Feb 13 15:12:19 2009 +++ (empty file) @@ -1,36 +0,0 @@ - -class EventBus(object): - """ Event Bus for distributing named and unnamed events. """ - def __init__(self): - self._kwsubscribers = {} - self._subscribers = [] - - def subscribe(self, callback=None, **kwcallback): - """ subscribe callback to bus events. """ - if callback: - self._subscribers.append(callback) - assert not kwcallback - else: - assert len(kwcallback) == 1 - name, call = kwcallback.popitem() - self._kwsubscribers.setdefault(name, []).append(call) - - def unsubscribe(self, callback=None, **kwcallback): - """ unsubscribe given callback from bus events. """ - try: - if callback is not None: - self._subscribers.remove(callback) - else: - for name, call in kwcallback.items(): - self._kwsubscribers[name].remove(call) - except ValueError, e: - raise KeyError(*e.args) - - def notify(self, **kw): - for name in kw: - for callback in self._kwsubscribers.get(name, ()): - callback(kw[name]) - for callback in self._subscribers: - callback((name, kw[name])) - -bus = EventBus() Copied: py/branch/pytestplugin/py/misc/event.py (from r61811, py/branch/pytestplugin/py/misc/bus.py) ============================================================================== --- py/branch/pytestplugin/py/misc/bus.py (original) +++ py/branch/pytestplugin/py/misc/event.py Fri Feb 13 15:12:19 2009 @@ -33,4 +33,4 @@ for callback in self._subscribers: callback((name, kw[name])) -bus = EventBus() +eventbus = EventBus() Modified: py/branch/pytestplugin/py/misc/plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/plugin.py (original) +++ py/branch/pytestplugin/py/misc/plugin.py Fri Feb 13 15:12:19 2009 @@ -1,12 +1,12 @@ import py -from py.__.misc.bus import bus +from py.__.misc.event import eventbus class PluginManager: _pyspec = "pylib" def __init__(self): self.list = [] - bus.subscribe(self._forwardevent) + eventbus.subscribe(self._forwardevent) def _forwardevent(self, (name, obj)): self.calleach("pyevent_" + name, obj) @@ -14,7 +14,7 @@ def import_module(self, modspec): # XXX allow modspec to specify version / lookup modpath = modspec - py.bus.notify(importingmodule=modpath) + py.event.notify(importingmodule=modpath) __import__(modpath) def consider_env(self): @@ -36,7 +36,7 @@ def register(self, plugin): assert not isinstance(plugin, str) self.list.append(plugin) - py.bus.notify(pluginregistered=plugin) + py.event.notify(pluginregistered=plugin) def iterattr(self, attrname): for plugin in self.list: Deleted: /py/branch/pytestplugin/py/misc/testing/test_bus.py ============================================================================== --- /py/branch/pytestplugin/py/misc/testing/test_bus.py Fri Feb 13 15:12:19 2009 +++ (empty file) @@ -1,72 +0,0 @@ - -import py -from py.__.misc.bus import EventBus - -class TestEventBus: - def test_kw_register_notify_unregister(self): - bus = EventBus() - l = [] - bus.subscribe(hello=l.append) - bus.notify(some=1) - assert not l - bus.notify(hello=5) - assert len(l) == 1 - l.remove(5) - bus.unsubscribe(hello=l.append) - bus.notify(hello=7) - assert not l - - def test_kw_unregister_non_existing(self): - bus = EventBus() - py.test.raises(KeyError, "bus.unsubscribe(hello=42)") - bus.subscribe(hello=10) - bus.unsubscribe(hello=10) - py.test.raises(KeyError, "bus.unsubscribe(hello=42)") - - def test_kw_multiregister(self): - bus = EventBus() - l1 = [] - l2 = [] - bus.subscribe(hello=l1.append) - bus.subscribe(hello=l2.append) - bus.subscribe(world=l2.append) - bus.notify(some=1) - assert not l1 and not l2 - bus.notify(hello=5) - assert len(l1) == 1 - assert len(l2) == 1 - bus.notify(world=42) - assert len(l2) == 2 - bus.unsubscribe(hello=l2.append) - bus.unsubscribe(world=l2.append) - bus.unsubscribe(hello=l1.append) - - def test_simple_anonymous(self): - bus = EventBus() - l = [] - bus.subscribe(l.append) - bus.notify(hello=1) - assert l == [("hello", 1)] - bus.unsubscribe(l.append) - bus.notify(hello=1) - assert l == [("hello", 1)] - - def test_multi_anonymous(self): - bus = EventBus() - l1 = [] - l2 = [] - bus.subscribe(l1.append) - bus.subscribe(l2.append) - bus.notify(event=1) - bus.notify(event2=2) - bus.notify(event3=3) - assert l1 == [("event", 1), ("event2", 2), ("event3", 3)] - assert l2 == [("event", 1), ("event2", 2), ("event3", 3)] - bus.unsubscribe(l1.append) - bus.unsubscribe(l2.append) - py.test.raises(KeyError, "bus.unsubscribe(l1.append)") - py.test.raises(KeyError, "bus.unsubscribe(l2.append)") - - -def test_api(): - assert isinstance(py.bus, EventBus) Copied: py/branch/pytestplugin/py/misc/testing/test_event.py (from r61811, py/branch/pytestplugin/py/misc/testing/test_bus.py) ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_bus.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_event.py Fri Feb 13 15:12:19 2009 @@ -1,6 +1,6 @@ import py -from py.__.misc.bus import EventBus +from py.__.misc.event import EventBus class TestEventBus: def test_kw_register_notify_unregister(self): @@ -69,4 +69,4 @@ def test_api(): - assert isinstance(py.bus, EventBus) + assert isinstance(py.event, EventBus) Modified: py/branch/pytestplugin/py/misc/testing/test_plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_plugin.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_plugin.py Fri Feb 13 15:12:19 2009 @@ -82,7 +82,7 @@ excinfo = py.test.raises(ImportError, "pm.consider_module(mod)") mod.pylib = "os" l = [] - py.bus.subscribe(importingmodule=l.append) + py.event.subscribe(importingmodule=l.append) pm.consider_module(mod) assert len(l) == 1 assert l[0] == mod.pylib From hpk at codespeak.net Fri Feb 13 15:30:03 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 13 Feb 2009 15:30:03 +0100 (CET) Subject: [py-svn] r61831 - in py/branch/pytestplugin/py/test: . testing Message-ID: <20090213143003.E78CD169DB3@codespeak.net> Author: hpk Date: Fri Feb 13 15:29:59 2009 New Revision: 61831 Added: py/branch/pytestplugin/py/test/handleplugin.py (contents, props changed) py/branch/pytestplugin/py/test/testing/test_handleplugin.py (contents, props changed) Log: draft of initial glue code to use the new event/plugin mechanisms for the existing py.test plugins Added: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/handleplugin.py Fri Feb 13 15:29:59 2009 @@ -0,0 +1,34 @@ +""" +handling py.test related plugins +""" +import py + +class PytestPluginManager(object): + def __init__(self, pluginmanager=py.plugin): + self.pm = pluginmanager + + # + # API for interacting with registered and instantiated plugin objects + # + def setinitial(self, modules): + for module in modules: + self.pm.consider_module(module) + + def add_cmdlineoptions(self, config): + # XXX think about sorting/grouping of options from user-perspective + opts = [] + for name, options in self.pm.iterattr("pytest_cmdlineoptions"): + opts.extend(options) + config.addoptions("ungrouped options added by plugins", *opts) + + def ensure_configured_plugins(self, config): + def configureplugin(plugin): + self.pm.callone(plugin, "pytest_configure", config=config) + self.configureplugin = configureplugin + self.pm.subscribe(pluginregistered=autoconfigure) + self.pm.callplugins("pytest_configure", config=config) + + def unconfigure_plugins(self, config): + self.pm.callplugins("pytest_unconfigure", config=config) + self.pm.unsubscribe(pluginregistered=self.configureplugin) + del self.configureplugin Added: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Fri Feb 13 15:29:59 2009 @@ -0,0 +1,18 @@ +import py +from py.__.test.handleplugin import PytestPluginManager + +def test_addcmdlineoptions(): + class PseudoPM: + def iterattr(self, name): + assert name == "pytest_cmdlineoptions" + return [("xxx", + [py.std.optparse.Option("--pseudopm", action="store", dest="x")])] + class PseudoConfig: + opts = [] + def addoptions(self, *opts): + self.opts.append(opts) + pseudopm = PseudoPM() + pseudoconfig = PseudoConfig() + pc = PytestPluginManager(pseudopm) + pc.add_cmdlineoptions(pseudoconfig) + assert len(pseudoconfig.opts) == 1 From py-svn at codespeak.net Fri Feb 13 19:16:04 2009 From: py-svn at codespeak.net (py-svn at codespeak.net) Date: Fri, 13 Feb 2009 19:16:04 +0100 (CET) Subject: [py-svn] Scarlett kept moaning so loud Message-ID: <20090213181604.83E10169E16@codespeak.net> An HTML attachment was scrubbed... URL: From getxsick at codespeak.net Sat Feb 14 03:07:22 2009 From: getxsick at codespeak.net (getxsick at codespeak.net) Date: Sat, 14 Feb 2009 03:07:22 +0100 (CET) Subject: [py-svn] r61864 - py/trunk/py Message-ID: <20090214020722.53B9C168408@codespeak.net> Author: getxsick Date: Sat Feb 14 03:07:20 2009 New Revision: 61864 Modified: py/trunk/py/conftest.py Log: remove unnecessary bracket from the help message Modified: py/trunk/py/conftest.py ============================================================================== --- py/trunk/py/conftest.py (original) +++ py/trunk/py/conftest.py Sat Feb 14 03:07:20 2009 @@ -35,7 +35,7 @@ help="relative path to doc output location (relative from py/)"), Option('', '--runslowtests', action="store_true", dest="runslowtests", default=False, - help="run slow tests)"), + help="run slow tests"), ) dist_rsync_roots = ['.'] From hpk at codespeak.net Mon Feb 16 11:06:20 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 16 Feb 2009 11:06:20 +0100 (CET) Subject: [py-svn] r61942 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090216100620.4DB211684DC@codespeak.net> Author: hpk Date: Mon Feb 16 11:06:15 2009 New Revision: 61942 Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Log: only test resultlog functionality here, not event dispatch Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Mon Feb 16 11:06:15 2009 @@ -218,20 +218,16 @@ def test_internal_exception(self): # they are produced for example by a teardown failing # at the end of the run - from py.__.test import event - bus = event.EventBus() - reslog = ResultLog(StringIO.StringIO()) - bus.subscribe(reslog.log_event_to_file) try: raise ValueError except ValueError: excinfo = py.code.ExceptionInfo() + from py.__.test import event internal = event.InternalException(excinfo) - - bus.notify(internal) - + reslog = ResultLog(StringIO.StringIO()) + reslog.log_event_to_file(internal) entry = reslog.logfile.getvalue() entry_lines = entry.splitlines() From hpk at codespeak.net Mon Feb 16 12:08:23 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 16 Feb 2009 12:08:23 +0100 (CET) Subject: [py-svn] r61943 - py/branch/pytestplugin/py/misc Message-ID: <20090216110823.3CEA916846C@codespeak.net> Author: hpk Date: Mon Feb 16 12:08:21 2009 New Revision: 61943 Modified: py/branch/pytestplugin/py/misc/event.py Log: unify dict for callbacks Modified: py/branch/pytestplugin/py/misc/event.py ============================================================================== --- py/branch/pytestplugin/py/misc/event.py (original) +++ py/branch/pytestplugin/py/misc/event.py Mon Feb 16 12:08:21 2009 @@ -2,35 +2,41 @@ class EventBus(object): """ Event Bus for distributing named and unnamed events. """ def __init__(self): - self._kwsubscribers = {} - self._subscribers = [] + self._subscribers = {} + + def _getsubscribers(self, key): + try: + return self._subscribers[key] + except KeyError: + x = self._subscribers[key] = [] + return x def subscribe(self, callback=None, **kwcallback): """ subscribe callback to bus events. """ if callback: - self._subscribers.append(callback) + self._getsubscribers('').append(callback) assert not kwcallback else: assert len(kwcallback) == 1 name, call = kwcallback.popitem() - self._kwsubscribers.setdefault(name, []).append(call) + self._getsubscribers(name).append(call) def unsubscribe(self, callback=None, **kwcallback): """ unsubscribe given callback from bus events. """ try: if callback is not None: - self._subscribers.remove(callback) + self._getsubscribers('').remove(callback) else: for name, call in kwcallback.items(): - self._kwsubscribers[name].remove(call) + self._getsubscribers(name).remove(call) except ValueError, e: raise KeyError(*e.args) def notify(self, **kw): for name in kw: - for callback in self._kwsubscribers.get(name, ()): + for callback in self._getsubscribers(name): callback(kw[name]) - for callback in self._subscribers: + for callback in self._getsubscribers(''): callback((name, kw[name])) eventbus = EventBus() From hpk at codespeak.net Mon Feb 16 13:00:44 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 16 Feb 2009 13:00:44 +0100 (CET) Subject: [py-svn] r61944 - in py/branch/pytestplugin/py/misc: . testing Message-ID: <20090216120044.E7802168498@codespeak.net> Author: hpk Date: Mon Feb 16 13:00:44 2009 New Revision: 61944 Modified: py/branch/pytestplugin/py/misc/testing/test_warn.py py/branch/pytestplugin/py/misc/warn.py Log: reuse py.event bus, mark file for further refactoring Modified: py/branch/pytestplugin/py/misc/testing/test_warn.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_warn.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_warn.py Mon Feb 16 13:00:44 2009 @@ -47,7 +47,6 @@ warning = self.warnings[0] assert warning.msg == "xxx (since version 3.0)" -def test_APIWARN(): +def test_default(): from py.__.misc.warn import APIWARN - wb = APIWARN.im_self - assert wb._forward in wb._eventbus._subscribers + assert APIWARN.im_self._eventbus is py.event Modified: py/branch/pytestplugin/py/misc/warn.py ============================================================================== --- py/branch/pytestplugin/py/misc/warn.py (original) +++ py/branch/pytestplugin/py/misc/warn.py Mon Feb 16 13:00:44 2009 @@ -11,18 +11,22 @@ def __str__(self): return self.msg +# XXX probably only apiwarn() + py.event forwarding +# warn_explicit is actually needed + class WarningBus(object): - def __init__(self): - self._eventbus = EventBus() + def __init__(self, eventbus=py.event): + self._eventbus = eventbus def subscribe(self, callable): - self._eventbus.subscribe(callable) + self._eventbus.subscribe(warning=callable) def unsubscribe(self, callable): - self._eventbus.unsubscribe(callable) + self._eventbus.unsubscribe(warning=callable) def _setforwarding(self): - self._eventbus.subscribe(self._forward) + self._eventbus.subscribe(warning=self._forward) + def _forward(self, warning): py.std.warnings.warn_explicit(warning, category=Warning, filename=str(warning.path), @@ -66,7 +70,7 @@ filename = module path = py.path.local(filename) warning = Warning(msg, path, lineno) - self._eventbus.notify(warning) + self._eventbus.notify(warning=warning) # singleton api warner for py lib apiwarner = WarningBus() From hpk at codespeak.net Mon Feb 16 13:02:02 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 16 Feb 2009 13:02:02 +0100 (CET) Subject: [py-svn] r61945 - py/branch/pytestplugin/py/misc/testing Message-ID: <20090216120202.06504168498@codespeak.net> Author: hpk Date: Mon Feb 16 13:02:02 2009 New Revision: 61945 Modified: py/branch/pytestplugin/py/misc/testing/test_plugin.py Log: try harder to implement correct py lib for test Modified: py/branch/pytestplugin/py/misc/testing/test_plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_plugin.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_plugin.py Mon Feb 16 13:02:02 2009 @@ -95,6 +95,7 @@ pm = PluginManager() KEY = "PYLIB" old = os.environ.get(KEY, None) + olddir = py.path.local(py.__file__).dirpath().dirpath().chdir() try: os.environ[KEY] = "unknownconsider_env" excinfo = py.test.raises(py.process.cmdexec.Error, """ @@ -103,6 +104,7 @@ assert str(excinfo.value).find("ImportError") != -1 assert str(excinfo.value).find("unknownconsider") != -1 finally: + olddir.chdir() if old is None: del os.environ[KEY] else: From hpk at codespeak.net Mon Feb 16 14:08:05 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 16 Feb 2009 14:08:05 +0100 (CET) Subject: [py-svn] r61948 - in py/branch/pytestplugin/py/misc: . testing Message-ID: <20090216130805.4B472168498@codespeak.net> Author: hpk Date: Mon Feb 16 14:08:02 2009 New Revision: 61948 Modified: py/branch/pytestplugin/py/misc/plugin.py py/branch/pytestplugin/py/misc/testing/test_plugin.py Log: use less globals Modified: py/branch/pytestplugin/py/misc/plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/plugin.py (original) +++ py/branch/pytestplugin/py/misc/plugin.py Mon Feb 16 14:08:02 2009 @@ -1,12 +1,14 @@ import py -from py.__.misc.event import eventbus class PluginManager: _pyspec = "pylib" - def __init__(self): + def __init__(self, bus=None): + if bus is None: + from py.__.misc.event import eventbus as bus + self._bus = bus self.list = [] - eventbus.subscribe(self._forwardevent) + self._bus.subscribe(self._forwardevent) def _forwardevent(self, (name, obj)): self.calleach("pyevent_" + name, obj) @@ -14,7 +16,7 @@ def import_module(self, modspec): # XXX allow modspec to specify version / lookup modpath = modspec - py.event.notify(importingmodule=modpath) + self._bus.notify(importingmodule=modpath) __import__(modpath) def consider_env(self): Modified: py/branch/pytestplugin/py/misc/testing/test_plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_plugin.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_plugin.py Mon Feb 16 14:08:02 2009 @@ -87,8 +87,9 @@ assert len(l) == 1 assert l[0] == mod.pylib -def test_api(): +def test_api_and_defaults(): assert isinstance(py.plugin, PluginManager) + assert py.plugin._bus is py.event def test_subprocess_env(): # XXX write a helper for preserving os.environ From hpk at codespeak.net Mon Feb 16 14:37:54 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 16 Feb 2009 14:37:54 +0100 (CET) Subject: [py-svn] r61949 - in py/branch/pytestplugin/py/test: . testing Message-ID: <20090216133754.D6851168478@codespeak.net> Author: hpk Date: Mon Feb 16 14:37:54 2009 New Revision: 61949 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/event.py py/branch/pytestplugin/py/test/pmanage.py py/branch/pytestplugin/py/test/session.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_pmanage.py Log: intermediate: use more of new event bus Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Mon Feb 16 14:37:54 2009 @@ -4,7 +4,6 @@ from conftesthandle import Conftest from py.__.test.defaultconftest import adddefaultoptions from py.__.test.pmanage import PluginManager -from py.__.test.event import EventBus optparse = py.compat.optparse @@ -31,11 +30,12 @@ Option = optparse.Option _initialized = False - def __init__(self): + def __init__(self, bus=None): self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") - self.bus = EventBus() + from py.__.test.event import EventBus + self.bus = EventBus(bus) self.pluginmanager = PluginManager() self._conftest = Conftest(onimport=self.pluginmanager.consider_module) @@ -254,7 +254,7 @@ pass # this is the one per-process instance of py.test configuration -config_per_process = Config() +config_per_process = Config(bus=py.event) # default import paths for sessions Modified: py/branch/pytestplugin/py/test/event.py ============================================================================== --- py/branch/pytestplugin/py/test/event.py (original) +++ py/branch/pytestplugin/py/test/event.py Mon Feb 16 14:37:54 2009 @@ -5,23 +5,44 @@ import py import time from py.__.test.outcome import Skipped +import py.__.misc.event + + +class CallWithValue(object): + def __init__(self, callback): + self.callback = callback + def __call__(self, (name, obj)): + return self.callback(obj) class EventBus(object): - """ General Event Bus for distributing events. """ - def __init__(self): - self._subscribers = [] + """ Bus for distributing events. """ + def __init__(self, bus=None): + if bus is None: + bus = py.__.misc.event.EventBus() + self._bus = bus + + def issubscribed(self, callback): + for x in self._bus._getsubscribers(''): + if isinstance(x, CallWithValue) and x.callback == callback: + return True - def subscribe(self, callback): + def subscribe(self, callback=None, **kw): """ subscribe given callback to bus events. """ - self._subscribers.append(callback) + if callback is not None: + callback = CallWithValue(callback) + self._bus.subscribe(callback, **kw) - def unsubscribe(self, callback): + def unsubscribe(self, callback, **kw): """ unsubscribe given callback from bus events. """ - self._subscribers.remove(callback) + for x in self._bus._getsubscribers(''): + if isinstance(x, CallWithValue) and x.callback == callback: + self._bus.unsubscribe(x, **kw) + break + else: + self._bus.unsubscribe(**kw) def notify(self, event): - for subscriber in self._subscribers: - subscriber(event) + self._bus.notify(anonymous=event) class BaseEvent(object): Modified: py/branch/pytestplugin/py/test/pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/pmanage.py (original) +++ py/branch/pytestplugin/py/test/pmanage.py Mon Feb 16 14:37:54 2009 @@ -112,11 +112,11 @@ return l def configure(self, config): - assert self.forward_event not in config.bus._subscribers - config.bus._subscribers.append(self.forward_event) + #assert self.forward_event not in config.bus._subscribers + config.bus.subscribe(self.forward_event) self.callplugins("pytest_configure", config=config) self._configured = config def unconfigure(self, config): self.callplugins("pytest_unconfigure", config=config) - config.bus._subscribers.remove(self.forward_event) + config.bus.unsubscribe(self.forward_event) Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Mon Feb 16 14:37:54 2009 @@ -7,7 +7,6 @@ import py from py.__.test import event, outcome -from py.__.test.event import EventBus import py.__.test.custompdb # used for genitems() Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Mon Feb 16 14:37:54 2009 @@ -3,11 +3,12 @@ from py.__.test.config import gettopdir from py.__.test.testing import suptest -from py.__.test import event -from py.__.test.config import Config def getcolitems(config): return [config.getfsnode(arg) for arg in config.args] + +def test_default_bus(): + assert py.test.config.bus._bus is py.event def test_tmpdir(): d1 = py.test.ensuretemp('hello') @@ -118,7 +119,7 @@ # we check that config "remote" config objects # have correct plugin initialization assert config2.pluginmanager._plugins - assert config2.pluginmanager.forward_event in config2.bus._subscribers + assert config2.bus.issubscribed(config2.pluginmanager.forward_event) for col1, col2 in zip(getcolitems(config), getcolitems(config2)): assert col1.fspath == col2.fspath cols = getcolitems(config2) Modified: py/branch/pytestplugin/py/test/testing/test_pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pmanage.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pmanage.py Mon Feb 16 14:37:54 2009 @@ -166,9 +166,9 @@ pm.addpluginclass(My3) config = pytestConfig() pm.configure(config) - assert pm.forward_event in config.bus._subscribers + assert config.bus.issubscribed(pm.forward_event) pm.unconfigure(config) - assert pm.forward_event not in config.bus._subscribers + assert not config.bus.issubscribed(pm.forward_event) assert l == ['configure', 'unconfigure'] def test_pytest_event(self): From hpk at codespeak.net Mon Feb 16 15:01:56 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 16 Feb 2009 15:01:56 +0100 (CET) Subject: [py-svn] r61950 - py/branch/pytestplugin/py/test Message-ID: <20090216140156.D72DD16849F@codespeak.net> Author: hpk Date: Mon Feb 16 15:01:54 2009 New Revision: 61950 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/event.py py/branch/pytestplugin/py/test/session.py Log: intermediate prepare move to kw events Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Mon Feb 16 15:01:54 2009 @@ -4,6 +4,7 @@ from conftesthandle import Conftest from py.__.test.defaultconftest import adddefaultoptions from py.__.test.pmanage import PluginManager +from py.__.test.event import EventBus optparse = py.compat.optparse @@ -34,8 +35,9 @@ self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") - from py.__.test.event import EventBus - self.bus = EventBus(bus) + if bus is None: + bus = EventBus() + self.bus = bus self.pluginmanager = PluginManager() self._conftest = Conftest(onimport=self.pluginmanager.consider_module) @@ -254,7 +256,7 @@ pass # this is the one per-process instance of py.test configuration -config_per_process = Config(bus=py.event) +config_per_process = Config(bus=EventBus(bus=py.event)) # default import paths for sessions Modified: py/branch/pytestplugin/py/test/event.py ============================================================================== --- py/branch/pytestplugin/py/test/event.py (original) +++ py/branch/pytestplugin/py/test/event.py Mon Feb 16 15:01:54 2009 @@ -41,9 +41,11 @@ else: self._bus.unsubscribe(**kw) - def notify(self, event): - self._bus.notify(anonymous=event) - + def notify(self, event=None, **kw): + if event is not None: + kw = kw.copy() + kw['*'] = event + self._bus.notify(**kw) class BaseEvent(object): def __repr__(self): Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Mon Feb 16 15:01:54 2009 @@ -45,16 +45,16 @@ if isinstance(next, Item): remaining = self.filteritems([next]) if remaining: - self.bus.notify(event.ItemStart(next)) + self.bus.notify(itemstart=event.ItemStart(next)) yield next else: assert isinstance(next, Collector) - self.bus.notify(event.CollectionStart(next)) + self.bus.notify(collectionstart=event.CollectionStart(next)) ev = basic_collect_report(next) if ev.passed: for x in self.genitems(ev.result, keywordexpr): yield x - self.bus.notify(ev) + self.bus.notify(collectionfinish=ev) def filteritems(self, colitems): """ return items to process (some may be deselected)""" @@ -72,7 +72,7 @@ continue remaining.append(colitem) if deselected: - self.bus.notify(event.Deselected(deselected, )) + self.bus.notify(deselectedtest=event.Deselected(deselected, )) if self.config.option.keyword.endswith(":"): self._nomatch = True return remaining @@ -84,7 +84,7 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.bus.notify(event.TestrunStart()) + self.bus.notify(sessionstarts=event.TestrunStart()) # XXX the following is not used or neccessary for the DSession subclass self._failurelist = [] self.bus.subscribe(self._processfailures) @@ -97,7 +97,7 @@ def sessionfinishes(self, exitstatus=0, excinfo=None): """ teardown any resources after a test run. """ - self.bus.notify(event.TestrunFinish(exitstatus=exitstatus, + self.bus.notify(sessionfinishes=event.TestrunFinish(exitstatus=exitstatus, excinfo=excinfo)) self.bus.unsubscribe(self._processfailures) return self._failurelist @@ -113,7 +113,7 @@ colitems = self.getinitialitems(colitems) self.shouldstop = False self.sessionstarts() - self.bus.notify(makehostup()) + self.bus.notify(hostup=makehostup()) exitstatus = outcome.EXIT_OK captured_excinfo = None try: @@ -127,7 +127,7 @@ captured_excinfo = py.code.ExceptionInfo() exitstatus = outcome.EXIT_INTERRUPTED except: - self.bus.notify(event.InternalException()) + self.bus.notify(internalerror=event.InternalException()) exitstatus = outcome.EXIT_INTERNALERROR if self._failurelist and exitstatus == 0: exitstatus = outcome.EXIT_TESTSFAILED @@ -141,5 +141,4 @@ runner = item._getrunner() pdb = self.config.option.usepdb and self.runpdb or None testrep = runner(item, pdb=pdb) - self.bus.notify(testrep) - + self.bus.notify(itemtestreport=testrep) From hpk at codespeak.net Mon Feb 16 16:13:13 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 16 Feb 2009 16:13:13 +0100 (CET) Subject: [py-svn] r61951 - in py/branch/pytestplugin/py/test: . dsession dsession/testing looponfail looponfail/testing testing Message-ID: <20090216151313.D12CF168478@codespeak.net> Author: hpk Date: Mon Feb 16 16:13:11 2009 New Revision: 61951 Modified: py/branch/pytestplugin/py/test/dsession/dsession.py py/branch/pytestplugin/py/test/dsession/hostmanage.py py/branch/pytestplugin/py/test/dsession/masterslave.py py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py py/branch/pytestplugin/py/test/event.py py/branch/pytestplugin/py/test/looponfail/remote.py py/branch/pytestplugin/py/test/looponfail/testing/test_util.py py/branch/pytestplugin/py/test/testing/test_event.py Log: intermediate: only notify with named events, disallow sending unnamed events. Modified: py/branch/pytestplugin/py/test/dsession/dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/dsession.py Mon Feb 16 16:13:11 2009 @@ -43,6 +43,10 @@ self._testsfailed = False self.trace = config.maketrace("dsession.log") + def queueput(self, **kw): + assert len(kw) == 1 + self.queue.put(kw) + def fixoptions(self): """ check, fix and determine conflicting options. """ option = self.config.option @@ -97,12 +101,14 @@ # we use a timeout here so that control-C gets through while 1: try: - ev = self.queue.get(timeout=2.0) + kw = self.queue.get(timeout=2.0) break except Queue.Empty: continue loopstate.dowork = True - self.bus.notify(ev) + self.bus.notify(**kw) + assert len(kw) == 1 + name, ev = kw.items()[0] if isinstance(ev, event.ItemTestReport): self.removeitem(ev.colitem) if ev.failed: @@ -133,9 +139,10 @@ def loop_once_shutdown(self, loopstate): # once we are in shutdown mode we dont send # events other than HostDown upstream - ev = self.queue.get() - if isinstance(ev, event.HostDown): - self.bus.notify(ev) + kw = self.queue.get() + name, ev = kw.items()[0] + if name == "hostdown": + self.bus.notify(hostdown=ev) self.removehost(ev.host) if not self.host2pending: # finished @@ -156,7 +163,7 @@ except KeyboardInterrupt: exitstatus = outcome.EXIT_INTERRUPTED except: - self.bus.notify(event.InternalException()) + self.bus.notify(internalerror=event.InternalException()) exitstatus = outcome.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: exitstatus = outcome.EXIT_TESTSFAILED @@ -185,8 +192,8 @@ senditems.append(next) else: ev = basic_collect_report(next) - self.bus.notify(event.CollectionStart(next)) - self.queue.put(ev) + self.bus.notify(collectionstart=event.CollectionStart(next)) + self.queue.put({'collectionstart': ev}) self.senditems(senditems) def senditems(self, tosend): @@ -203,14 +210,14 @@ # "sending same item %r to multiple hosts " # "not implemented" %(item,)) self.item2host[item] = host - self.bus.notify(event.ItemStart(item, host)) + self.bus.notify(itemstart=event.ItemStart(item, host)) pending.extend(sending) tosend[:] = tosend[room:] # update inplace if not tosend: break if tosend: # we have some left, give it to the main loop - self.queue.put(event.RescheduleItems(tosend)) + self.queueput(rescheduleitems = event.RescheduleItems(tosend)) def removeitem(self, item): if item not in self.item2host: @@ -222,12 +229,15 @@ def handle_crashitem(self, item, host): longrepr = "!!! Host %r crashed during running of test %r" %(host, item) rep = event.ItemTestReport(item, when="???", excinfo=longrepr) - self.bus.notify(rep) + self.bus.notify(itemtestreport=rep) def setup_hosts(self): """ setup any neccessary resources ahead of the test run. """ self.hm = HostManager(self) - self.hm.setup_hosts(notify=self.queue.put) + def kwnotify(**kw): + assert len(kw) == 1 + self.queue.put(kw) + self.hm.setup_hosts(notify=kwnotify) def teardown_hosts(self): """ teardown any resources after a test run. """ Modified: py/branch/pytestplugin/py/test/dsession/hostmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/hostmanage.py (original) +++ py/branch/pytestplugin/py/test/dsession/hostmanage.py Mon Feb 16 16:13:11 2009 @@ -148,7 +148,7 @@ def prepare_gateways(self): for host in self.hosts: host.initgateway() - self.session.bus.notify(event.HostGatewayReady(host, self.roots)) + self.session.bus.notify(hostgatewayready=event.HostGatewayReady(host, self.roots)) def init_rsync(self): self.prepare_gateways() @@ -164,7 +164,7 @@ for host in self.hosts: rsync.add_target_host(host, destrelpath) rsync.send(raises=False) - self.session.bus.notify(event.RsyncFinished()) + self.session.bus.notify(rsyncfinished=event.RsyncFinished()) def setup_hosts(self, notify=None): if notify is None: Modified: py/branch/pytestplugin/py/test/dsession/masterslave.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/masterslave.py (original) +++ py/branch/pytestplugin/py/test/dsession/masterslave.py Mon Feb 16 16:13:11 2009 @@ -30,19 +30,20 @@ if not self._down: if not err: err = "TERMINATED" - self.notify(event.HostDown(self.host, err)) + self.notify(hostdown=event.HostDown(self.host, err)) return if ev is None: self._down = True - self.notify(event.HostDown(self.host, None)) + self.notify(hostdown=event.HostDown(self.host, None)) return except KeyboardInterrupt: raise except: excinfo = py.code.ExceptionInfo() print "!" * 20, excinfo - ev = event.InternalException(excinfo) - self.notify(ev) + self.notify(internalerror=event.InternalException(excinfo)) + else: + self.notify(itemtestreport=ev) def send(self, item): assert item is not None Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py Mon Feb 16 16:13:11 2009 @@ -69,7 +69,8 @@ """) session = DSession(modcol._config) session.triggertesting([modcol]) - rep = session.queue.get(block=False) + kw = session.queue.get(block=False) + name, rep = kw.items()[0] assert isinstance(rep, event.CollectionReport) assert len(rep.result) == 1 @@ -89,7 +90,8 @@ assert host2_sent == [item] * session.MAXITEMSPERHOST assert session.host2pending[host1] == host1_sent assert session.host2pending[host2] == host2_sent - ev = session.queue.get(block=False) + kw = session.queue.get(block=False) + name, ev = kw.items()[0] assert isinstance(ev, event.RescheduleItems) assert ev.items == [item] @@ -117,17 +119,17 @@ session.addhost(host1) ev = event.RescheduleItems([item]) loopstate = LoopState([]) - session.queue.put(ev) + session.queueput(rescheduleitems=ev) session.loop_once(loopstate) # check that RescheduleEvents are not immediately # rescheduled if there are no hosts assert loopstate.dowork == False - session.queue.put(event.NOP()) + session.queueput(anonymous=event.NOP()) session.loop_once(loopstate) - session.queue.put(event.NOP()) + session.queueput(anonymous=event.NOP()) session.loop_once(loopstate) assert host1.node.sent == [[item]] - session.queue.put(run(item)) + session.queueput(itemtestreport=run(item)) session.loop_once(loopstate) assert loopstate.shuttingdown assert not loopstate.testsfailed @@ -142,7 +144,7 @@ # setup a HostDown event ev = event.HostDown(host1, None) - session.queue.put(ev) + session.queueput(hostdown=ev) loopstate = LoopState([item]) loopstate.dowork = False @@ -172,7 +174,7 @@ session.senditems([item1, item2]) host = session.item2host[item1] ev = event.HostDown(host, None) - session.queue.put(ev) + session.queueput(hostdown=ev) events = [] ; session.bus.subscribe(events.append) loopstate = LoopState([]) @@ -191,7 +193,7 @@ session = DSession(item._config) host1 = Host("localhost") hostup = makehostup(host1) - session.queue.put(hostup) + session.queueput(hostup=hostup) loopstate = LoopState([item]) loopstate.dowork = False assert len(session.host2pending) == 0 @@ -204,7 +206,7 @@ ev = event.NOP() events = [] ; session.bus.subscribe(events.append) - session.queue.put(ev) + session.queueput(anonymous=ev) session.loop_once(LoopState([])) assert events[0] == ev @@ -215,15 +217,15 @@ session.addhost(host1) loopstate = LoopState([item]) - session.queue.put(event.NOP()) + session.queueput(anonymous=event.NOP()) session.loop_once(loopstate) assert host1.node.sent == [[item]] ev = run(item) - session.queue.put(ev) + session.queueput(itemtestreport=ev) session.loop_once(loopstate) assert loopstate.shuttingdown - session.queue.put(event.HostDown(host1, None)) + session.queueput(hostdown=event.HostDown(host1, None)) session.loop_once(loopstate) dumpqueue(session.queue) return session, loopstate.exitstatus @@ -258,8 +260,8 @@ # run tests ourselves and produce reports ev1 = run(items[0]) ev2 = run(items[1]) - session.queue.put(ev1) # a failing one - session.queue.put(ev2) + session.queueput(itemtestreport=ev1) # a failing one + session.queueput(itemtestreport=ev2) # now call the loop loopstate = LoopState(items) session.loop_once(loopstate) @@ -275,11 +277,11 @@ loopstate.shuttingdown = True l = [] session.bus.subscribe(l.append) - session.queue.put(run(item)) + session.queueput(itemtestreport=run(item)) session.loop_once(loopstate) assert not l ev = event.HostDown(host) - session.queue.put(ev) + session.queueput(hostdown=ev) session.loop_once(loopstate) assert l == [ev] @@ -320,13 +322,13 @@ host.node = MockNode() session.addhost(host) session.senditems([item]) - session.queue.put(run(item)) + session.queueput(itemtestreport=run(item)) loopstate = LoopState([]) session.loop_once(loopstate) assert host.node._shutdown is True assert loopstate.exitstatus is None, "loop did not wait for hostdown" assert loopstate.shuttingdown - session.queue.put(event.HostDown(host, None)) + session.queueput(hostdown=event.HostDown(host, None)) session.loop_once(loopstate) assert loopstate.exitstatus == 0 @@ -347,10 +349,10 @@ session.senditems([item1]) # host2pending will become empty when the loop sees # the report - session.queue.put(run(item1)) + session.queueput(itemtestreport=run(item1)) # but we have a collection pending - session.queue.put(colreport) + session.queueput(collectionreport=colreport) loopstate = LoopState([]) session.loop_once(loopstate) Modified: py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py Mon Feb 16 16:13:11 2009 @@ -280,14 +280,18 @@ session = py.test.config._reparse([self.source]).initsession() hm = HostManager(session, hosts=hosts) queue = py.std.Queue.Queue() - hm.setup_hosts(notify=queue.put) + def queueput(**kw): + queue.put(kw) + hm.setup_hosts(notify=queueput) for host in hm.hosts: - ev = queue.get(timeout=2.0) + kw = queue.get(timeout=2.0) + name, ev = kw.items()[0] assert isinstance(ev, event.HostUp) for host in hm.hosts: host.node.shutdown() for host in hm.hosts: - ev = queue.get(timeout=2.0) + kw = queue.get(timeout=2.0) + name, ev = kw.items()[0] assert isinstance(ev, event.HostDown) def XXXtest_ssh_rsync_samehost_twice(self): Modified: py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py Mon Feb 16 16:13:11 2009 @@ -10,13 +10,15 @@ events = [] while 1: try: - ev = self.queue.get(timeout=timeout) + kw = self.queue.get(timeout=timeout) except py.std.Queue.Empty: print "node channel", self.node.channel print "remoteerror", self.node.channel._getremoteerror() print "seen events", events raise IOError("did not see %r events" % (eventtype)) else: + assert len(kw) == 1 + name, ev = kw.items()[0] if isinstance(ev, eventtype): return ev events.append(ev) @@ -28,7 +30,9 @@ self.queue = py.std.Queue.Queue() self.host = Host("localhost") self.host.initgateway() - self.node = MasterNode(self.host, self.config, self.queue.put) + def queueput(**kw): + self.queue.put(kw) + self.node = MasterNode(self.host, self.config, queueput) assert not self.node.channel.isclosed() def getitem(self, source): Modified: py/branch/pytestplugin/py/test/event.py ============================================================================== --- py/branch/pytestplugin/py/test/event.py (original) +++ py/branch/pytestplugin/py/test/event.py Mon Feb 16 16:13:11 2009 @@ -41,10 +41,7 @@ else: self._bus.unsubscribe(**kw) - def notify(self, event=None, **kw): - if event is not None: - kw = kw.copy() - kw['*'] = event + def notify(self, **kw): self._bus.notify(**kw) class BaseEvent(object): Modified: py/branch/pytestplugin/py/test/looponfail/remote.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/remote.py (original) +++ py/branch/pytestplugin/py/test/looponfail/remote.py Mon Feb 16 16:13:11 2009 @@ -164,5 +164,5 @@ session.main(colitems) session.bus.unsubscribe(recordfailures) ev = event.LooponfailingInfo(failreports, [config.topdir]) - session.bus.notify(ev) + session.bus.notify(looponfailinginfo=ev) channel.send([x.colitem._totrail() for x in failreports if x.failed]) Modified: py/branch/pytestplugin/py/test/looponfail/testing/test_util.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/testing/test_util.py (original) +++ py/branch/pytestplugin/py/test/looponfail/testing/test_util.py Mon Feb 16 16:13:11 2009 @@ -66,19 +66,19 @@ def test_eventrecorder(): bus = event.EventBus() recorder = EventRecorder(bus) - bus.notify(event.NOP()) + bus.notify(anonymous=event.NOP()) assert recorder.events assert not recorder.getfailures() rep = event.ItemTestReport(None, None) rep.failed = True - bus.notify(rep) + bus.notify(itemtestreport=rep) failures = recorder.getfailures() assert failures == [rep] recorder.clear() assert not recorder.events assert not recorder.getfailures() recorder.unsubscribe() - bus.notify(rep) + bus.notify(itemtestreport=rep) assert not recorder.events assert not recorder.getfailures() Modified: py/branch/pytestplugin/py/test/testing/test_event.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_event.py (original) +++ py/branch/pytestplugin/py/test/testing/test_event.py Mon Feb 16 16:13:11 2009 @@ -9,9 +9,9 @@ bus = EventBus() l = [] bus.subscribe(l.append) - bus.notify(1) - bus.notify(2) - bus.notify(3) + bus.notify(x=1) + bus.notify(x=2) + bus.notify(x=3) assert l == [1,2,3] def test_multi_sub(self): @@ -20,9 +20,9 @@ l2 = [] bus.subscribe(l1.append) bus.subscribe(l2.append) - bus.notify(1) - bus.notify(2) - bus.notify(3) + bus.notify(x=1) + bus.notify(x=2) + bus.notify(x=3) assert l1 == [1,2,3] assert l2 == [1,2,3] @@ -30,9 +30,9 @@ bus = EventBus() l = [] bus.subscribe(l.append) - bus.notify(1) + bus.notify(x=1) bus.unsubscribe(l.append) - bus.notify(2) + bus.notify(x=22) assert l == [1] def test_event_attributes(): From hpk at codespeak.net Mon Feb 16 18:02:32 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 16 Feb 2009 18:02:32 +0100 (CET) Subject: [py-svn] r61952 - in py/branch/pytestplugin/py: misc misc/testing test test/testing Message-ID: <20090216170232.1F6391684BD@codespeak.net> Author: hpk Date: Mon Feb 16 18:02:30 2009 New Revision: 61952 Modified: py/branch/pytestplugin/py/misc/plugin.py py/branch/pytestplugin/py/misc/testing/test_plugin.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py Log: another intermediate checkin for merging plugin logic Modified: py/branch/pytestplugin/py/misc/plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/plugin.py (original) +++ py/branch/pytestplugin/py/misc/plugin.py Mon Feb 16 18:02:30 2009 @@ -5,7 +5,8 @@ def __init__(self, bus=None): if bus is None: - from py.__.misc.event import eventbus as bus + from py.__.misc.event import EventBus + bus = EventBus() self._bus = bus self.list = [] self._bus.subscribe(self._forwardevent) @@ -38,7 +39,7 @@ def register(self, plugin): assert not isinstance(plugin, str) self.list.append(plugin) - py.event.notify(pluginregistered=plugin) + self._bus.notify(pluginregistered=plugin) def iterattr(self, attrname): for plugin in self.list: @@ -60,4 +61,9 @@ break return ret -plugin = PluginManager() + def callone(self, plugin, methname, *args, **kwargs): + call = getattr(plugin, methname, None) + if call is not None: + return call(*args, **kwargs) + +plugin = PluginManager(bus=py.event) Modified: py/branch/pytestplugin/py/misc/testing/test_plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_plugin.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_plugin.py Mon Feb 16 18:02:30 2009 @@ -8,8 +8,9 @@ pm = PluginManager() class MyPlugin: pass - pm.register(MyPlugin) - assert MyPlugin in pm.list + my = MyPlugin() + pm.register(my) + assert my in pm.list def test_onregister(self): pm = PluginManager() @@ -82,7 +83,7 @@ excinfo = py.test.raises(ImportError, "pm.consider_module(mod)") mod.pylib = "os" l = [] - py.event.subscribe(importingmodule=l.append) + pm._bus.subscribe(importingmodule=l.append) pm.consider_module(mod) assert len(l) == 1 assert l[0] == mod.pylib Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Mon Feb 16 18:02:30 2009 @@ -4,15 +4,21 @@ import py class PytestPluginManager(object): - def __init__(self, pluginmanager=py.plugin): - self.pm = pluginmanager + def __init__(self, pm=None): + if pm is None: + from py.__.misc.plugin import PluginManager + pm = PluginManager() + self.pm = pm # # API for interacting with registered and instantiated plugin objects # def setinitial(self, modules): for module in modules: - self.pm.consider_module(module) + self.consider_module(module) + + def consider_module(self, module): + self.pm.consider_module(module) def add_cmdlineoptions(self, config): # XXX think about sorting/grouping of options from user-perspective @@ -24,11 +30,11 @@ def ensure_configured_plugins(self, config): def configureplugin(plugin): self.pm.callone(plugin, "pytest_configure", config=config) - self.configureplugin = configureplugin - self.pm.subscribe(pluginregistered=autoconfigure) - self.pm.callplugins("pytest_configure", config=config) + self._configureplugin = configureplugin + self.pm._bus.subscribe(pluginregistered=self._configureplugin) + self.pm.calleach("pytest_configure", config=config) def unconfigure_plugins(self, config): - self.pm.callplugins("pytest_unconfigure", config=config) - self.pm.unsubscribe(pluginregistered=self.configureplugin) - del self.configureplugin + self.pm.calleach("pytest_unconfigure", config=config) + self.pm._bus.unsubscribe(pluginregistered=self._configureplugin) + del self._configureplugin Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Mon Feb 16 18:02:30 2009 @@ -1,6 +1,32 @@ import py from py.__.test.handleplugin import PytestPluginManager +def test_considermodule(): + pp = PytestPluginManager() + mod = py.std.new.module("hello") + mod.pylib = "helloworldmod_42" + py.test.raises(ImportError, "pp.consider_module(mod)") + +def test_setinitial(): + pp = PytestPluginManager() + mod = py.std.new.module("hello") + mod.pylib = "helloworldmod_42" + py.test.raises(ImportError, "pp.setinitial([mod])") + +def test_ensureconfigure(): + pp = PytestPluginManager() + l = [] + class A: + def pytest_configure(self, config): + l.append(self) + pp.pm.register(A()) + assert len(l) == 0 + pp.ensure_configured_plugins(config=42) + assert len(l) == 1 + pp.pm.register(A()) + assert len(l) == 2 + assert l[0] != l[1] + def test_addcmdlineoptions(): class PseudoPM: def iterattr(self, name): From getxsick at codespeak.net Mon Feb 16 20:30:16 2009 From: getxsick at codespeak.net (getxsick at codespeak.net) Date: Mon, 16 Feb 2009 20:30:16 +0100 (CET) Subject: [py-svn] r61954 - in py/trunk/py: bin execnet misc process Message-ID: <20090216193016.ABEAD1684FB@codespeak.net> Author: getxsick Date: Mon Feb 16 20:30:14 2009 New Revision: 61954 Modified: py/trunk/py/bin/gendoc.py py/trunk/py/execnet/register.py py/trunk/py/misc/_dist.py py/trunk/py/process/cmdexec.py Log: removed of using some deprecated modules/functions from stdlib. used subprocess instead (the module is included to stdlib since 2.4) Modified: py/trunk/py/bin/gendoc.py ============================================================================== --- py/trunk/py/bin/gendoc.py (original) +++ py/trunk/py/bin/gendoc.py Mon Feb 16 20:30:14 2009 @@ -15,11 +15,12 @@ sys.path.insert(0, '.') import py -import os +import os, subprocess def sysexec(cmd): print "executing", cmd - os.system(cmd) + p = subprocess.Popen(cmd, shell=True) + os.waitpid(p.pid, 0) if __name__ == '__main__': pydir = py.path.local().join("py") Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Mon Feb 16 20:30:14 2009 @@ -1,5 +1,6 @@ import os, inspect, socket +from subprocess import Popen, PIPE import sys from py.magic import autopath ; mypath = autopath() from py.__.misc.warn import APIWARN @@ -56,7 +57,8 @@ class PopenCmdGateway(InstallableGateway): def __init__(self, cmd): - infile, outfile = os.popen2(cmd) + p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True) + infile, outfile = p.stdin, p.stdout self._cmd = cmd io = inputoutput.Popen2IO(infile, outfile) super(PopenCmdGateway, self).__init__(io=io) Modified: py/trunk/py/misc/_dist.py ============================================================================== --- py/trunk/py/misc/_dist.py (original) +++ py/trunk/py/misc/_dist.py Mon Feb 16 20:30:14 2009 @@ -1,5 +1,6 @@ import py import sys, os, re +import subprocess from distutils import sysconfig from distutils import core @@ -147,7 +148,8 @@ win32con.SMTO_ABORTIFHUNG, 5000) # Propagate changes to current command prompt - os.system("set PATH=%s" % path) + p = subprocess.Popen("set PATH=%s" % path, shell=True) + os.waitpid(p.pid, 0) def get_registry_value(reg, key, value_name): k = _winreg.OpenKey(reg, key) Modified: py/trunk/py/process/cmdexec.py ============================================================================== --- py/trunk/py/process/cmdexec.py (original) +++ py/trunk/py/process/cmdexec.py Mon Feb 16 20:30:14 2009 @@ -24,13 +24,13 @@ the error-output from the command. """ __tracebackhide__ = True - import popen2 + from subprocess import Popen, PIPE import errno #print "execing", cmd - child = popen2.Popen3(cmd, 1) - stdin, stdout, stderr = child.tochild, child.fromchild, child.childerr - stdin.close() + child = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, + close_fds=True) + stdin, stdout, stderr = child.stdin, child.stdout, child.stderr # XXX sometimes we get a blocked r.read() call (see below) # although select told us there is something to read. From hpk at codespeak.net Tue Feb 17 09:56:30 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 17 Feb 2009 09:56:30 +0100 (CET) Subject: [py-svn] r61961 - in py/trunk/py: doc test Message-ID: <20090217085630.726FF1684F6@codespeak.net> Author: hpk Date: Tue Feb 17 09:56:29 2009 New Revision: 61961 Modified: py/trunk/py/doc/test.txt py/trunk/py/test/defaultconftest.py Log: improve documentation about "-k" keyword handling. Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Tue Feb 17 09:56:29 2009 @@ -139,8 +139,8 @@ .. _`selection by keyword`: -selecting tests by keyword --------------------------- +selecting/unselecting tests by keyword +--------------------------------------------- You can selectively run tests by specifiying a keyword on the command line. Example:: @@ -151,10 +151,20 @@ and where the word "test_simple" equals the start of one part of the path leading up to the test item. Directory and file basenames as well as function, class and function/method names each form a possibly -matching name. +matching name. You can also unselect tests by preceding a keyword +with a dash:: + + py.test. -k "-test_simple" + +will run all tests except where the word "test_simple" matches a tests keyword. +Note that you need to quote the keyword if the shell recognizes "-" as an intro +to a cmdline option. Lastly, you may use + + py.test. -k "test_simple:" - Note that the exact semantics are still experimental but - should always remain intuitive. +which will run all tests after the expression has *matched once*, i.e. +all tests that are seen after a test that matches the "test_simple" +keyword. testing with multiple python versions / executables --------------------------------------------------- Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Tue Feb 17 09:56:29 2009 @@ -42,7 +42,9 @@ Option('-k', action="store", dest="keyword", default='', help="only run test items matching the given " - "keyword expression."), + "comma separated keywords. precede keyword with '-' to negate. " + "Terminate with ':' to treat a match as a signal to run all subsequent tests. " + ), Option('-l', '--showlocals', action="store_true", dest="showlocals", default=False, help="show locals in tracebacks (disabled by default)."), From hpk at codespeak.net Tue Feb 17 12:33:16 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 17 Feb 2009 12:33:16 +0100 (CET) Subject: [py-svn] r61965 - py/trunk/py/test Message-ID: <20090217113316.462D9168452@codespeak.net> Author: hpk Date: Tue Feb 17 12:33:15 2009 New Revision: 61965 Modified: py/trunk/py/test/defaultconftest.py Log: correct help string Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Tue Feb 17 12:33:15 2009 @@ -42,8 +42,8 @@ Option('-k', action="store", dest="keyword", default='', help="only run test items matching the given " - "comma separated keywords. precede keyword with '-' to negate. " - "Terminate with ':' to treat a match as a signal to run all subsequent tests. " + "space separated keywords. precede a keyword with '-' to negate. " + "Terminate the expression with ':' to treat a match as a signal to run all subsequent tests. " ), Option('-l', '--showlocals', action="store_true", dest="showlocals", default=False, From hpk at codespeak.net Tue Feb 17 15:35:41 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 17 Feb 2009 15:35:41 +0100 (CET) Subject: [py-svn] r61973 - in py/branch/pytestplugin/py: misc misc/testing test test/plugin test/testing Message-ID: <20090217143541.7656F1684E9@codespeak.net> Author: hpk Date: Tue Feb 17 15:35:39 2009 New Revision: 61973 Modified: py/branch/pytestplugin/py/misc/plugin.py py/branch/pytestplugin/py/misc/testing/test_plugin.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/defaultconftest.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/plugin/conftest.py py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/testing/conftest.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py py/branch/pytestplugin/py/test/testing/test_pmanage.py Log: puh! this is a major milestone in merging pytest/pylib event and plugin logic - i probably should have used a more incremental approach for this, will try to do so now. Modified: py/branch/pytestplugin/py/misc/plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/plugin.py (original) +++ py/branch/pytestplugin/py/misc/plugin.py Tue Feb 17 15:35:39 2009 @@ -39,6 +39,7 @@ def register(self, plugin): assert not isinstance(plugin, str) self.list.append(plugin) + #print "registering", self, plugin self._bus.notify(pluginregistered=plugin) def iterattr(self, attrname): @@ -51,14 +52,17 @@ def calleach(self, methname, *args, **kwargs): ret = [] for call in self.iterattr(methname): - ret.append(call(*args, **kwargs)) + result = call(*args, **kwargs) + ret.append(result) return ret def callfirst(self, methname, *args, **kwargs): ret = [] for call in self.iterattr(methname): - ret.append(call(*args, **kwargs)) - break + result = call(*args, **kwargs) + if result is not None: # XXX odd to interpret the result here + ret.append(result) + break return ret def callone(self, plugin, methname, *args, **kwargs): Modified: py/branch/pytestplugin/py/misc/testing/test_plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_plugin.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_plugin.py Tue Feb 17 15:35:39 2009 @@ -50,6 +50,24 @@ l = pm.callfirst('m') assert l == [42] + def test_callfirst_ignores_None(self): + pm = PluginManager() + class api1: + def m(self): + return None + class api2: + def m(self): + return 41 + pm.register(api1()) + pm.register(api1()) + pm.register(api1()) + pm.register(api2()) + pm.register(api1()) + pm.register(api1()) + l = pm.callfirst('m') + assert l == [41] + + def test_iterattr(self): pm = PluginManager() class api1: Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Tue Feb 17 15:35:39 2009 @@ -3,7 +3,7 @@ import py from conftesthandle import Conftest from py.__.test.defaultconftest import adddefaultoptions -from py.__.test.pmanage import PluginManager +from py.__.test.handleplugin import PytestPluginManager from py.__.test.event import EventBus optparse = py.compat.optparse @@ -31,14 +31,17 @@ Option = optparse.Option _initialized = False - def __init__(self, bus=None): + def __init__(self, bus=None, pluginmanager=None): self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") if bus is None: bus = EventBus() self.bus = bus - self.pluginmanager = PluginManager() + if pluginmanager is None: + pluginmanager = PytestPluginManager() + assert isinstance(pluginmanager, PytestPluginManager) + self.pluginmanager = pluginmanager self._conftest = Conftest(onimport=self.pluginmanager.consider_module) def parse(self, args): @@ -50,7 +53,9 @@ self._initialized = True adddefaultoptions(self) self._conftest.setinitial(args) - self.pluginmanager.setinitial(self._conftest.getconftestmodules(None)) + #print "parse, seeing pluginmanager", self.pluginmanager.pm + #self.pluginmanager.setinitial(self._conftest.getconftestmodules(None)) + #print "plugins", self.pluginmanager.pm.list self.pluginmanager.add_cmdlineoptions(self) args = [str(x) for x in args] cmdlineoption, args = self._parser.parse_args(args) @@ -75,7 +80,7 @@ self.topdir = py.path.local(topdir) self._mergerepr(self._repr) del self._repr - self.pluginmanager.configure(self) + self.pluginmanager.configure(config=self) def _makerepr(self): l = [] @@ -256,7 +261,9 @@ pass # this is the one per-process instance of py.test configuration -config_per_process = Config(bus=EventBus(bus=py.event)) +config_per_process = Config( + bus=EventBus(bus=py.event), + pluginmanager=PytestPluginManager(py.plugin)) # default import paths for sessions Modified: py/branch/pytestplugin/py/test/defaultconftest.py ============================================================================== --- py/branch/pytestplugin/py/test/defaultconftest.py (original) +++ py/branch/pytestplugin/py/test/defaultconftest.py Tue Feb 17 15:35:39 2009 @@ -10,11 +10,7 @@ conf_iocapture = "fd" # overridable from conftest.py -pytest_plugins = [ - 'pytest_terminal', - 'pytest_resultlog', - 'pytest_eventlog', -] +pytest_plugins = "terminal resultlog eventlog".split() # =================================================== # Distributed testing specific options Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Tue Feb 17 15:35:39 2009 @@ -1,40 +1,111 @@ """ -handling py.test related plugins +handling py.test plugins. """ import py class PytestPluginManager(object): def __init__(self, pm=None): + from py.__.misc.plugin import PluginManager if pm is None: - from py.__.misc.plugin import PluginManager pm = PluginManager() + #assert isinstance(pm, PluginManager) self.pm = pm + self._plugins = {} + # API for bootstrapping # - # API for interacting with registered and instantiated plugin objects - # + def getplugin(self, pname): + return self._plugins[pname.lower()] + def setinitial(self, modules): for module in modules: self.consider_module(module) - def consider_module(self, module): - self.pm.consider_module(module) + def consider_module(self, mod): + attr = getattr(mod, "pytest_plugins", ()) + if not isinstance(attr, (list, tuple)): + attr = (attr,) + for spec in attr: + if spec: + self.import_plugin(spec) + + def import_plugin(self, spec): + if isinstance(spec, str): + modname, clsname = canonical_names(spec) + if clsname not in self._plugins: + mod = importplugin(modname) + plugin = registerplugin(self.pm.register, mod, clsname) + self._plugins[clsname.lower()] = plugin + self.consider_module(mod) + else: + clsname = spec.__name__ + plugin = spec() + self.pm.register(plugin) + print "registered", plugin, "at", self + + # + # + # API for interacting with registered and instantiated plugin objects + # + # + def getfirst(self, methname): + for x in self.pm.iterattr(methname): + return x + + def callfirst(self, *args, **kwargs): + return self.pm.callfirst(*args, **kwargs) + + def calleach(self, *args, **kwargs): + return self.pm.calleach(*args, **kwargs) + def add_cmdlineoptions(self, config): # XXX think about sorting/grouping of options from user-perspective + #assert self.pm.list opts = [] - for name, options in self.pm.iterattr("pytest_cmdlineoptions"): + for options in self.pm.iterattr("pytest_cmdlineoptions"): opts.extend(options) config.addoptions("ungrouped options added by plugins", *opts) - def ensure_configured_plugins(self, config): + def configure(self, config): def configureplugin(plugin): self.pm.callone(plugin, "pytest_configure", config=config) + if hasattr(plugin, 'pytest_event'): + config.bus._bus.subscribe(plugin.pytest_event) self._configureplugin = configureplugin self.pm._bus.subscribe(pluginregistered=self._configureplugin) - self.pm.calleach("pytest_configure", config=config) + for plugin in self.pm.list: + configureplugin(plugin) - def unconfigure_plugins(self, config): - self.pm.calleach("pytest_unconfigure", config=config) + def unconfigure(self, config): self.pm._bus.unsubscribe(pluginregistered=self._configureplugin) + for plugin in self.pm.list: + self.pm.callone(plugin, "pytest_unconfigure", config=config) + if hasattr(plugin, 'pytest_event'): + config.bus._bus.unsubscribe(plugin.pytest_event) del self._configureplugin + +# +# XXX old code to automatically load classes +# +def canonical_names(importspec): + modprefix = "pytest_" + if not importspec.startswith(modprefix): + importspec = modprefix + importspec + clsname = importspec[len(modprefix):].capitalize() + return importspec, clsname + +def registerplugin(registerfunc, mod, clsname): + pluginclass = getattr(mod, clsname) + plugin = pluginclass() + registerfunc(plugin) + return plugin + +def importplugin(importspec): + try: + return __import__(importspec) + except ImportError, e: + try: + return __import__("py.__.test.plugin.%s" %(importspec), None, None, '__doc__') + except ImportError: + raise ImportError(importspec) Modified: py/branch/pytestplugin/py/test/plugin/conftest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/conftest.py (original) +++ py/branch/pytestplugin/py/test/plugin/conftest.py Tue Feb 17 15:35:39 2009 @@ -1,6 +1,6 @@ import py -pytest_plugins = "pytest_pytester", "pytest_plugintester" +pytest_plugins = "pytester", "plugintester" class Directory(py.test.collect.Directory): def consider_file(self, path): Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Tue Feb 17 15:35:39 2009 @@ -30,7 +30,7 @@ plugintester.apicheck(Eventlog) fstester = plugintester.fstester() - fstester.makepyfile(test_one=""" + fstester.makepyfile(""" def test_pass(): pass """) Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Tue Feb 17 15:35:39 2009 @@ -35,10 +35,10 @@ return crunner def apicheck(self, impclass): - from py.__.test.pmanage import PluginManager + from py.__.test.handleplugin import PytestPluginManager print "loading and checking", impclass fail = False - pm = PluginManager() + pm = PytestPluginManager() plugin = pm.import_plugin(impclass) methods = collectattr(plugin.__class__) hooks = collectattr(PytestPluginHooks) Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Tue Feb 17 15:35:39 2009 @@ -90,6 +90,9 @@ if err: for line in err: print >>py.std.sys.stderr, line + if out: + for line in out: + print >>py.std.sys.stdout, line return RunResult(ret, out, err) def runpybin(self, scriptname, *args): @@ -147,6 +150,7 @@ fstester = FSTester(pyfuncitem) return fstester, None + class EventSorter(object): def __init__(self, config, session=None): self.config = config Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Tue Feb 17 15:35:39 2009 @@ -18,7 +18,7 @@ self.resultlog.logfile.close() del self.resultlog - def pytest_event(self, event): + def pytest_event(self, (name, event)): if hasattr(self, 'resultlog'): self.resultlog.log_event_to_file(event) Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Tue Feb 17 15:35:39 2009 @@ -9,7 +9,6 @@ default=False, help="only collect tests, don't execute them."), ] - def pytest_configure(self, config): if config.option.collectonly: self.reporter = CollectonlyReporter(config) @@ -23,8 +22,15 @@ assert hasattr(self.reporter._tw, name), name setattr(self.reporter._tw, name, getattr(config, attr)) - def pytest_event(self, event): - self.reporter.processevent(event) + def pytest_event(self, (name, obj)): + #print "processing event", event + #print "looking for", name + repmethod = getattr(self.reporter, "pyevent_%s" % name, None) + if repmethod is not None: + #print "found", repmethod + repmethod(obj) + else: + self.reporter.processevent(obj) class BaseReporter(object): def __init__(self): @@ -119,9 +125,9 @@ self._tw.sep(sep, title, **markup) def getcategoryletterword(self, event): - result = self.config.pluginmanager.callfirst("pytest_report_teststatus", event=event) - if result is not None: - return result + reslist = self.config.pluginmanager.callfirst("pytest_report_teststatus", event=event) + if reslist: + return reslist[0] for cat in 'skipped failed passed ???'.split(): if getattr(event, cat, None): break @@ -148,6 +154,9 @@ if self.config.option.verbose: self.write_line("HostGatewayReady: %s" %(ev.host,)) + def pyevent_pluginregistered(self, plugin): + self.write_line("PLUGIN registered: %s" %(plugin,)) + def rep_HostUp(self, ev): d = ev.platinfo.copy() d['hostid'] = ev.host.hostid @@ -205,14 +214,20 @@ super(TerminalReporter, self).rep_TestrunStart(ev) self.write_sep("=", "test session starts", bold=True) self._sessionstarttime = py.std.time.time() - #self.out_hostinfo() + rev = py.__pkg__.getrev() + self.write_line("using py lib: %s " % ( + py.path.local(py.__file__).dirpath(), rev)) + plugins = ", ".join(list(self.config.pluginmanager._plugins)) + self.write_line("active plugins: %s" %(plugins,)) + for i, testarg in py.builtin.enumerate(self.config.args): + self.write_line("test object %d: %s" %(i+1, testarg)) def rep_TestrunFinish(self, ev): self._tw.line("") if ev.exitstatus in (0, 1, 2): self.summary_failures() self.summary_skips() - self.config.pluginmanager.callplugins("pytest_terminal_summary", terminalreporter=self) + self.config.pluginmanager.calleach("pytest_terminal_summary", terminalreporter=self) if ev.excrepr is not None: self.summary_final_exc(ev.excrepr) if ev.exitstatus == 2: Modified: py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py Tue Feb 17 15:35:39 2009 @@ -14,6 +14,7 @@ to test functions and methods. """ def pytest_pyfunc_arg(self, pyfuncitem, argname): + print "tmpdir: receiving call", pyfuncitem, argname if argname == "tmpdir": basename = "_".join(pyfuncitem.listnames()) # move to config Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Tue Feb 17 15:35:39 2009 @@ -137,10 +137,10 @@ return self.join(name) def makeitem(self, name, obj): - res = self._config.pluginmanager.callfirst( + reslist = self._config.pluginmanager.callfirst( "pytest_pymodule_makeitem", modcol=self, name=name, obj=obj) - if res is not None: - return res + if reslist: + return reslist[0] if (self.classnamefilter(name)) and \ py.std.inspect.isclass(obj): res = self._deprecated_join(name) @@ -354,17 +354,17 @@ return kwargs def fillarg(self, argname, kwargs): - res = self._config.pluginmanager.callfirst( + reslist = self._config.pluginmanager.callfirst( "pytest_pyfunc_arg", pyfuncitem=self, argname=argname) - if res is not None: - value, argfinalizer = res + if reslist: + value, argfinalizer = reslist[0] kwargs[argname] = value if argfinalizer is not None: self._argfinalizers.append(argfinalizer) else: - #self._config.pluginmanager.trace( - # "could not find argument %r, plugins=%r" %( - # argname,self._config.pluginmanager._plugins)) + print "pluginmanager is", self._config.pluginmanager + print "could not find argument %r, plugins=%r" %( + argname,self._config.pluginmanager._plugins) raise TypeError("could not provide funcargument %r" %(argname,)) def __eq__(self, other): Modified: py/branch/pytestplugin/py/test/testing/conftest.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/conftest.py (original) +++ py/branch/pytestplugin/py/test/testing/conftest.py Tue Feb 17 15:35:39 2009 @@ -1,2 +1,2 @@ -pytest_plugins = "pytest_pytester", "pytest_tmpdir" +pytest_plugins = "pytest_xfail", "pytest_pytester", "pytest_tmpdir" Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Tue Feb 17 15:35:39 2009 @@ -118,8 +118,8 @@ config2._initafterpickle(topdir=tmp.dirpath()) # we check that config "remote" config objects # have correct plugin initialization - assert config2.pluginmanager._plugins - assert config2.bus.issubscribed(config2.pluginmanager.forward_event) + #XXX assert config2.pluginmanager.pm._plugins + #XXX assert config2.bus.issubscribed(config2.pluginmanager.forward_event) for col1, col2 in zip(getcolitems(config), getcolitems(config2)): assert col1.fspath == col2.fspath cols = getcolitems(config2) Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Tue Feb 17 15:35:39 2009 @@ -1,19 +1,20 @@ import py -from py.__.test.handleplugin import PytestPluginManager +from py.__.test.handleplugin import PytestPluginManager, canonical_names +from py.__.test.handleplugin import registerplugin, importplugin def test_considermodule(): pp = PytestPluginManager() mod = py.std.new.module("hello") - mod.pylib = "helloworldmod_42" + mod.pytest_plugins = "helloworldmod_42" py.test.raises(ImportError, "pp.consider_module(mod)") def test_setinitial(): pp = PytestPluginManager() mod = py.std.new.module("hello") - mod.pylib = "helloworldmod_42" + mod.pytest_plugins = "helloworldmod_42" py.test.raises(ImportError, "pp.setinitial([mod])") -def test_ensureconfigure(): +def test_configure(): pp = PytestPluginManager() l = [] class A: @@ -21,11 +22,15 @@ l.append(self) pp.pm.register(A()) assert len(l) == 0 - pp.ensure_configured_plugins(config=42) + pp.configure(config=42) assert len(l) == 1 pp.pm.register(A()) assert len(l) == 2 assert l[0] != l[1] + pp.unconfigure(config=42) + pp.pm.register(A()) + assert len(l) == 2 + def test_addcmdlineoptions(): class PseudoPM: @@ -42,3 +47,255 @@ pc = PytestPluginManager(pseudopm) pc.add_cmdlineoptions(pseudoconfig) assert len(pseudoconfig.opts) == 1 + +def test_canonical_names(): + impname, clsname = canonical_names("xyz") + assert impname == "pytest_xyz" + assert clsname == "Xyz" + impname, clsname = canonical_names("pytest_xyz") + assert impname == "pytest_xyz" + assert clsname == "Xyz" + +def test_registerplugin(): + l = [] + registerfunc = l.append + registerplugin(registerfunc, py.io, "TerminalWriter") + assert len(l) == 1 + assert isinstance(l[0], py.io.TerminalWriter) + +def test_importplugin(): + assert importplugin("py") == py + py.test.raises(ImportError, "importplugin('laksjd.qwe')") + mod = importplugin("pytest_terminal") + assert mod == py.__.test.plugin.pytest_terminal + +import os, sys +import py +from py.__.test.event import NOP +from py.__.test.config import Config as pytestConfig + +class TestInitialization: + disabled = True + def setup_method(self, method): + self.tmpdir = py.test.ensuretemp("%s.%s.%s" % + (__name__, self.__class__.__name__, method.__name__)) + + def test_import_plugin_importname(self): + pm = PytestPluginManager() + py.test.raises(ImportError, 'pm.import_plugin("x.y")') + py.test.raises(ImportError, 'pm.import_plugin("pytest_x.y")') + + sys.path.insert(0, str(self.tmpdir)) + try: + pluginname = "pytest_hello" + self.tmpdir.join(pluginname + ".py").write(py.code.Source(""" + class Hello: + pass + """)) + pm.import_plugin("hello") + pm.import_plugin("pytest_hello") + plugin = pm.getplugin("hello") + finally: + sys.path.remove(str(self.tmpdir)) + + def test_import_plugin_defaults_to_pytestplugin(self): + from py.__.test.defaultconftest import pytest_plugins + for name in pytest_plugins: + if isinstance(name, str): + break + sys.path.insert(0, str(self.tmpdir)) + try: + self.tmpdir.join(name + ".py").write(py.code.Source(""" + class Terminal: + pass + class AnotherPlugin: + pass + pytest_plugins = AnotherPlugin + """)) + pm = PytestPluginManager() + pm.import_plugin(name) + plugin = pm.getplugin("terminal") + #del sys.modules[name] + print pm._plugins + plugin = pm.getplugin("anotherplugin") + finally: + sys.path.remove(str(self.tmpdir)) + + def test_import_plugin_class(self): + pm = PytestPluginManager() + class SomePlugin: + pass + pm.import_plugin(SomePlugin) + plugin = pm.getplugin("someplugin") + assert isinstance(plugin, SomePlugin) + i = len(pm._plugins) + pm.import_plugin(SomePlugin) + assert len(pm._plugins) == i + + def test_addpluginclass_post_configure(self): + pm = PytestPluginManager() + l = [] + class SomePlugin: + def pytest_configure(self, config): + l.append(config) + conf = pytestConfig() + pm.configure(config=conf) + pm.import_plugin(SomePlugin) + assert len(l) == 1 + assert l[0] is conf + + def test_consider_module(self): + pm = PytestPluginManager() + sys.path.insert(0, str(self.tmpdir)) + try: + self.tmpdir.join("pytest_plug1.py").write("class Plug1: pass") + self.tmpdir.join("pytest_plug2.py").write("class Plug2: pass") + mod = py.std.new.module("temp") + mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] + pm.consider_module(mod) + assert pm.getplugin("plug1").__class__.__name__ == "Plug1" + assert pm.getplugin("plug2").__class__.__name__ == "Plug2" + finally: + sys.path.remove(str(self.tmpdir)) + + + def test_addpluginclass(self): + pm = PytestPluginManager() + class My: + pass + pm.addpluginclass(My) + assert len(pm._plugins) == 1 + pm.addpluginclass(My) + assert len(pm._plugins) == 1 + + def test_getplugin(self): + pm = PytestPluginManager() + assert py.test.raises(LookupError, "pm.getplugin('_xxx')") + + class PluGin: pass + pm.addpluginclass(PluGin) + myplugin1 = pm.getplugin("plugin") + myplugin2 = pm.getplugin("Plugin") + assert myplugin1 is myplugin2 + +class TestPluginInteraction: + disabled = True + def test_getfirst(self): + pm = PytestPluginManager() + class My1: + x = 1 + assert pm.getfirst("x") is None + pm.addpluginclass(My1) + assert pm.getfirst("x") == 1 + + def test_callplugins(self): + pm = PytestPluginManager() + class My: + def method(self, arg): + pass + pm.addpluginclass(My) + py.test.raises(TypeError, 'pm.callplugins("method")') + py.test.raises(TypeError, 'pm.callplugins("method", 42)') + pm.callplugins("method", arg=42) + py.test.raises(TypeError, 'pm.callplugins("method", arg=42, s=13)') + + def test_callfirst(self): + pm = PytestPluginManager() + class My1: + def method(self): + pass + class My2: + def method(self): + return True + class My3: + def method(self): + return None + assert pm.callfirst("method") is None + assert pm.callfirst("methodnotexists") is None + pm.addpluginclass(My1) + assert pm.callfirst("method") is None + pm.addpluginclass(My2) + assert pm.callfirst("method") == True + pm.addpluginclass(My3) + assert pm.callfirst("method") == True + + def test_listattr(self): + pm = PytestPluginManager() + class My2: + x = 42 + pm.addpluginclass(My2) + assert not pm.listattr("hello") + assert pm.listattr("x") == [('my2', 42)] + + def test_eventbus_interaction(self): + pm = PytestPluginManager() + l = [] + class My3: + def pytest_configure(self, config): + l.append("configure") + def pytest_unconfigure(self, config): + l.append("unconfigure") + pm.addpluginclass(My3) + config = pytestConfig() + pm.configure(config) + assert config.bus.issubscribed(pm.forward_event) + pm.unconfigure(config) + assert not config.bus.issubscribed(pm.forward_event) + assert l == ['configure', 'unconfigure'] + + def test_pytest_event(self): + pm = PytestPluginManager() + l = [] + class My2: + def pytest_event(self, event): + l.append(event) + pm.addpluginclass(My2) + ev = NOP() + pm.forward_event(ev) + assert len(l) == 1 + assert l[0] is ev + + def test_addcmdlineoptions(self): + pm = PytestPluginManager() + class My: + pytest_cmdlineoptions = [ + py.test.config.Option("--hello", dest="dest", default=242) + ] + pm.addpluginclass(My) + config = pytestConfig() + pm.add_cmdlineoptions(config) + opt = config._parser.get_option("--hello") + assert opt + assert opt.default == 242 + + def test_setinitial_env(self): + pm = PytestPluginManager() + KEY = "PYTEST_PLUGINS" + old = os.environ.get(KEY, None) + try: + os.environ[KEY] = "test_setinitial" + py.test.raises(ImportError, "pm.setinitial([])") + finally: + if old is None: + del os.environ[KEY] + else: + os.environ[KEY] = old + + def test_conftest_specifies_plugin(self, fstester): + fstester.makepyfile( + conftest=""" + import py + class MyPlugin: + pytest_cmdlineoptions = [ + py.test.config.Option("--myplugin-option", dest="myplugin", + help="myplugin option", + ) + ] + pytest_plugins = MyPlugin + """ + ) + result = fstester.runpytest(fstester.tmpdir, '-h') + result.stdout.fnmatch_lines([ + "*--myplugin-option*", + ]) + Modified: py/branch/pytestplugin/py/test/testing/test_pmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pmanage.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pmanage.py Tue Feb 17 15:35:39 2009 @@ -27,6 +27,7 @@ finally: sys.path.remove(str(self.tmpdir)) + @py.test.keywords(xfail=True) def test_import_plugin_defaults_to_pytestplugin(self): from py.__.test.defaultconftest import pytest_plugins for name in pytest_plugins: From hpk at codespeak.net Tue Feb 17 17:55:21 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 17 Feb 2009 17:55:21 +0100 (CET) Subject: [py-svn] r61974 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090217165521.5D90D16851E@codespeak.net> Author: hpk Date: Tue Feb 17 17:55:18 2009 New Revision: 61974 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/event.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/testing/test_event.py Log: refactoring terminal reporter towards keyword based event handling. tests pass, so i commit. Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Tue Feb 17 17:55:18 2009 @@ -4,7 +4,6 @@ from conftesthandle import Conftest from py.__.test.defaultconftest import adddefaultoptions from py.__.test.handleplugin import PytestPluginManager -from py.__.test.event import EventBus optparse = py.compat.optparse @@ -36,7 +35,7 @@ self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") if bus is None: - bus = EventBus() + bus = py.test._EventBus() self.bus = bus if pluginmanager is None: pluginmanager = PytestPluginManager() @@ -262,7 +261,7 @@ # this is the one per-process instance of py.test configuration config_per_process = Config( - bus=EventBus(bus=py.event), + bus=py.test._EventBus(bus=py.event), pluginmanager=PytestPluginManager(py.plugin)) # default import paths for sessions Modified: py/branch/pytestplugin/py/test/event.py ============================================================================== --- py/branch/pytestplugin/py/test/event.py (original) +++ py/branch/pytestplugin/py/test/event.py Tue Feb 17 17:55:18 2009 @@ -21,9 +21,13 @@ bus = py.__.misc.event.EventBus() self._bus = bus - def issubscribed(self, callback): - for x in self._bus._getsubscribers(''): - if isinstance(x, CallWithValue) and x.callback == callback: + def issubscribed(self, callback=None, **kw): + if callback is not None: + for x in self._bus._getsubscribers(''): + if isinstance(x, CallWithValue) and x.callback == callback: + return True + for name, value in kw.items(): + if value in self._bus._getsubscribers(name): return True def subscribe(self, callback=None, **kw): @@ -32,6 +36,13 @@ callback = CallWithValue(callback) self._bus.subscribe(callback, **kw) + def subscribe_methods(self, instance, prefix = "pyevent_"): + for cls in py.std.inspect.getmro(instance.__class__): + for attrname in cls.__dict__: + if attrname.startswith(prefix): + name = attrname[len(prefix):] + self.subscribe(**{name: getattr(instance, attrname)}) + def unsubscribe(self, callback, **kw): """ unsubscribe given callback from bus events. """ for x in self._bus._getsubscribers(''): Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Tue Feb 17 17:55:18 2009 @@ -41,7 +41,7 @@ clsname = spec.__name__ plugin = spec() self.pm.register(plugin) - print "registered", plugin, "at", self + #print "registered", plugin, "at", self # # Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Tue Feb 17 17:55:18 2009 @@ -32,6 +32,74 @@ else: self.reporter.processevent(obj) +class OutcomeStats: + def __init__(self): + self._reset() + + def _reset(self): + self.skipped = [] + self.passed = [] + self.failed = [] + self.deselected = [] + + def pyevent_itemtestreport(self, ev): + for name in 'skipped failed passed'.split(): + if getattr(ev, name): + getattr(self, name).append(ev) + + def pyevent_collectionreport(self, ev): + for name in 'skipped failed'.split(): + if getattr(ev, name): + getattr(self, name).append(ev) + return + + def pyevent_deselected(self, ev): + self.deselected.extend(ev.items) + + def pyevent_testrunstart(self, ev): + self._reset() + +def test_outcomestats(): + names = 'passed skipped deselected failed'.split() + #for name in names: + # stats = OutcomeStats() + # assert getattr(stats, name) == [] + # bus = py.test._EventBus() + # bus.subscribe_methods(stats) + # class X: pass + # for name2 in names: + # if name2 != name: + # setattr(X, name2, False) + # else: + # setattr(X, name2, True) + # bus.notify(itemtestreport=X) + # assert getattr(stats, name) == [X] + + stats = OutcomeStats() + bus = py.test._EventBus() + bus.subscribe_methods(stats) + class P: + passed = True + failed = skipped = False + bus.notify(itemtestreport=P) + assert stats.passed + assert stats.passed == [P] + bus.notify(itemtestreport=P) + assert stats.passed == [P, P] + class F: + skipped = passed = False + failed = True + bus.notify(itemtestreport=F) + assert stats.failed == [F] + bus.notify(collectionreport=F) + assert stats.failed == [F, F] + class D: + items = [42] + bus.notify(deselected=D) + assert stats.deselected == [42] + + + class BaseReporter(object): def __init__(self): self._reset() Modified: py/branch/pytestplugin/py/test/testing/test_event.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_event.py (original) +++ py/branch/pytestplugin/py/test/testing/test_event.py Tue Feb 17 17:55:18 2009 @@ -35,6 +35,15 @@ bus.notify(x=22) assert l == [1] + def test_subscribe_methods(self): + bus = EventBus() + class A: + def pyevent_hello(self, ev): pass + class B(A): pass + b = B() + bus.subscribe_methods(b) + assert bus.issubscribed(hello=b.pyevent_hello) + def test_event_attributes(): for name, value in vars(event).items(): if py.std.inspect.isclass(value) and issubclass(value, event.BaseEvent): From getxsick at codespeak.net Tue Feb 17 18:24:42 2009 From: getxsick at codespeak.net (getxsick at codespeak.net) Date: Tue, 17 Feb 2009 18:24:42 +0100 (CET) Subject: [py-svn] r61975 - in py/trunk/py: bin execnet misc process Message-ID: <20090217172442.18D5916852D@codespeak.net> Author: getxsick Date: Tue Feb 17 18:24:41 2009 New Revision: 61975 Modified: py/trunk/py/bin/gendoc.py py/trunk/py/execnet/register.py py/trunk/py/misc/_dist.py py/trunk/py/process/cmdexec.py Log: subprocess is in stdlib since 2.4 for backwards compatibility we use compat.subprocess if needed Modified: py/trunk/py/bin/gendoc.py ============================================================================== --- py/trunk/py/bin/gendoc.py (original) +++ py/trunk/py/bin/gendoc.py Tue Feb 17 18:24:41 2009 @@ -15,7 +15,11 @@ sys.path.insert(0, '.') import py -import os, subprocess +import os +try: + import subprocess +except ImportError: + from py.__.compat import subprocess def sysexec(cmd): print "executing", cmd Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Tue Feb 17 18:24:41 2009 @@ -1,10 +1,14 @@ import os, inspect, socket -from subprocess import Popen, PIPE import sys from py.magic import autopath ; mypath = autopath() from py.__.misc.warn import APIWARN +try: + from subprocess import Popen, PIPE +except ImportError: + from py.__.compat.subprocess import Popen, PIPE + import py if sys.platform == "win32": win32 = True Modified: py/trunk/py/misc/_dist.py ============================================================================== --- py/trunk/py/misc/_dist.py (original) +++ py/trunk/py/misc/_dist.py Tue Feb 17 18:24:41 2009 @@ -1,8 +1,11 @@ import py import sys, os, re -import subprocess from distutils import sysconfig from distutils import core +try: + import subprocess +except ImportError: + from py.__.compat import subprocess winextensions = 1 if sys.platform == 'win32': Modified: py/trunk/py/process/cmdexec.py ============================================================================== --- py/trunk/py/process/cmdexec.py (original) +++ py/trunk/py/process/cmdexec.py Tue Feb 17 18:24:41 2009 @@ -24,7 +24,11 @@ the error-output from the command. """ __tracebackhide__ = True - from subprocess import Popen, PIPE + try: + from subprocess import Popen, PIPE + except ImportError: + from py.__.compat.subprocess import Popen, PIPE + import errno #print "execing", cmd From hpk at codespeak.net Tue Feb 17 19:25:46 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 17 Feb 2009 19:25:46 +0100 (CET) Subject: [py-svn] r61978 - in py/branch/pytestplugin/py: . test test/testing Message-ID: <20090217182546.F0057168453@codespeak.net> Author: hpk Date: Tue Feb 17 19:25:44 2009 New Revision: 61978 Modified: py/branch/pytestplugin/py/__init__.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/event.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/testing/test_event.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py Log: automatically unsubscribe all py.test._EventBus subscribers when plugins get "unconfigured" Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Tue Feb 17 19:25:44 2009 @@ -70,6 +70,8 @@ # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), + 'test._EventBus' : ('./test/event.py', 'EventBus'), + 'test._PytestPluginManager' : ('./test/handleplugin.py', 'PytestPluginManager'), 'test.raises' : ('./test/outcome.py', 'raises'), 'test.keywords' : ('./test/outcome.py', 'keywords',), 'test.deprecated_call' : ('./test/outcome.py', 'deprecated_call'), Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Tue Feb 17 19:25:44 2009 @@ -3,7 +3,6 @@ import py from conftesthandle import Conftest from py.__.test.defaultconftest import adddefaultoptions -from py.__.test.handleplugin import PytestPluginManager optparse = py.compat.optparse @@ -38,8 +37,8 @@ bus = py.test._EventBus() self.bus = bus if pluginmanager is None: - pluginmanager = PytestPluginManager() - assert isinstance(pluginmanager, PytestPluginManager) + pluginmanager = py.test._PytestPluginManager(_bus=bus._bus) + assert isinstance(pluginmanager, py.test._PytestPluginManager) self.pluginmanager = pluginmanager self._conftest = Conftest(onimport=self.pluginmanager.consider_module) @@ -262,7 +261,7 @@ # this is the one per-process instance of py.test configuration config_per_process = Config( bus=py.test._EventBus(bus=py.event), - pluginmanager=PytestPluginManager(py.plugin)) + pluginmanager=py.test._PytestPluginManager(py.plugin)) # default import paths for sessions Modified: py/branch/pytestplugin/py/test/event.py ============================================================================== --- py/branch/pytestplugin/py/test/event.py (original) +++ py/branch/pytestplugin/py/test/event.py Tue Feb 17 19:25:44 2009 @@ -20,6 +20,7 @@ if bus is None: bus = py.__.misc.event.EventBus() self._bus = bus + self._memo = [] def issubscribed(self, callback=None, **kw): if callback is not None: @@ -32,10 +33,26 @@ def subscribe(self, callback=None, **kw): """ subscribe given callback to bus events. """ + if not hasattr(self, '_memo'): + raise EnvironmentError("event bus already closed") + d = kw.copy() if callback is not None: + d[''] = callback callback = CallWithValue(callback) + self._memo.append(d) + #print "appending", d self._bus.subscribe(callback, **kw) + def close(self): + for kw in self._memo: + #print "closing", kw + if '' in kw: + callback = kw.pop('') + else: + callback = None + self.unsubscribe(callback, **kw) + del self._memo + def subscribe_methods(self, instance, prefix = "pyevent_"): for cls in py.std.inspect.getmro(instance.__class__): for attrname in cls.__dict__: @@ -43,7 +60,7 @@ name = attrname[len(prefix):] self.subscribe(**{name: getattr(instance, attrname)}) - def unsubscribe(self, callback, **kw): + def unsubscribe(self, callback=None, **kw): """ unsubscribe given callback from bus events. """ for x in self._bus._getsubscribers(''): if isinstance(x, CallWithValue) and x.callback == callback: Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Tue Feb 17 19:25:44 2009 @@ -4,10 +4,10 @@ import py class PytestPluginManager(object): - def __init__(self, pm=None): + def __init__(self, pm=None, _bus=None): from py.__.misc.plugin import PluginManager if pm is None: - pm = PluginManager() + pm = PluginManager(bus=_bus) #assert isinstance(pm, PluginManager) self.pm = pm self._plugins = {} @@ -72,17 +72,16 @@ self.pm.callone(plugin, "pytest_configure", config=config) if hasattr(plugin, 'pytest_event'): config.bus._bus.subscribe(plugin.pytest_event) + #self.bus.subscribe_methods(plugin) self._configureplugin = configureplugin - self.pm._bus.subscribe(pluginregistered=self._configureplugin) + config.bus.subscribe(pluginregistered=self._configureplugin) for plugin in self.pm.list: configureplugin(plugin) def unconfigure(self, config): - self.pm._bus.unsubscribe(pluginregistered=self._configureplugin) for plugin in self.pm.list: self.pm.callone(plugin, "pytest_unconfigure", config=config) - if hasattr(plugin, 'pytest_event'): - config.bus._bus.unsubscribe(plugin.pytest_event) + config.bus.close() del self._configureplugin # Modified: py/branch/pytestplugin/py/test/testing/test_event.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_event.py (original) +++ py/branch/pytestplugin/py/test/testing/test_event.py Tue Feb 17 19:25:44 2009 @@ -5,6 +5,7 @@ from py.__.code.testing.test_excinfo import TWMock class TestEventBus: + def test_simple(self): bus = EventBus() l = [] @@ -44,6 +45,35 @@ bus.subscribe_methods(b) assert bus.issubscribed(hello=b.pyevent_hello) + def test_close_simple(self): + membus = EventBus() + bus = membus._bus + def f(x): pass + membus.subscribe(f) + membus.subscribe(hello=f) + assert len(bus._getsubscribers('hello')) == 1 + assert len(bus._getsubscribers('')) == 1 + membus.close() + assert len(bus._getsubscribers('hello')) == 0 + assert len(bus._getsubscribers('')) == 0 + py.test.raises(EnvironmentError, "membus.subscribe(hello=f)") + + @py.test.keywords(xfail=True) + def test_close_with_unsubscribe(self): + membus = EventBus() + bus = membus._bus + def f(x): pass + def g(x): pass + membus.subscribe(f) + membus.subscribe(hello=f) + membus.unsubscribe(f) + membus.unsubscribe(hello=f) + membus.subscribe(hello=g) + membus.close() + assert len(bus._getsubscribers('hello')) == 0 + assert len(bus._getsubscribers('')) == 0 + + def test_event_attributes(): for name, value in vars(event).items(): if py.std.inspect.isclass(value) and issubclass(value, event.BaseEvent): Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Tue Feb 17 19:25:44 2009 @@ -1,6 +1,7 @@ import py from py.__.test.handleplugin import PytestPluginManager, canonical_names from py.__.test.handleplugin import registerplugin, importplugin +from py.__.test.config import Config as pytestConfig def test_considermodule(): pp = PytestPluginManager() @@ -15,19 +16,20 @@ py.test.raises(ImportError, "pp.setinitial([mod])") def test_configure(): - pp = PytestPluginManager() + config = pytestConfig() + pp = config.pluginmanager l = [] class A: def pytest_configure(self, config): l.append(self) pp.pm.register(A()) assert len(l) == 0 - pp.configure(config=42) + pp.configure(config=config) assert len(l) == 1 pp.pm.register(A()) assert len(l) == 2 assert l[0] != l[1] - pp.unconfigure(config=42) + pp.unconfigure(config=config) pp.pm.register(A()) assert len(l) == 2 From hpk at codespeak.net Tue Feb 17 19:49:26 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 17 Feb 2009 19:49:26 +0100 (CET) Subject: [py-svn] r61979 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090217184926.CB97616852D@codespeak.net> Author: hpk Date: Tue Feb 17 19:49:23 2009 New Revision: 61979 Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Log: print test name as well Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Tue Feb 17 19:49:23 2009 @@ -26,7 +26,8 @@ entry = event.longrepr.reprcrash key = entry.path, entry.lineno, entry.message reason = event.longrepr.reprcrash.message - tr._tw.line("%s:%d: %s" %(entry.path, entry.lineno, entry.message)) + modpath = event.colitem.getmodpath(stopatmodule=False) + tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message)) xpassed = terminalreporter.status2event.get("xpassed") if xpassed: @@ -56,6 +57,6 @@ result = fstester.runpytest(p) extra = result.stdout.fnmatch_lines([ "*XFAILURES*", - "*test_one.py:5: assert 0*", + "*test_one.test_this*test_one.py:5: assert 0*", ]) assert result.ret == 1 From hpk at codespeak.net Tue Feb 17 19:50:20 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 17 Feb 2009 19:50:20 +0100 (CET) Subject: [py-svn] r61980 - in py/branch/pytestplugin/py: misc misc/testing test test/testing Message-ID: <20090217185020.8B3EF168527@codespeak.net> Author: hpk Date: Tue Feb 17 19:50:17 2009 New Revision: 61980 Modified: py/branch/pytestplugin/py/misc/plugin.py py/branch/pytestplugin/py/misc/testing/test_plugin.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py Log: subscribe pyevent_NAME methods from test plugins directly Modified: py/branch/pytestplugin/py/misc/plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/plugin.py (original) +++ py/branch/pytestplugin/py/misc/plugin.py Tue Feb 17 19:50:17 2009 @@ -9,10 +9,6 @@ bus = EventBus() self._bus = bus self.list = [] - self._bus.subscribe(self._forwardevent) - - def _forwardevent(self, (name, obj)): - self.calleach("pyevent_" + name, obj) def import_module(self, modspec): # XXX allow modspec to specify version / lookup Modified: py/branch/pytestplugin/py/misc/testing/test_plugin.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_plugin.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_plugin.py Tue Feb 17 19:50:17 2009 @@ -2,6 +2,7 @@ import py import os from py.__.misc.plugin import PluginManager +pytest_plugins = "xfail" class TestPluginManager: def test_register(self): @@ -12,6 +13,7 @@ pm.register(my) assert my in pm.list + @py.test.keywords(xfail=True) def test_onregister(self): pm = PluginManager() l = [] Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Tue Feb 17 19:50:17 2009 @@ -72,7 +72,7 @@ self.pm.callone(plugin, "pytest_configure", config=config) if hasattr(plugin, 'pytest_event'): config.bus._bus.subscribe(plugin.pytest_event) - #self.bus.subscribe_methods(plugin) + config.bus.subscribe_methods(plugin) self._configureplugin = configureplugin config.bus.subscribe(pluginregistered=self._configureplugin) for plugin in self.pm.list: Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Tue Feb 17 19:50:17 2009 @@ -19,9 +19,13 @@ config = pytestConfig() pp = config.pluginmanager l = [] + events = [] class A: def pytest_configure(self, config): l.append(self) + def pyevent_hello(self, obj): + events.append(obj) + pp.pm.register(A()) assert len(l) == 0 pp.configure(config=config) @@ -29,10 +33,14 @@ pp.pm.register(A()) assert len(l) == 2 assert l[0] != l[1] + + config.bus.notify(hello=42) + assert len(events) == 2 + assert events == [42,42] + pp.unconfigure(config=config) pp.pm.register(A()) assert len(l) == 2 - def test_addcmdlineoptions(): class PseudoPM: @@ -70,6 +78,7 @@ py.test.raises(ImportError, "importplugin('laksjd.qwe')") mod = importplugin("pytest_terminal") assert mod == py.__.test.plugin.pytest_terminal + import os, sys import py From hpk at codespeak.net Tue Feb 17 22:10:27 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 17 Feb 2009 22:10:27 +0100 (CET) Subject: [py-svn] r61981 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090217211027.DEE65168550@codespeak.net> Author: hpk Date: Tue Feb 17 22:10:26 2009 New Revision: 61981 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Log: shift plugin class to end of file Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Tue Feb 17 22:10:26 2009 @@ -137,19 +137,6 @@ self.session = self.config.initsession() return self.config.getfsnode(path) -class Pytester: - def pytest_pyfunc_arg(self, pyfuncitem, argname): - if argname == "linecomp": - return LineComp(), None - elif argname == "LineMatcher": - return LineMatcher, None - elif argname == "tsession": - tsession = TSession(pyfuncitem) - return tsession, None - elif argname == "fstester": - fstester = FSTester(pyfuncitem) - return fstester, None - class EventSorter(object): def __init__(self, config, session=None): @@ -265,3 +252,17 @@ raise AssertionError("expected line not found: %r" % line) extralines.extend(lines1) return extralines + + +class Pytester: + def pytest_pyfunc_arg(self, pyfuncitem, argname): + if argname == "linecomp": + return LineComp(), None + elif argname == "LineMatcher": + return LineMatcher, None + elif argname == "tsession": + tsession = TSession(pyfuncitem) + return tsession, None + elif argname == "fstester": + fstester = FSTester(pyfuncitem) + return fstester, None From hpk at codespeak.net Tue Feb 17 22:38:12 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 17 Feb 2009 22:38:12 +0100 (CET) Subject: [py-svn] r61989 - in py/branch/pytestplugin/py/test: dsession plugin Message-ID: <20090217213812.6BFAD168513@codespeak.net> Author: hpk Date: Tue Feb 17 22:38:12 2009 New Revision: 61989 Modified: py/branch/pytestplugin/py/test/dsession/dsession.py py/branch/pytestplugin/py/test/dsession/masterslave.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Log: move terminal reporter some more towards keyword based events Modified: py/branch/pytestplugin/py/test/dsession/dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/dsession.py Tue Feb 17 22:38:12 2009 @@ -191,9 +191,9 @@ if isinstance(next, Item): senditems.append(next) else: - ev = basic_collect_report(next) self.bus.notify(collectionstart=event.CollectionStart(next)) - self.queue.put({'collectionstart': ev}) + ev = basic_collect_report(next) + self.queueput(collectionreport=ev) self.senditems(senditems) def senditems(self, tosend): @@ -217,7 +217,7 @@ break if tosend: # we have some left, give it to the main loop - self.queueput(rescheduleitems = event.RescheduleItems(tosend)) + self.queueput(rescheduleitems=event.RescheduleItems(tosend)) def removeitem(self, item): if item not in self.item2host: Modified: py/branch/pytestplugin/py/test/dsession/masterslave.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/masterslave.py (original) +++ py/branch/pytestplugin/py/test/dsession/masterslave.py Tue Feb 17 22:38:12 2009 @@ -43,7 +43,13 @@ print "!" * 20, excinfo self.notify(internalerror=event.InternalException(excinfo)) else: - self.notify(itemtestreport=ev) + # XXX we need to have the proper event name + if isinstance(ev, event.HostUp): + self.notify(hostup=ev) + elif isinstance(ev, event.ItemTestReport): + self.notify(itemtestreport=ev) + elif isinstance(ev, event.HostDown): + self.notify(hostdown=ev) def send(self, item): assert item is not None Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Tue Feb 17 22:38:12 2009 @@ -35,6 +35,8 @@ class OutcomeStats: def __init__(self): self._reset() + def _subscribeat(self, bus): + bus.subscribe_methods(self) def _reset(self): self.skipped = [] @@ -45,6 +47,7 @@ def pyevent_itemtestreport(self, ev): for name in 'skipped failed passed'.split(): if getattr(ev, name): + #print "appending", ev getattr(self, name).append(ev) def pyevent_collectionreport(self, ev): @@ -59,49 +62,71 @@ def pyevent_testrunstart(self, ev): self._reset() -def test_outcomestats(): - names = 'passed skipped deselected failed'.split() - #for name in names: - # stats = OutcomeStats() - # assert getattr(stats, name) == [] - # bus = py.test._EventBus() - # bus.subscribe_methods(stats) - # class X: pass - # for name2 in names: - # if name2 != name: - # setattr(X, name2, False) - # else: - # setattr(X, name2, True) - # bus.notify(itemtestreport=X) - # assert getattr(stats, name) == [X] - - stats = OutcomeStats() - bus = py.test._EventBus() - bus.subscribe_methods(stats) - class P: - passed = True - failed = skipped = False - bus.notify(itemtestreport=P) - assert stats.passed - assert stats.passed == [P] - bus.notify(itemtestreport=P) - assert stats.passed == [P, P] - class F: - skipped = passed = False - failed = True - bus.notify(itemtestreport=F) - assert stats.failed == [F] - bus.notify(collectionreport=F) - assert stats.failed == [F, F] - class D: - items = [42] - bus.notify(deselected=D) - assert stats.deselected == [42] - + def get_folded_skips(self): + d = {} + #print "self.status2event skipped", self.status2event.get("skipped", []) + #print "self.stats.skipped", self.stats.skipped + assert len(self.status2event.get('skipped', ())) == len(self.stats.skipped) + for event in self.status2event.get("skipped", []): + entry = event.longrepr.reprcrash + key = entry.path, entry.lineno, entry.message + d.setdefault(key, []).append(event) + l = [] + for key, events in d.iteritems(): + l.append((len(events),) + key) + return l + +class TestOutcomeStats: + def test_outcomestats(self): + names = 'passed skipped deselected failed'.split() + #for name in names: + # stats = OutcomeStats() + # assert getattr(stats, name) == [] + # bus = py.test._EventBus() + # bus.subscribe_methods(stats) + # class X: pass + # for name2 in names: + # if name2 != name: + # setattr(X, name2, False) + # else: + # setattr(X, name2, True) + # bus.notify(itemtestreport=X) + # assert getattr(stats, name) == [X] + + stats = OutcomeStats() + bus = py.test._EventBus() + stats._subscribeat(bus) + class P: + passed = True + failed = skipped = False + bus.notify(itemtestreport=P) + assert stats.passed + assert stats.passed == [P] + bus.notify(itemtestreport=P) + assert stats.passed == [P, P] + class F: + skipped = passed = False + failed = True + bus.notify(itemtestreport=F) + assert stats.failed == [F] + class S: + skipped = True + passed = failed = False + bus.notify(itemtestreport=S) + assert stats.skipped == [S] + + bus.notify(collectionreport=F) + assert stats.failed == [F, F] + class D: + items = [42] + bus.notify(deselected=D) + assert stats.deselected == [42] + class BaseReporter(object): def __init__(self): + self.stats = OutcomeStats() self._reset() def _reset(self): @@ -136,22 +161,23 @@ def rep_Deselected(self, ev): self.status2event.setdefault('deselected', []).extend(ev.items) - def _folded_skips(self): - d = {} - for event in self.status2event.get("skipped", []): - entry = event.longrepr.reprcrash - key = entry.path, entry.lineno, entry.message - d.setdefault(key, []).append(event) - l = [] - for key, events in d.iteritems(): - l.append((len(events),) + key) - return l +def folded_skips(skipped): + d = {} + for event in skipped: + entry = event.longrepr.reprcrash + key = entry.path, entry.lineno, entry.message + d.setdefault(key, []).append(event) + l = [] + for key, events in d.iteritems(): + l.append((len(events),) + key) + return l class TerminalReporter(BaseReporter): def __init__(self, config, file=None): super(TerminalReporter, self).__init__() self.config = config + self.stats._subscribeat(config.bus) self.curdir = py.path.local() if file is None: file = py.std.sys.stdout @@ -356,14 +382,13 @@ if l: self.write_sep("=", "%d tests deselected by %r" %( len(l), self.config.option.keyword), bold=True) - def summary_skips(self): if "failed" not in self.status2event or self.config.option.showskipsummary: - folded_skips = self._folded_skips() - if folded_skips: + fskips = folded_skips(self.status2event.get('skipped', [])) + if fskips: self.write_sep("_", "skipped test summary") - for num, fspath, lineno, reason in folded_skips: + for num, fspath, lineno, reason in fskips: self._tw.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) def summary_final_exc(self, excrepr): @@ -384,7 +409,7 @@ def __init__(self, config, out=None): super(CollectonlyReporter, self).__init__() - self.config = config + self.stats._subscribeat(config.bus) if out is None: out = py.std.sys.stdout self.out = py.io.TerminalWriter(out) @@ -621,11 +646,12 @@ raise KeyboardInterrupt # simulating the user """, configargs=("--showskipsummary",) + ("-v",)*verbose) rep = TerminalReporter(modcol._config, file=linecomp.stringio) - rep.processevent(event.TestrunStart()) + bus = modcol._config.bus + bus.subscribe(rep.processevent) # XXX + bus.notify(testrunstart=event.TestrunStart()) try: for item in tsession.genitems([modcol]): - ev = basic_run_report(item) - rep.processevent(ev) + bus.notify(itemtestreport=basic_run_report(item)) except KeyboardInterrupt: excinfo = py.code.ExceptionInfo() else: @@ -633,7 +659,7 @@ s = linecomp.stringio.getvalue() if not verbose: assert s.find("_keyboard_interrupt.py Fs") != -1 - rep.processevent(event.TestrunFinish(exitstatus=2, excinfo=excinfo)) + bus.notify(testrunfinish=event.TestrunFinish(exitstatus=2, excinfo=excinfo)) text = linecomp.stringio.getvalue() linecomp.assert_contains_lines([ " def test_foobar():", @@ -651,6 +677,29 @@ def test_verbose_keyboard_interrupt(self, tsession, linecomp): self.pseudo_keyboard_interrupt(tsession, linecomp, verbose=True) + def test_skip_reasons_folding(self): + class longrepr: + class reprcrash: + path = 'xyz' + lineno = 3 + message = "justso" + + ev1 = event.CollectionReport(None, None) + ev1.when = "execute" + ev1.skipped = True + ev1.longrepr = longrepr + + ev2 = event.ItemTestReport(None, excinfo=longrepr) + ev2.skipped = True + + l = folded_skips([ev1, ev2]) + assert len(l) == 1 + num, fspath, lineno, reason = l[0] + assert num == 2 + assert fspath == longrepr.reprcrash.path + assert lineno == longrepr.reprcrash.lineno + assert reason == longrepr.reprcrash.message + class TestCollectonly: def test_collectonly_basic(self, tsession, linecomp): modcol = tsession.getmodulecol(configargs=['--collectonly'], source=""" @@ -738,31 +787,6 @@ rep.processevent(ev) assert rep.status2event[outcome] - def test_skip_reasons_folding(self): - rep = BaseReporter() - class longrepr: - class reprcrash: - path = 'xyz' - lineno = 3 - message = "justso" - - ev1 = event.CollectionReport(None, None) - ev1.when = "execute" - ev1.skipped = True - ev1.longrepr = longrepr - - ev2 = event.ItemTestReport(None, excinfo=longrepr) - ev2.skipped = True - rep.processevent(ev1) - rep.processevent(ev2) - assert len(rep.status2event['skipped']) == 2 - l = rep._folded_skips() - assert len(l) == 1 - num, fspath, lineno, reason = l[0] - assert num == 2 - assert fspath == longrepr.reprcrash.path - assert lineno == longrepr.reprcrash.lineno - assert reason == longrepr.reprcrash.message def test_repr_python_version(): py.magic.patch(sys, 'version_info', (2, 5, 1, 'final', 0)) From hpk at codespeak.net Wed Feb 18 09:07:23 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 09:07:23 +0100 (CET) Subject: [py-svn] r61991 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090218080723.C9BB8168569@codespeak.net> Author: hpk Date: Wed Feb 18 09:07:21 2009 New Revision: 61991 Removed: py/branch/pytestplugin/py/test/pmanage.py py/branch/pytestplugin/py/test/testing/test_pmanage.py Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/plugin/pytest_xfail.py py/branch/pytestplugin/py/test/session.py Log: * getting rid of a base class in the Terminal Reporter and moving yet more towards keyword based events. * removing old plugin manage and tests Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Wed Feb 18 09:07:21 2009 @@ -21,11 +21,11 @@ def pytest_terminal_summary(self, terminalreporter): if terminalreporter.config.option.pocoo_sendfailures: tr = terminalreporter - if "failed" in tr.status2event and tr.config.option.tbstyle != "no": + if tr.stats.failed and tr.config.option.tbstyle != "no": terminalreporter.write_sep("=", "Sending failures to %s" %(url.base,)) terminalreporter.write_line("xmlrpcurl: %s" %(url.xmlrpc,)) serverproxy = self.getproxy() - for ev in terminalreporter.status2event['failed']: + for ev in terminalreporter.stats.failed: tw = py.io.TerminalWriter(stringio=True) ev.toterminal(tw) s = tw.stringio.getvalue() Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 18 09:07:21 2009 @@ -22,20 +22,18 @@ assert hasattr(self.reporter._tw, name), name setattr(self.reporter._tw, name, getattr(config, attr)) - def pytest_event(self, (name, obj)): + def pytest_event(self, (name, event)): #print "processing event", event #print "looking for", name - repmethod = getattr(self.reporter, "pyevent_%s" % name, None) + evname = event.__class__.__name__ + repmethod = getattr(self.reporter, "rep_%s" % evname, None) if repmethod is not None: - #print "found", repmethod - repmethod(obj) - else: - self.reporter.processevent(obj) + #print "calling", repmethod, event + repmethod(event) class OutcomeStats: - def __init__(self): + def __init__(self, bus): self._reset() - def _subscribeat(self, bus): bus.subscribe_methods(self) def _reset(self): @@ -47,14 +45,14 @@ def pyevent_itemtestreport(self, ev): for name in 'skipped failed passed'.split(): if getattr(ev, name): - #print "appending", ev - getattr(self, name).append(ev) + l = getattr(self, name) + l.append(ev) def pyevent_collectionreport(self, ev): - for name in 'skipped failed'.split(): - if getattr(ev, name): - getattr(self, name).append(ev) - return + if ev.skipped: + self.skipped.append(ev) + if ev.failed: + self.failed.append(ev) def pyevent_deselected(self, ev): self.deselected.extend(ev.items) @@ -62,20 +60,6 @@ def pyevent_testrunstart(self, ev): self._reset() - def get_folded_skips(self): - d = {} - #print "self.status2event skipped", self.status2event.get("skipped", []) - #print "self.stats.skipped", self.stats.skipped - assert len(self.status2event.get('skipped', ())) == len(self.stats.skipped) - for event in self.status2event.get("skipped", []): - entry = event.longrepr.reprcrash - key = entry.path, entry.lineno, entry.message - d.setdefault(key, []).append(event) - l = [] - for key, events in d.iteritems(): - l.append((len(events),) + key) - return l - class TestOutcomeStats: def test_outcomestats(self): names = 'passed skipped deselected failed'.split() @@ -84,18 +68,13 @@ # assert getattr(stats, name) == [] # bus = py.test._EventBus() # bus.subscribe_methods(stats) - # class X: pass - # for name2 in names: - # if name2 != name: - # setattr(X, name2, False) - # else: - # setattr(X, name2, True) - # bus.notify(itemtestreport=X) - # assert getattr(stats, name) == [X] + # rep = event.ItemTestReport(None) + # setattr(rep, name, True) + # bus.notify(itemtestreport=rep) + # assert getattr(stats, name) == [rep] - stats = OutcomeStats() bus = py.test._EventBus() - stats._subscribeat(bus) + stats = OutcomeStats(bus) class P: passed = True failed = skipped = False @@ -104,63 +83,25 @@ assert stats.passed == [P] bus.notify(itemtestreport=P) assert stats.passed == [P, P] + class F: skipped = passed = False failed = True bus.notify(itemtestreport=F) assert stats.failed == [F] + bus.notify(collectionreport=F) + assert stats.failed == [F, F] class S: skipped = True passed = failed = False bus.notify(itemtestreport=S) assert stats.skipped == [S] - bus.notify(collectionreport=F) - assert stats.failed == [F, F] class D: items = [42] bus.notify(deselected=D) assert stats.deselected == [42] - - -class BaseReporter(object): - def __init__(self): - self.stats = OutcomeStats() - self._reset() - - def _reset(self): - self.status2event = {} - - def processevent(self, ev): - evname = ev.__class__.__name__ - repmethod = getattr(self, "rep_%s" % evname, None) - if repmethod is None: - self.rep(ev) - else: - repmethod(ev) - - def rep(self, ev): - pass - - def rep_ItemTestReport(self, ev): - for name in 'skipped failed passed'.split(): - if getattr(ev, name): - self.status2event.setdefault(name, []).append(ev) - return - - def rep_CollectionReport(self, ev): - for name in 'skipped failed'.split(): - if getattr(ev, name): - self.status2event.setdefault(name, []).append(ev) - return - - def rep_TestrunStart(self, ev): - self._reset() - - def rep_Deselected(self, ev): - self.status2event.setdefault('deselected', []).extend(ev.items) - def folded_skips(skipped): d = {} for event in skipped: @@ -173,19 +114,15 @@ return l -class TerminalReporter(BaseReporter): +class TerminalReporter: def __init__(self, config, file=None): - super(TerminalReporter, self).__init__() - self.config = config - self.stats._subscribeat(config.bus) + self.config = config + self.stats = OutcomeStats(config.bus) self.curdir = py.path.local() if file is None: file = py.std.sys.stdout self._tw = py.io.TerminalWriter(file) - - def _reset(self): self.currentfspath = None - super(TerminalReporter, self)._reset() def write_fspath_result(self, fspath, res): if fspath != self.currentfspath: @@ -284,10 +221,11 @@ self.write_sep("!", "RESCHEDULING %s " %(ev.items,)) def rep_ItemTestReport(self, ev): - #super(TerminalReporter, self).rep_ItemTestReport(ev) fspath = ev.colitem.fspath cat, letter, word = self.getcategoryletterword(ev) - self.status2event.setdefault(cat, []).append(ev) + l = self.stats.__dict__.setdefault(cat, []) + if ev not in l: + l.append(ev) if not self.config.option.verbose: self.write_fspath_result(fspath, letter) else: @@ -296,7 +234,6 @@ self.write_ensure_prefix(line, word) def rep_CollectionReport(self, ev): - super(TerminalReporter, self).rep_CollectionReport(ev) if not ev.passed: if ev.failed: msg = ev.longrepr.reprcrash.message @@ -305,7 +242,6 @@ self.write_fspath_result(ev.colitem.fspath, "S") def rep_TestrunStart(self, ev): - super(TerminalReporter, self).rep_TestrunStart(ev) self.write_sep("=", "test session starts", bold=True) self._sessionstarttime = py.std.time.time() rev = py.__pkg__.getrev() @@ -359,9 +295,9 @@ # def summary_failures(self): - if "failed" in self.status2event and self.config.option.tbstyle != "no": + if self.stats.failed and self.config.option.tbstyle != "no": self.write_sep("=", "FAILURES") - for ev in self.status2event['failed']: + for ev in self.stats.failed: self.write_sep("_") ev.toterminal(self._tw) @@ -371,21 +307,21 @@ keys = "failed passed skipped deselected".split() parts = [] for key in keys: - if key in self.status2event: - parts.append("%d %s" %(len(self.status2event[key]), key)) + val = getattr(self.stats, key) + if val: + parts.append("%d %s" %(len(val), key)) line = ", ".join(parts) # XXX coloring self.write_sep("=", "%s in %.2f seconds" %(line, session_duration)) def summary_deselected(self): - l = self.status2event.get("deselected", None) - if l: + if self.stats.deselected: self.write_sep("=", "%d tests deselected by %r" %( - len(l), self.config.option.keyword), bold=True) + len(self.stats.deselected), self.config.option.keyword), bold=True) def summary_skips(self): - if "failed" not in self.status2event or self.config.option.showskipsummary: - fskips = folded_skips(self.status2event.get('skipped', [])) + if not self.stats.failed or self.config.option.showskipsummary: + fskips = folded_skips(self.stats.skipped) if fskips: self.write_sep("_", "skipped test summary") for num, fspath, lineno, reason in fskips: @@ -404,12 +340,11 @@ py.std.sys.executable, repr_pythonversion())) -class CollectonlyReporter(BaseReporter): +class CollectonlyReporter: INDENT = " " def __init__(self, config, out=None): - super(CollectonlyReporter, self).__init__() - self.stats._subscribeat(config.bus) + self.config = config if out is None: out = py.std.sys.stdout self.out = py.io.TerminalWriter(out) @@ -427,7 +362,6 @@ self.outindent(event.item) def rep_CollectionReport(self, ev): - super(CollectonlyReporter, self).rep_CollectionReport(ev) if not ev.passed: self.outindent("!!! %s !!!" % ev.longrepr.reprcrash.message) self.indent = self.indent[:-len(self.INDENT)] @@ -459,7 +393,7 @@ item = tsession.getitem("def test_func(): pass") rep = TerminalReporter(item._config, linecomp.stringio) host = Host("localhost") - rep.processevent(makehostup(host)) + rep.rep_HostUp(makehostup(host)) linecomp.assert_contains_lines([ "*%s %s %s - Python %s" %(host.hostid, sys.platform, sys.executable, repr_pythonversion(sys.version_info)) @@ -476,15 +410,16 @@ assert 0 """) rep = TerminalReporter(modcol._config, file=linecomp.stringio) - rep.processevent(event.TestrunStart()) + registerdispatcher(rep) + rep.config.bus.notify(testrunstart=event.TestrunStart()) + for item in tsession.genitems([modcol]): ev = basic_run_report(item) - rep.processevent(ev) + rep.config.bus.notify(itemtestreport=ev) linecomp.assert_contains_lines([ "*test_pass_skip_fail.py .sF" ]) - - rep.processevent(event.TestrunFinish()) + rep.config.bus.notify(testrunfinish=event.TestrunFinish()) linecomp.assert_contains_lines([ " def test_func():", "> assert 0", @@ -502,21 +437,21 @@ assert 0 """, configargs=("-v",)) rep = TerminalReporter(modcol._config, file=linecomp.stringio) - rep.processevent(event.TestrunStart()) + registerdispatcher(rep) + rep.config.bus.notify(testrunstart=event.TestrunStart()) items = modcol.collect() for item in items: - rep.processevent(event.ItemStart(item)) + rep.config.bus.notify(itemstart=event.ItemStart(item)) s = linecomp.stringio.getvalue().strip() assert s.endswith(item.name) - ev = basic_run_report(item) - rep.processevent(ev) + rep.config.bus.notify(itemtestreport=basic_run_report(item)) linecomp.assert_contains_lines([ "*test_pass_skip_fail_verbose.py:2: *test_ok*PASS", "*test_pass_skip_fail_verbose.py:4: *test_skip*SKIP", "*test_pass_skip_fail_verbose.py:6: *test_func*FAIL", ]) - rep.processevent(event.TestrunFinish()) + rep.config.bus.notify(testrunfinish=event.TestrunFinish()) linecomp.assert_contains_lines([ " def test_func():", "> assert 0", @@ -526,14 +461,14 @@ def test_collect_fail(self, tsession, linecomp): modcol = tsession.getmodulecol("import xyz") rep = TerminalReporter(modcol._config, file=linecomp.stringio) - tsession.session.bus.subscribe(rep.processevent) - rep.processevent(event.TestrunStart()) + registerdispatcher(rep) + rep.config.bus.notify(testrunstart=event.TestrunStart()) l = list(tsession.genitems([modcol])) assert len(l) == 0 linecomp.assert_contains_lines([ "*test_collect_fail.py F*" ]) - rep.processevent(event.TestrunFinish()) + rep.config.bus.notify(testrunfinish=event.TestrunFinish()) linecomp.assert_contains_lines([ "> import xyz", "E ImportError: No module named xyz" @@ -543,7 +478,7 @@ modcol = tsession.getmodulecol("def test_one(): pass") rep = TerminalReporter(modcol._config, file=linecomp.stringio) excinfo = py.test.raises(ValueError, "raise ValueError('hello')") - rep.processevent(event.InternalException(excinfo)) + rep.rep_InternalException(event.InternalException(excinfo)) linecomp.assert_contains_lines([ "InternalException: >*raise ValueError*" ]) @@ -555,11 +490,11 @@ """, configargs=("-v",)) host1 = Host("localhost") rep = TerminalReporter(modcol._config, file=linecomp.stringio) - rep.processevent(event.HostGatewayReady(host1, None)) + rep.rep_HostGatewayReady(event.HostGatewayReady(host1, None)) linecomp.assert_contains_lines([ "*HostGatewayReady*" ]) - rep.processevent(event.HostDown(host1, "myerror")) + rep.rep_HostDown(event.HostDown(host1, "myerror")) linecomp.assert_contains_lines([ "*HostDown*myerror*", ]) @@ -584,7 +519,7 @@ """) rep = TerminalReporter(modcol._config, file=linecomp.stringio) reports = [basic_run_report(x) for x in modcol.collect()] - rep.processevent(event.LooponfailingInfo(reports, [modcol._config.topdir])) + rep.rep_LooponfailingInfo(event.LooponfailingInfo(reports, [modcol._config.topdir])) linecomp.assert_contains_lines([ "*test_looponfailingreport.py:2: assert 0", "*test_looponfailingreport.py:4: ValueError*", @@ -593,7 +528,8 @@ ]) def test_tb_option(self, tsession, linecomp): - for tbopt in ["no", "short", "long"]: + # XXX usage of tsession and event bus + for tbopt in ["long", "short", "no"]: print 'testing --tb=%s...' % tbopt modcol = tsession.getmodulecol(""" import py @@ -604,13 +540,14 @@ g() # --calling-- """, configargs=("--tb=%s" % tbopt,)) rep = TerminalReporter(modcol._config, file=linecomp.stringio) - rep.processevent(event.TestrunStart()) + registerdispatcher(rep) + rep.config.bus.notify(testrunstart=event.TestrunStart()) for item in tsession.genitems([modcol]): - ev = basic_run_report(item) - rep.processevent(ev) - rep.processevent(event.TestrunFinish()) + rep.config.bus.notify(itemtestreport=basic_run_report(item)) + rep.config.bus.notify(testrunfinish=event.TestrunFinish()) s = linecomp.stringio.getvalue() if tbopt == "long": + print s assert 'print 6*7' in s else: assert 'print 6*7' not in s @@ -629,9 +566,10 @@ pass """) rep = TerminalReporter(modcol._config, file=linecomp.stringio) + registerdispatcher(rep) l = list(tsession.genitems([modcol])) assert len(l) == 1 - rep.processevent(event.ItemStart(l[0])) + rep.config.bus.notify(itemstart=event.ItemStart(l[0])) linecomp.assert_contains_lines([ "*test_show_path_before_running_test.py*" ]) @@ -646,8 +584,8 @@ raise KeyboardInterrupt # simulating the user """, configargs=("--showskipsummary",) + ("-v",)*verbose) rep = TerminalReporter(modcol._config, file=linecomp.stringio) + registerdispatcher(rep) bus = modcol._config.bus - bus.subscribe(rep.processevent) # XXX bus.notify(testrunstart=event.TestrunStart()) try: for item in tsession.genitems([modcol]): @@ -708,17 +646,19 @@ """) #stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) + registerdispatcher(rep) indent = rep.indent - rep.processevent(event.CollectionStart(modcol)) + rep.config.bus.notify(collectionstart=event.CollectionStart(modcol)) linecomp.assert_contains_lines([ "" ]) item = modcol.join("test_func") - rep.processevent(event.ItemStart(item)) + rep.config.bus.notify(collectionstart=event.ItemStart(item)) linecomp.assert_contains_lines([ " ", ]) - rep.processevent(event.CollectionReport(modcol, [], excinfo=None)) + rep.config.bus.notify( + collectionreport=event.CollectionReport(modcol, [], excinfo=None)) assert rep.indent == indent def test_collectonly_skipped_module(self, tsession, linecomp): @@ -727,7 +667,7 @@ py.test.skip("nomod") """) rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) - tsession.session.bus.subscribe(rep.processevent) + registerdispatcher(rep) cols = list(tsession.genitems([modcol])) assert len(cols) == 0 linecomp.assert_contains_lines(""" @@ -740,7 +680,7 @@ raise ValueError(0) """) rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) - tsession.session.bus.subscribe(rep.processevent) + registerdispatcher(rep) cols = list(tsession.genitems([modcol])) assert len(cols) == 0 linecomp.assert_contains_lines(""" @@ -748,46 +688,6 @@ !!! ValueError: 0 !!! """) -class TestBaseReporter: - def test_dispatch_to_matching_method(self): - l = [] - class MyReporter(BaseReporter): - def rep_TestrunStart(self, ev): - l.append(ev) - rep = MyReporter() - ev = event.TestrunStart() - rep.processevent(ev) - assert len(l) == 1 - assert l[0] is ev - - def test_dispatch_to_default(self): - l = [] - class MyReporter(BaseReporter): - def rep(self, ev): - l.append(ev) - rep = MyReporter() - ev = event.NOP() - rep.processevent(ev) - assert len(l) == 1 - assert l[0] is ev - - def test_TestItemReport_one(self): - for outcome in 'passed skipped failed'.split(): - rep = BaseReporter() - ev = event.ItemTestReport(None) - setattr(ev, outcome, True) - rep.processevent(ev) - assert rep.status2event[outcome] - - def test_CollectionReport_events_are_counted(self): - for outcome in 'skipped failed'.split(): - rep = BaseReporter() - ev = event.CollectionReport(None, None) - setattr(ev, outcome, True) - rep.processevent(ev) - assert rep.status2event[outcome] - - def test_repr_python_version(): py.magic.patch(sys, 'version_info', (2, 5, 1, 'final', 0)) try: @@ -796,3 +696,11 @@ assert repr_pythonversion() == str(x) finally: py.magic.revert(sys, 'version_info') + +def registerdispatcher(rep): + def dispatch((name, event)): + meth = getattr(rep, "rep_" + event.__class__.__name__, None) + if meth is not None: + meth(event) + rep.config.bus._bus.subscribe(dispatch) + Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Wed Feb 18 09:07:21 2009 @@ -19,7 +19,7 @@ # a hook implemented called by the terminalreporter instance/plugin def pytest_terminal_summary(self, terminalreporter): tr = terminalreporter - xfailed = tr.status2event.get("xfailed") + xfailed = tr.stats.__dict__.get("xfailed") if xfailed: tr.write_sep("_", "EXPECTED XFAILURES") for event in xfailed: @@ -29,7 +29,7 @@ modpath = event.colitem.getmodpath(stopatmodule=False) tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message)) - xpassed = terminalreporter.status2event.get("xpassed") + xpassed = terminalreporter.stats.__dict__.get("xpassed") if xpassed: tr.write_sep("_", "UNEXPECTEDLY PASSING") for event in xpassed: Deleted: /py/branch/pytestplugin/py/test/pmanage.py ============================================================================== --- /py/branch/pytestplugin/py/test/pmanage.py Wed Feb 18 09:07:21 2009 +++ (empty file) @@ -1,122 +0,0 @@ -""" -manage bootstrap, lifecycle and interaction with plugins -""" -import py - -pytest_plugins = "pytest_plugins" - -DEBUG = False - -class PluginManager(object): - def __init__(self): - # XXX turn this into ordered dict? - self._plugins = {} - - def addpluginclass(self, pluginclass): - name = pluginclass.__name__.lower() - if name in self._plugins: - self.trace("plugin with name %r aready loaded" % name) - return - plugin = pluginclass() - if hasattr(self, '_configured') and hasattr(plugin, 'pytest_configure'): - plugin.pytest_configure(config=self._configured) - self.trace("instantiating and configuring: %s" %(pluginclass,)) - else: - self.trace("instantiating plugin: %s" %(pluginclass,)) - self._plugins[name] = plugin - return plugin - - def trace(self, msg): - if DEBUG: - print >>py.std.sys.stderr, msg - - def import_plugin(self, importspec): - if not isinstance(importspec, basestring): - return self.addpluginclass(importspec) - else: - modprefix = "pytest_" - if not importspec.startswith(modprefix): - importspec = modprefix + importspec - try: - mod = __import__(importspec) - except ImportError, e: - try: - mod = __import__("py.__.test.plugin.%s" %(importspec), None, None, '__doc__') - except ImportError: - raise ImportError(importspec) - clsname = importspec[len(modprefix):].capitalize() - pluginclass = getattr(mod, clsname) - result = self.addpluginclass(pluginclass) - self.consider_module(mod) - return result - - def getplugin(self, pname): - return self._plugins[pname.lower()] - - def consider_module(self, mod): - attr = getattr(mod, pytest_plugins, ()) - if not isinstance(attr, (list, tuple)): - attr = (attr,) - for spec in attr: - if spec: - self.import_plugin(spec) - - # - # API for interacting with registered and instantiated plugin objects - # - def setinitial(self, modules): - for module in modules: - self.consider_module(module) - envspec = py.std.os.environ.get("PYTEST_PLUGINS", None) - if envspec: - for spec in map(str.strip, envspec.split(",")): - if spec: - self.import_plugin(spec) - - def add_cmdlineoptions(self, config): - # XXX think about sorting/grouping of options from user-perspective - opts = [] - for name, options in self.listattr("pytest_cmdlineoptions"): - opts.extend(options) - config.addoptions("ungrouped options added by plugins", *opts) - - def callplugins(self, methname, **args): - """ call all plugins with the given method name and args. """ - for name, method in self.listattr(methname): - method(**args) - - def callfirst(self, methname, **args): - """ call all plugins with the given method name and args. """ - for name, method in self.listattr(methname): - res = method(**args) - if res is not None: - return res - - def getfirst(self, methname): - """ call all plugins with the given method name and args. """ - l = self.listattr(methname) - if l: - return l[0][1] - - def forward_event(self, event): - self.callplugins("pytest_event", event=event) - - def listattr(self, attrname): - l = [] - for name, plugin in self._plugins.items(): - try: - attrvalue = getattr(plugin, attrname) - l.append((name, attrvalue)) - except AttributeError: - continue - return l - - def configure(self, config): - #assert self.forward_event not in config.bus._subscribers - config.bus.subscribe(self.forward_event) - self.callplugins("pytest_configure", config=config) - self._configured = config - - def unconfigure(self, config): - self.callplugins("pytest_unconfigure", config=config) - config.bus.unsubscribe(self.forward_event) Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Wed Feb 18 09:07:21 2009 @@ -42,19 +42,21 @@ """ yield Items from iterating over the given colitems. """ while colitems: next = colitems.pop(0) + assert self.bus is next._config.bus + notify = self.bus.notify if isinstance(next, Item): remaining = self.filteritems([next]) if remaining: - self.bus.notify(itemstart=event.ItemStart(next)) + notify(itemstart=event.ItemStart(next)) yield next else: assert isinstance(next, Collector) - self.bus.notify(collectionstart=event.CollectionStart(next)) + notify(collectionstart=event.CollectionStart(next)) ev = basic_collect_report(next) if ev.passed: for x in self.genitems(ev.result, keywordexpr): yield x - self.bus.notify(collectionfinish=ev) + notify(collectionreport=ev) def filteritems(self, colitems): """ return items to process (some may be deselected)""" @@ -72,7 +74,7 @@ continue remaining.append(colitem) if deselected: - self.bus.notify(deselectedtest=event.Deselected(deselected, )) + self.bus.notify(deselected=event.Deselected(deselected, )) if self.config.option.keyword.endswith(":"): self._nomatch = True return remaining Deleted: /py/branch/pytestplugin/py/test/testing/test_pmanage.py ============================================================================== --- /py/branch/pytestplugin/py/test/testing/test_pmanage.py Wed Feb 18 09:07:21 2009 +++ (empty file) @@ -1,230 +0,0 @@ -import os, sys -import py -from py.__.test.pmanage import PluginManager -from py.__.test.event import NOP -from py.__.test.config import Config as pytestConfig - -class TestInitialization: - def setup_method(self, method): - self.tmpdir = py.test.ensuretemp("%s.%s.%s" % - (__name__, self.__class__.__name__, method.__name__)) - - def test_import_plugin_importname(self): - pm = PluginManager() - py.test.raises(ImportError, 'pm.import_plugin("x.y")') - py.test.raises(ImportError, 'pm.import_plugin("pytest_x.y")') - - sys.path.insert(0, str(self.tmpdir)) - try: - pluginname = "pytest_hello" - self.tmpdir.join(pluginname + ".py").write(py.code.Source(""" - class Hello: - pass - """)) - pm.import_plugin("hello") - pm.import_plugin("pytest_hello") - plugin = pm.getplugin("hello") - finally: - sys.path.remove(str(self.tmpdir)) - - @py.test.keywords(xfail=True) - def test_import_plugin_defaults_to_pytestplugin(self): - from py.__.test.defaultconftest import pytest_plugins - for name in pytest_plugins: - if isinstance(name, str): - break - sys.path.insert(0, str(self.tmpdir)) - try: - self.tmpdir.join(name + ".py").write(py.code.Source(""" - class Terminal: - pass - class AnotherPlugin: - pass - pytest_plugins = AnotherPlugin - """)) - pm = PluginManager() - pm.import_plugin(name) - plugin = pm.getplugin("terminal") - del sys.modules[name] - print pm._plugins - plugin = pm.getplugin("anotherplugin") - finally: - sys.path.remove(str(self.tmpdir)) - - def test_import_plugin_class(self): - pm = PluginManager() - class SomePlugin: - pass - pm.import_plugin(SomePlugin) - plugin = pm.getplugin("someplugin") - assert isinstance(plugin, SomePlugin) - i = len(pm._plugins) - pm.import_plugin(SomePlugin) - assert len(pm._plugins) == i - - def test_addpluginclass_post_configure(self): - pm = PluginManager() - l = [] - class SomePlugin: - def pytest_configure(self, config): - l.append(config) - conf = pytestConfig() - pm.configure(config=conf) - pm.import_plugin(SomePlugin) - assert len(l) == 1 - assert l[0] is conf - - def test_consider_module(self): - pm = PluginManager() - sys.path.insert(0, str(self.tmpdir)) - try: - self.tmpdir.join("pytest_plug1.py").write("class Plug1: pass") - self.tmpdir.join("pytest_plug2.py").write("class Plug2: pass") - mod = py.std.new.module("temp") - mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] - pm.consider_module(mod) - assert pm.getplugin("plug1").__class__.__name__ == "Plug1" - assert pm.getplugin("plug2").__class__.__name__ == "Plug2" - finally: - sys.path.remove(str(self.tmpdir)) - - - def test_addpluginclass(self): - pm = PluginManager() - class My: - pass - pm.addpluginclass(My) - assert len(pm._plugins) == 1 - pm.addpluginclass(My) - assert len(pm._plugins) == 1 - - def test_getplugin(self): - pm = PluginManager() - assert py.test.raises(LookupError, "pm.getplugin('_xxx')") - - class PluGin: pass - pm.addpluginclass(PluGin) - myplugin1 = pm.getplugin("plugin") - myplugin2 = pm.getplugin("Plugin") - assert myplugin1 is myplugin2 - -class TestPluginInteraction: - def test_getfirst(self): - pm = PluginManager() - class My1: - x = 1 - assert pm.getfirst("x") is None - pm.addpluginclass(My1) - assert pm.getfirst("x") == 1 - - def test_callplugins(self): - pm = PluginManager() - class My: - def method(self, arg): - pass - pm.addpluginclass(My) - py.test.raises(TypeError, 'pm.callplugins("method")') - py.test.raises(TypeError, 'pm.callplugins("method", 42)') - pm.callplugins("method", arg=42) - py.test.raises(TypeError, 'pm.callplugins("method", arg=42, s=13)') - - def test_callfirst(self): - pm = PluginManager() - class My1: - def method(self): - pass - class My2: - def method(self): - return True - class My3: - def method(self): - return None - assert pm.callfirst("method") is None - assert pm.callfirst("methodnotexists") is None - pm.addpluginclass(My1) - assert pm.callfirst("method") is None - pm.addpluginclass(My2) - assert pm.callfirst("method") == True - pm.addpluginclass(My3) - assert pm.callfirst("method") == True - - def test_listattr(self): - pm = PluginManager() - class My2: - x = 42 - pm.addpluginclass(My2) - assert not pm.listattr("hello") - assert pm.listattr("x") == [('my2', 42)] - - def test_eventbus_interaction(self): - pm = PluginManager() - l = [] - class My3: - def pytest_configure(self, config): - l.append("configure") - def pytest_unconfigure(self, config): - l.append("unconfigure") - pm.addpluginclass(My3) - config = pytestConfig() - pm.configure(config) - assert config.bus.issubscribed(pm.forward_event) - pm.unconfigure(config) - assert not config.bus.issubscribed(pm.forward_event) - assert l == ['configure', 'unconfigure'] - - def test_pytest_event(self): - pm = PluginManager() - l = [] - class My2: - def pytest_event(self, event): - l.append(event) - pm.addpluginclass(My2) - ev = NOP() - pm.forward_event(ev) - assert len(l) == 1 - assert l[0] is ev - - def test_addcmdlineoptions(self): - pm = PluginManager() - class My: - pytest_cmdlineoptions = [ - py.test.config.Option("--hello", dest="dest", default=242) - ] - pm.addpluginclass(My) - config = pytestConfig() - pm.add_cmdlineoptions(config) - opt = config._parser.get_option("--hello") - assert opt - assert opt.default == 242 - - def test_setinitial_env(self): - pm = PluginManager() - KEY = "PYTEST_PLUGINS" - old = os.environ.get(KEY, None) - try: - os.environ[KEY] = "test_setinitial" - py.test.raises(ImportError, "pm.setinitial([])") - finally: - if old is None: - del os.environ[KEY] - else: - os.environ[KEY] = old - - def test_conftest_specifies_plugin(self, fstester): - fstester.makepyfile( - conftest=""" - import py - class MyPlugin: - pytest_cmdlineoptions = [ - py.test.config.Option("--myplugin-option", dest="myplugin", - help="myplugin option", - ) - ] - pytest_plugins = MyPlugin - """ - ) - result = fstester.runpytest(fstester.tmpdir, '-h') - result.stdout.fnmatch_lines([ - "*--myplugin-option*", - ]) - From hpk at codespeak.net Wed Feb 18 09:59:07 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 09:59:07 +0100 (CET) Subject: [py-svn] r61992 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090218085907.CFFD7168529@codespeak.net> Author: hpk Date: Wed Feb 18 09:59:04 2009 New Revision: 61992 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Log: strike unneccessary stats class and event sink Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Wed Feb 18 09:59:04 2009 @@ -21,11 +21,11 @@ def pytest_terminal_summary(self, terminalreporter): if terminalreporter.config.option.pocoo_sendfailures: tr = terminalreporter - if tr.stats.failed and tr.config.option.tbstyle != "no": + if 'failed' in tr.stats and tr.config.option.tbstyle != "no": terminalreporter.write_sep("=", "Sending failures to %s" %(url.base,)) terminalreporter.write_line("xmlrpcurl: %s" %(url.xmlrpc,)) serverproxy = self.getproxy() - for ev in terminalreporter.stats.failed: + for ev in terminalreporter.stats.get('failed'): tw = py.io.TerminalWriter(stringio=True) ev.toterminal(tw) s = tw.stringio.getvalue() Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 18 09:59:04 2009 @@ -31,77 +31,6 @@ #print "calling", repmethod, event repmethod(event) -class OutcomeStats: - def __init__(self, bus): - self._reset() - bus.subscribe_methods(self) - - def _reset(self): - self.skipped = [] - self.passed = [] - self.failed = [] - self.deselected = [] - - def pyevent_itemtestreport(self, ev): - for name in 'skipped failed passed'.split(): - if getattr(ev, name): - l = getattr(self, name) - l.append(ev) - - def pyevent_collectionreport(self, ev): - if ev.skipped: - self.skipped.append(ev) - if ev.failed: - self.failed.append(ev) - - def pyevent_deselected(self, ev): - self.deselected.extend(ev.items) - - def pyevent_testrunstart(self, ev): - self._reset() - -class TestOutcomeStats: - def test_outcomestats(self): - names = 'passed skipped deselected failed'.split() - #for name in names: - # stats = OutcomeStats() - # assert getattr(stats, name) == [] - # bus = py.test._EventBus() - # bus.subscribe_methods(stats) - # rep = event.ItemTestReport(None) - # setattr(rep, name, True) - # bus.notify(itemtestreport=rep) - # assert getattr(stats, name) == [rep] - - bus = py.test._EventBus() - stats = OutcomeStats(bus) - class P: - passed = True - failed = skipped = False - bus.notify(itemtestreport=P) - assert stats.passed - assert stats.passed == [P] - bus.notify(itemtestreport=P) - assert stats.passed == [P, P] - - class F: - skipped = passed = False - failed = True - bus.notify(itemtestreport=F) - assert stats.failed == [F] - bus.notify(collectionreport=F) - assert stats.failed == [F, F] - class S: - skipped = True - passed = failed = False - bus.notify(itemtestreport=S) - assert stats.skipped == [S] - - class D: - items = [42] - bus.notify(deselected=D) - assert stats.deselected == [42] - def folded_skips(skipped): d = {} for event in skipped: @@ -113,11 +42,10 @@ l.append((len(events),) + key) return l - class TerminalReporter: def __init__(self, config, file=None): self.config = config - self.stats = OutcomeStats(config.bus) + self.stats = {} self.curdir = py.path.local() if file is None: file = py.std.sys.stdout @@ -219,13 +147,14 @@ def rep_RescheduleItems(self, ev): if self.config.option.debug: self.write_sep("!", "RESCHEDULING %s " %(ev.items,)) + + def rep_Deselected(self, ev): + self.stats.setdefault('deselected', []).append(ev) def rep_ItemTestReport(self, ev): fspath = ev.colitem.fspath cat, letter, word = self.getcategoryletterword(ev) - l = self.stats.__dict__.setdefault(cat, []) - if ev not in l: - l.append(ev) + self.stats.setdefault(cat, []).append(ev) if not self.config.option.verbose: self.write_fspath_result(fspath, letter) else: @@ -235,10 +164,14 @@ def rep_CollectionReport(self, ev): if not ev.passed: + #XXX allow plugins to override collection failures? + # cat, letter, word = self.getcategoryletterword(ev) if ev.failed: + self.stats.setdefault("failed", []).append(ev) msg = ev.longrepr.reprcrash.message self.write_fspath_result(ev.colitem.fspath, "F") elif ev.skipped: + self.stats.setdefault("skipped", []).append(ev) self.write_fspath_result(ev.colitem.fspath, "S") def rep_TestrunStart(self, ev): @@ -295,9 +228,9 @@ # def summary_failures(self): - if self.stats.failed and self.config.option.tbstyle != "no": + if 'failed' in self.stats and self.config.option.tbstyle != "no": self.write_sep("=", "FAILURES") - for ev in self.stats.failed: + for ev in self.stats['failed']: self.write_sep("_") ev.toterminal(self._tw) @@ -307,7 +240,7 @@ keys = "failed passed skipped deselected".split() parts = [] for key in keys: - val = getattr(self.stats, key) + val = self.stats.get(key, None) if val: parts.append("%d %s" %(len(val), key)) line = ", ".join(parts) @@ -315,17 +248,18 @@ self.write_sep("=", "%s in %.2f seconds" %(line, session_duration)) def summary_deselected(self): - if self.stats.deselected: + if 'deselected' in self.stats: self.write_sep("=", "%d tests deselected by %r" %( - len(self.stats.deselected), self.config.option.keyword), bold=True) + len(self.stats['deselected']), self.config.option.keyword), bold=True) def summary_skips(self): - if not self.stats.failed or self.config.option.showskipsummary: - fskips = folded_skips(self.stats.skipped) - if fskips: - self.write_sep("_", "skipped test summary") - for num, fspath, lineno, reason in fskips: - self._tw.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) + if 'skipped' in self.stats: + if 'failed' not in self.stats or self.config.option.showskipsummary: + fskips = folded_skips(self.stats['skipped']) + if fskips: + self.write_sep("_", "skipped test summary") + for num, fspath, lineno, reason in fskips: + self._tw.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) def summary_final_exc(self, excrepr): self.write_sep("!") Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Wed Feb 18 09:59:04 2009 @@ -19,7 +19,7 @@ # a hook implemented called by the terminalreporter instance/plugin def pytest_terminal_summary(self, terminalreporter): tr = terminalreporter - xfailed = tr.stats.__dict__.get("xfailed") + xfailed = tr.stats.get("xfailed") if xfailed: tr.write_sep("_", "EXPECTED XFAILURES") for event in xfailed: @@ -29,7 +29,7 @@ modpath = event.colitem.getmodpath(stopatmodule=False) tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message)) - xpassed = terminalreporter.stats.__dict__.get("xpassed") + xpassed = terminalreporter.stats.get("xpassed") if xpassed: tr.write_sep("_", "UNEXPECTEDLY PASSING") for event in xpassed: From hpk at codespeak.net Wed Feb 18 10:20:07 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 10:20:07 +0100 (CET) Subject: [py-svn] r61993 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090218092007.7071C16857E@codespeak.net> Author: hpk Date: Wed Feb 18 10:20:06 2009 New Revision: 61993 Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Log: porting keyword usage part 1. Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 18 10:20:06 2009 @@ -21,6 +21,7 @@ name = attr.split("_")[-1] assert hasattr(self.reporter._tw, name), name setattr(self.reporter._tw, name, getattr(config, attr)) + config.bus.subscribe_methods(self.reporter) def pytest_event(self, (name, event)): #print "processing event", event @@ -116,7 +117,7 @@ def pyevent_pluginregistered(self, plugin): self.write_line("PLUGIN registered: %s" %(plugin,)) - def rep_HostUp(self, ev): + def pyevent_hostup(self, ev): d = ev.platinfo.copy() d['hostid'] = ev.host.hostid d['version'] = repr_pythonversion(d['sys.version_info']) @@ -124,7 +125,7 @@ "%(sys.executable)s - Python %(version)s" % d) - def rep_HostDown(self, ev): + def pyevent_hostdown(self, ev): host = ev.host error = ev.error if error: @@ -327,7 +328,7 @@ item = tsession.getitem("def test_func(): pass") rep = TerminalReporter(item._config, linecomp.stringio) host = Host("localhost") - rep.rep_HostUp(makehostup(host)) + rep.pyevent_hostup(makehostup(host)) linecomp.assert_contains_lines([ "*%s %s %s - Python %s" %(host.hostid, sys.platform, sys.executable, repr_pythonversion(sys.version_info)) @@ -428,7 +429,7 @@ linecomp.assert_contains_lines([ "*HostGatewayReady*" ]) - rep.rep_HostDown(event.HostDown(host1, "myerror")) + rep.pyevent_hostdown(event.HostDown(host1, "myerror")) linecomp.assert_contains_lines([ "*HostDown*myerror*", ]) From hpk at codespeak.net Wed Feb 18 10:33:59 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 10:33:59 +0100 (CET) Subject: [py-svn] r61994 - in py/branch/pytestplugin/py/test: . plugin Message-ID: <20090218093359.8099516857E@codespeak.net> Author: hpk Date: Wed Feb 18 10:33:59 2009 New Revision: 61994 Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/session.py Log: port another event Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 18 10:33:59 2009 @@ -175,7 +175,7 @@ self.stats.setdefault("skipped", []).append(ev) self.write_fspath_result(ev.colitem.fspath, "S") - def rep_TestrunStart(self, ev): + def pyevent_testrunstart(self, ev): self.write_sep("=", "test session starts", bold=True) self._sessionstarttime = py.std.time.time() rev = py.__pkg__.getrev() @@ -638,4 +638,5 @@ if meth is not None: meth(event) rep.config.bus._bus.subscribe(dispatch) + rep.config.bus.subscribe_methods(rep) Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Wed Feb 18 10:33:59 2009 @@ -86,7 +86,7 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.bus.notify(sessionstarts=event.TestrunStart()) + self.bus.notify(testrunstart=event.TestrunStart()) # XXX the following is not used or neccessary for the DSession subclass self._failurelist = [] self.bus.subscribe(self._processfailures) From hpk at codespeak.net Wed Feb 18 10:51:57 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 10:51:57 +0100 (CET) Subject: [py-svn] r61995 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090218095157.0AF2D1684F9@codespeak.net> Author: hpk Date: Wed Feb 18 10:51:53 2009 New Revision: 61995 Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Log: port some collection events Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 18 10:51:53 2009 @@ -131,7 +131,7 @@ if error: self.write_line("HostDown %s: %s" %(host.hostid, error)) - def rep_ItemStart(self, ev): + def pyevent_itemstart(self, ev): if self.config.option.verbose: info = ev.item.repr_metainfo() line = info.verboseline(basedir=self.curdir) + " " @@ -163,7 +163,7 @@ line = info.verboseline(basedir=self.curdir) + " " self.write_ensure_prefix(line, word) - def rep_CollectionReport(self, ev): + def pyevent_collectionreport(self, ev): if not ev.passed: #XXX allow plugins to override collection failures? # cat, letter, word = self.getcategoryletterword(ev) @@ -289,14 +289,14 @@ def outindent(self, line): self.out.line(self.indent + str(line)) - def rep_CollectionStart(self, ev): + def pyevent_collectionstart(self, ev): self.outindent(ev.collector) self.indent += self.INDENT - def rep_ItemStart(self, event): + def pyevent_itemstart(self, event): self.outindent(event.item) - def rep_CollectionReport(self, ev): + def pyevent_collectionreport(self, ev): if not ev.passed: self.outindent("!!! %s !!!" % ev.longrepr.reprcrash.message) self.indent = self.indent[:-len(self.INDENT)] @@ -588,7 +588,7 @@ "" ]) item = modcol.join("test_func") - rep.config.bus.notify(collectionstart=event.ItemStart(item)) + rep.config.bus.notify(itemstart=event.ItemStart(item)) linecomp.assert_contains_lines([ " ", ]) From hpk at codespeak.net Wed Feb 18 10:57:55 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 10:57:55 +0100 (CET) Subject: [py-svn] r61996 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090218095755.DB24A1684F9@codespeak.net> Author: hpk Date: Wed Feb 18 10:57:55 2009 New Revision: 61996 Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Log: port a bunch more events to new-style Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 18 10:57:55 2009 @@ -106,11 +106,11 @@ else: return self._tw.markup("???", red=True) - def rep_InternalException(self, ev): + def pyevent_internalexception(self, ev): for line in str(ev.repr).split("\n"): self.write_line("InternalException: " + line) - def rep_HostGatewayReady(self, ev): + def pyevent_hostgatewayready(self, ev): if self.config.option.verbose: self.write_line("HostGatewayReady: %s" %(ev.host,)) @@ -145,14 +145,14 @@ fspath = ev.item.fspath self.write_fspath_result(fspath, "") - def rep_RescheduleItems(self, ev): + def pyevent_rescheduleitems(self, ev): if self.config.option.debug: self.write_sep("!", "RESCHEDULING %s " %(ev.items,)) - def rep_Deselected(self, ev): + def pyevent_deselected(self, ev): self.stats.setdefault('deselected', []).append(ev) - def rep_ItemTestReport(self, ev): + def pyevent_itemtestreport(self, ev): fspath = ev.colitem.fspath cat, letter, word = self.getcategoryletterword(ev) self.stats.setdefault(cat, []).append(ev) @@ -199,7 +199,7 @@ self.summary_deselected() self.summary_stats() - def rep_LooponfailingInfo(self, ev): + def pyevent_looponfailinginfo(self, ev): if ev.failreports: self.write_sep("#", "LOOPONFAILING", red=True) for report in ev.failreports: @@ -413,7 +413,7 @@ modcol = tsession.getmodulecol("def test_one(): pass") rep = TerminalReporter(modcol._config, file=linecomp.stringio) excinfo = py.test.raises(ValueError, "raise ValueError('hello')") - rep.rep_InternalException(event.InternalException(excinfo)) + rep.pyevent_internalexception(event.InternalException(excinfo)) linecomp.assert_contains_lines([ "InternalException: >*raise ValueError*" ]) @@ -425,7 +425,7 @@ """, configargs=("-v",)) host1 = Host("localhost") rep = TerminalReporter(modcol._config, file=linecomp.stringio) - rep.rep_HostGatewayReady(event.HostGatewayReady(host1, None)) + rep.pyevent_hostgatewayready(event.HostGatewayReady(host1, None)) linecomp.assert_contains_lines([ "*HostGatewayReady*" ]) @@ -454,7 +454,7 @@ """) rep = TerminalReporter(modcol._config, file=linecomp.stringio) reports = [basic_run_report(x) for x in modcol.collect()] - rep.rep_LooponfailingInfo(event.LooponfailingInfo(reports, [modcol._config.topdir])) + rep.pyevent_looponfailinginfo(event.LooponfailingInfo(reports, [modcol._config.topdir])) linecomp.assert_contains_lines([ "*test_looponfailingreport.py:2: assert 0", "*test_looponfailingreport.py:4: ValueError*", From hpk at codespeak.net Wed Feb 18 11:03:07 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 11:03:07 +0100 (CET) Subject: [py-svn] r61997 - in py/branch/pytestplugin/py/test: . plugin Message-ID: <20090218100307.926CC168512@codespeak.net> Author: hpk Date: Wed Feb 18 11:03:06 2009 New Revision: 61997 Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/session.py Log: yay, finally got rid of the old-style generic event catcher in favour of specifiy keyword based events Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 18 11:03:06 2009 @@ -23,15 +23,6 @@ setattr(self.reporter._tw, name, getattr(config, attr)) config.bus.subscribe_methods(self.reporter) - def pytest_event(self, (name, event)): - #print "processing event", event - #print "looking for", name - evname = event.__class__.__name__ - repmethod = getattr(self.reporter, "rep_%s" % evname, None) - if repmethod is not None: - #print "calling", repmethod, event - repmethod(event) - def folded_skips(skipped): d = {} for event in skipped: @@ -186,7 +177,7 @@ for i, testarg in py.builtin.enumerate(self.config.args): self.write_line("test object %d: %s" %(i+1, testarg)) - def rep_TestrunFinish(self, ev): + def pyevent_testrunfinish(self, ev): self._tw.line("") if ev.exitstatus in (0, 1, 2): self.summary_failures() @@ -301,7 +292,7 @@ self.outindent("!!! %s !!!" % ev.longrepr.reprcrash.message) self.indent = self.indent[:-len(self.INDENT)] - def rep_TestrunFinish(self, session): + def pyevent_testrunfinish(self, session): for ev in self._failed: ev.toterminal(self.out) @@ -633,10 +624,5 @@ py.magic.revert(sys, 'version_info') def registerdispatcher(rep): - def dispatch((name, event)): - meth = getattr(rep, "rep_" + event.__class__.__name__, None) - if meth is not None: - meth(event) - rep.config.bus._bus.subscribe(dispatch) rep.config.bus.subscribe_methods(rep) Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Wed Feb 18 11:03:06 2009 @@ -99,7 +99,7 @@ def sessionfinishes(self, exitstatus=0, excinfo=None): """ teardown any resources after a test run. """ - self.bus.notify(sessionfinishes=event.TestrunFinish(exitstatus=exitstatus, + self.bus.notify(testrunfinish=event.TestrunFinish(exitstatus=exitstatus, excinfo=excinfo)) self.bus.unsubscribe(self._processfailures) return self._failurelist From hpk at codespeak.net Wed Feb 18 11:16:13 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 11:16:13 +0100 (CET) Subject: [py-svn] r61999 - py/branch/pytestplugin/py/test Message-ID: <20090218101613.263E0168528@codespeak.net> Author: hpk Date: Wed Feb 18 11:16:12 2009 New Revision: 61999 Modified: py/branch/pytestplugin/py/test/session.py Log: getting rid of another generic event subscription Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Wed Feb 18 11:16:12 2009 @@ -11,8 +11,8 @@ # used for genitems() from py.__.test.outcome import Exit -Item = (py.test.collect.Item, py.test.collect.Item) -Collector = (py.test.collect.Collector, py.test.collect.Collector) +Item = py.test.collect.Item +Collector = py.test.collect.Collector from runner import basic_collect_report from py.__.test.dsession.hostmanage import makehostup @@ -24,6 +24,8 @@ def __init__(self, config): self.config = config self.bus = config.bus # shortcut + self.bus.subscribe_methods(self) + self._testsfailed = False self._nomatch = False def fixoptions(self): @@ -87,22 +89,18 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ self.bus.notify(testrunstart=event.TestrunStart()) - # XXX the following is not used or neccessary for the DSession subclass - self._failurelist = [] - self.bus.subscribe(self._processfailures) - - def _processfailures(self, ev): - if isinstance(ev, event.BaseReport) and ev.failed: - self._failurelist.append(ev) - if self.config.option.exitfirst: - self.shouldstop = True + + def pyevent_itemtestreport(self, rep): + if rep.failed: + self._testsfailed = True + if self.config.option.exitfirst: + self.shouldstop = True + pyevent_collectionreport = pyevent_itemtestreport def sessionfinishes(self, exitstatus=0, excinfo=None): """ teardown any resources after a test run. """ self.bus.notify(testrunfinish=event.TestrunFinish(exitstatus=exitstatus, excinfo=excinfo)) - self.bus.unsubscribe(self._processfailures) - return self._failurelist def getinitialitems(self, colitems): if colitems is None: @@ -131,7 +129,7 @@ except: self.bus.notify(internalerror=event.InternalException()) exitstatus = outcome.EXIT_INTERNALERROR - if self._failurelist and exitstatus == 0: + if exitstatus == 0 and self._testsfailed: exitstatus = outcome.EXIT_TESTSFAILED self.sessionfinishes(exitstatus=exitstatus, excinfo=captured_excinfo) return exitstatus From hpk at codespeak.net Wed Feb 18 12:10:09 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 12:10:09 +0100 (CET) Subject: [py-svn] r62001 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090218111009.64AAF168536@codespeak.net> Author: hpk Date: Wed Feb 18 12:10:07 2009 New Revision: 62001 Modified: py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_unittest.py py/branch/pytestplugin/py/test/testing/suptest.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py py/branch/pytestplugin/py/test/testing/test_session.py Log: * avoid double-registry of plugins * rename "parse_and_run" helper to "inline_run" Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Wed Feb 18 12:10:07 2009 @@ -32,7 +32,7 @@ def import_plugin(self, spec): if isinstance(spec, str): modname, clsname = canonical_names(spec) - if clsname not in self._plugins: + if clsname.lower() not in self._plugins: mod = importplugin(modname) plugin = registerplugin(self.pm.register, mod, clsname) self._plugins[clsname.lower()] = plugin @@ -88,6 +88,7 @@ # XXX old code to automatically load classes # def canonical_names(importspec): + importspec = importspec.lower() modprefix = "pytest_" if not importspec.startswith(modprefix): importspec = modprefix + importspec Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Wed Feb 18 12:10:07 2009 @@ -58,7 +58,7 @@ old = Pocoo.getproxy Pocoo.getproxy = MockProxy try: - result = fstester.parse_and_run(testpath, "--pocoo-sendfailures") + result = fstester.inline_run(testpath, "--pocoo-sendfailures") finally: Pocoo.getproxy = old assert len(l) == 1 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 18 12:10:07 2009 @@ -45,7 +45,7 @@ if not impname in self._plugins: self._plugins.append(impname) - def parse_and_run(self, *args): + def inline_run(self, *args): config = self.parseconfig(*args) config.pluginmanager.configure(config) session = config.initsession() Modified: py/branch/pytestplugin/py/test/plugin/pytest_unittest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_unittest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_unittest.py Wed Feb 18 12:10:07 2009 @@ -83,7 +83,7 @@ def test_failing(self): self.assertEquals('foo', 'bar') """) - sorter = fstester.parse_and_run(testpath) + sorter = fstester.inline_run(testpath) assert sorter.getreport("testpassing").passed assert sorter.getreport("test_failing").failed @@ -97,7 +97,7 @@ def test_setUp(self): self.assertEquals(1, self.foo) """) - sorter = fstester.parse_and_run(testpath) + sorter = fstester.inline_run(testpath) rep = sorter.getreport("test_setUp") assert rep.passed @@ -115,7 +115,7 @@ def test_check(self): self.assertEquals(MyTestCase.l, [None]) """) - sorter = fstester.parse_and_run(testpath) + sorter = fstester.inline_run(testpath) passed, skipped, failed = sorter.countoutcomes() assert passed + skipped + failed == 2 assert failed == 0, failed Modified: py/branch/pytestplugin/py/test/testing/suptest.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/suptest.py (original) +++ py/branch/pytestplugin/py/test/testing/suptest.py Wed Feb 18 12:10:07 2009 @@ -174,7 +174,7 @@ return runner(item, **runnerargs) class InlineSession(InlineCollection): - def parse_and_run(self, *args): + def inline_run(self, *args): config = self.parseconfig(*args) config.pluginmanager.configure(config) session = config.initsession() Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Wed Feb 18 12:10:07 2009 @@ -59,12 +59,10 @@ assert len(pseudoconfig.opts) == 1 def test_canonical_names(): - impname, clsname = canonical_names("xyz") - assert impname == "pytest_xyz" - assert clsname == "Xyz" - impname, clsname = canonical_names("pytest_xyz") - assert impname == "pytest_xyz" - assert clsname == "Xyz" + for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz': + impname, clsname = canonical_names(name) + assert impname == "pytest_xyz" + assert clsname == "Xyz" def test_registerplugin(): l = [] @@ -77,7 +75,23 @@ assert importplugin("py") == py py.test.raises(ImportError, "importplugin('laksjd.qwe')") mod = importplugin("pytest_terminal") - assert mod == py.__.test.plugin.pytest_terminal + assert mod is py.__.test.plugin.pytest_terminal + +class TestPluginManager: + def test_importplugin(self, fstester): + mod = py.std.new.module("x") + mod.pytest_plugins = "pytest_a" + aplugin = fstester.makepyfile(pytest_a="""class A: pass""") + pm = PytestPluginManager() + l = [] + pm.pm._bus.subscribe(pluginregistered=l.append) + #syspath.prepend(aplugin.dirpath()) + py.std.sys.path.insert(0, str(aplugin.dirpath())) + pm.consider_module(mod) + assert pm.getplugin('a').__class__.__name__ == "A" + assert len(l) == 1 + pm.consider_module(mod) + assert len(l) == 1 import os, sys Modified: py/branch/pytestplugin/py/test/testing/test_session.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_session.py (original) +++ py/branch/pytestplugin/py/test/testing/test_session.py Wed Feb 18 12:10:07 2009 @@ -14,7 +14,7 @@ assert 42 == 43 """) def check(keyword, name): - sorter = self.parse_and_run("-s", "-k", keyword, file_test) + sorter = self.inline_run("-s", "-k", keyword, file_test) passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 1 assert failed[0].colitem.name == name @@ -56,7 +56,7 @@ def test_two(): assert 1 def test_three(): assert 1 """) - sorter = self.parse_and_run("-k", "test_two:", threepass) + sorter = self.inline_run("-k", "test_two:", threepass) passed, skipped, failed = sorter.listoutcomes() assert len(passed) == 2 assert not failed From hpk at codespeak.net Wed Feb 18 13:08:35 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 13:08:35 +0100 (CET) Subject: [py-svn] r62002 - in py/branch/pytestplugin/py/test: . dsession/testing looponfail looponfail/testing plugin testing Message-ID: <20090218120835.E49BE16855C@codespeak.net> Author: hpk Date: Wed Feb 18 13:08:33 2009 New Revision: 62002 Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py py/branch/pytestplugin/py/test/event.py py/branch/pytestplugin/py/test/looponfail/remote.py py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py py/branch/pytestplugin/py/test/looponfail/testing/test_util.py py/branch/pytestplugin/py/test/looponfail/util.py py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/testing/suptest.py py/branch/pytestplugin/py/test/testing/test_collect.py py/branch/pytestplugin/py/test/testing/test_event.py Log: getting rid of pytest-event speciality of subscribing with "value" only callbacks. now all anonymous callbacks receive (name, obj) pairs as passed to notify(name=obj) Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py Wed Feb 18 13:08:33 2009 @@ -24,6 +24,53 @@ while queue.qsize(): print queue.get() +class EventRecorder(object): + def __init__(self, bus): + self.events = [] + self.bus = bus + bus.subscribe(self.record) + + def record(self, (name, event)): + self.events.append((name, event)) + + def getfirstnamed(self, name): + for evname, event in self.events: + if evname == name: + return event + + def getfailures(self): + l = [] + for evname, ev in self.events: + if evname in ('itemtestreport', 'collectionreport'): + if ev.failed: + l.append(ev) + return l + + def clear(self): + self.events[:] = [] + + def unsubscribe(self): + self.bus.unsubscribe(self.record) + +def test_eventrecorder(): + bus = event.EventBus() + recorder = EventRecorder(bus) + bus.notify(anonymous=event.NOP()) + assert recorder.events + assert not recorder.getfailures() + rep = event.ItemTestReport(None, None) + rep.failed = True + bus.notify(itemtestreport=rep) + failures = recorder.getfailures() + assert failures == [rep] + recorder.clear() + assert not recorder.events + assert not recorder.getfailures() + recorder.unsubscribe() + bus.notify(itemtestreport=rep) + assert not recorder.events + assert not recorder.getfailures() + class TestDSession(InlineCollection): def test_fixoptions(self): config = self.parseconfig("--exec=xxx") @@ -175,13 +222,12 @@ host = session.item2host[item1] ev = event.HostDown(host, None) session.queueput(hostdown=ev) - - events = [] ; session.bus.subscribe(events.append) + evrec = EventRecorder(session.bus) loopstate = LoopState([]) session.loop_once(loopstate) assert loopstate.colitems == [item2] # do not reschedule crash item - testrep = [x for x in events if isinstance(x, event.ItemTestReport)][0] + testrep = evrec.getfirstnamed("itemtestreport") assert testrep.failed assert testrep.colitem == item1 assert str(testrep.longrepr).find("crashed") != -1 @@ -205,10 +251,10 @@ session = DSession(item._config) ev = event.NOP() - events = [] ; session.bus.subscribe(events.append) + evrec = EventRecorder(session.bus) session.queueput(anonymous=ev) session.loop_once(LoopState([])) - assert events[0] == ev + assert evrec.getfirstnamed('anonymous') == ev def runthrough(self, item): session = DSession(item._config) @@ -275,15 +321,14 @@ session.addhost(host) loopstate = LoopState([]) loopstate.shuttingdown = True - l = [] - session.bus.subscribe(l.append) + evrec = EventRecorder(session.bus) session.queueput(itemtestreport=run(item)) session.loop_once(loopstate) - assert not l + assert not evrec.events ev = event.HostDown(host) session.queueput(hostdown=ev) session.loop_once(loopstate) - assert l == [ev] + assert evrec.getfirstnamed('hostdown') == ev def test_filteritems(self): modcol = self.getmodulecol(""" @@ -298,11 +343,11 @@ dsel = session.filteritems([modcol]) assert dsel == [modcol] items = modcol.collect() - events = [] ; session.bus.subscribe(events.append) + evrec = EventRecorder(session.bus) remaining = session.filteritems(items) assert remaining == [] - ev = events[-1] + evname, ev = evrec.events[-1] assert isinstance(ev, event.Deselected) assert ev.items == items @@ -310,7 +355,7 @@ remaining = session.filteritems(items) assert remaining == [items[0]] - ev = events[-1] + evname, ev = evrec.events[-1] assert isinstance(ev, event.Deselected) assert ev.items == [items[1]] Modified: py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py Wed Feb 18 13:08:33 2009 @@ -16,7 +16,7 @@ events = [] while 1: try: - ev = queue.get(timeout=timeout) + evname, ev = queue.get(timeout=timeout) except py.std.Queue.Empty: print "seen events", events raise IOError("did not see %r events" % (eventtype)) Modified: py/branch/pytestplugin/py/test/event.py ============================================================================== --- py/branch/pytestplugin/py/test/event.py (original) +++ py/branch/pytestplugin/py/test/event.py Wed Feb 18 13:08:33 2009 @@ -7,13 +7,6 @@ from py.__.test.outcome import Skipped import py.__.misc.event - -class CallWithValue(object): - def __init__(self, callback): - self.callback = callback - def __call__(self, (name, obj)): - return self.callback(obj) - class EventBus(object): """ Bus for distributing events. """ def __init__(self, bus=None): @@ -24,9 +17,7 @@ def issubscribed(self, callback=None, **kw): if callback is not None: - for x in self._bus._getsubscribers(''): - if isinstance(x, CallWithValue) and x.callback == callback: - return True + kw[''] = callback for name, value in kw.items(): if value in self._bus._getsubscribers(name): return True @@ -38,7 +29,6 @@ d = kw.copy() if callback is not None: d[''] = callback - callback = CallWithValue(callback) self._memo.append(d) #print "appending", d self._bus.subscribe(callback, **kw) @@ -62,12 +52,7 @@ def unsubscribe(self, callback=None, **kw): """ unsubscribe given callback from bus events. """ - for x in self._bus._getsubscribers(''): - if isinstance(x, CallWithValue) and x.callback == callback: - self._bus.unsubscribe(x, **kw) - break - else: - self._bus.unsubscribe(**kw) + return self._bus.unsubscribe(callback, **kw) def notify(self, **kw): self._bus.notify(**kw) Modified: py/branch/pytestplugin/py/test/looponfail/remote.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/remote.py (original) +++ py/branch/pytestplugin/py/test/looponfail/remote.py Wed Feb 18 13:08:33 2009 @@ -154,7 +154,7 @@ #session.bus.subscribe(sendevent) failreports = [] - def recordfailures(ev): + def recordfailures((evname, ev)): if isinstance(ev, event.BaseReport): if ev.failed: failreports.append(ev) Modified: py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py (original) +++ py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py Wed Feb 18 13:08:33 2009 @@ -1,26 +1,10 @@ import py from py.__.test.testing import suptest from py.__.test.looponfail.remote import LooponfailingSession, LoopState, RemoteControl -from py.__.test import event -def getevent(l, evtype): - result = getevents(l, evtype) - if not result: - raise ValueError("event %r not found in %r" %(evtype, l)) - return result[0] - -def getevents(l, evtype): - result = [] - for ev in l: - if isinstance(ev, evtype): - result.append(ev) - return result - - class TestRemoteControl(suptest.InlineCollection): def test_nofailures(self): item = self.getitem("def test_func(): pass\n") - events = [] control = RemoteControl(item._config) control.setup() failures = control.runsession() Modified: py/branch/pytestplugin/py/test/looponfail/testing/test_util.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/testing/test_util.py (original) +++ py/branch/pytestplugin/py/test/looponfail/testing/test_util.py Wed Feb 18 13:08:33 2009 @@ -1,5 +1,5 @@ import py -from py.__.test.looponfail.util import StatRecorder, EventRecorder +from py.__.test.looponfail.util import StatRecorder from py.__.test import event def test_filechange(): @@ -62,30 +62,4 @@ tmp.ensure("newfile.py") reply.get(timeout=0.5) wp.shutdown() - -def test_eventrecorder(): - bus = event.EventBus() - recorder = EventRecorder(bus) - bus.notify(anonymous=event.NOP()) - assert recorder.events - assert not recorder.getfailures() - rep = event.ItemTestReport(None, None) - rep.failed = True - bus.notify(itemtestreport=rep) - failures = recorder.getfailures() - assert failures == [rep] - recorder.clear() - assert not recorder.events - assert not recorder.getfailures() - recorder.unsubscribe() - bus.notify(itemtestreport=rep) - assert not recorder.events - assert not recorder.getfailures() - - - - - - - - + Modified: py/branch/pytestplugin/py/test/looponfail/util.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/util.py (original) +++ py/branch/pytestplugin/py/test/looponfail/util.py Wed Feb 18 13:08:33 2009 @@ -52,19 +52,3 @@ self.statcache = newstat return changed - -class EventRecorder(object): - def __init__(self, bus): - self.events = [] - self.bus = bus - self.bus.subscribe(self.events.append) - - def getfailures(self): - return [ev for ev in self.events - if isinstance(ev, event.BaseReport) and \ - ev.failed] - def clear(self): - self.events[:] = [] - - def unsubscribe(self): - self.bus.unsubscribe(self.events.append) Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Wed Feb 18 13:08:33 2009 @@ -16,7 +16,7 @@ self.eventlogfile.close() del self.eventlogfile - def pytest_event(self, event): + def pytest_event(self, (name, event)): if hasattr(self, 'eventlogfile'): f = self.eventlogfile print >>f, event Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 18 13:08:33 2009 @@ -143,7 +143,7 @@ self.config = config self.session = session self.cls2events = d = {} - def app(event): + def app((name, event)): print "[event]", event for cls in py.std.inspect.getmro(event.__class__): if cls is not object: Modified: py/branch/pytestplugin/py/test/testing/suptest.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/suptest.py (original) +++ py/branch/pytestplugin/py/test/testing/suptest.py Wed Feb 18 13:08:33 2009 @@ -13,12 +13,12 @@ from py.__.test import event from fnmatch import fnmatch -def eventappender(session): +def eventappender(bus): l = [] - def app(ev): + def app((name, ev)): print ev l.append(ev) - session.bus.subscribe(app) + bus.subscribe(app) return l def initsorter_from_cmdline(args=None): @@ -52,7 +52,7 @@ self.config = config self.session = session self.cls2events = d = {} - def app(event): + def app((name, event)): print "[event]", event for cls in py.std.inspect.getmro(event.__class__): if cls is not object: Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Wed Feb 18 13:08:33 2009 @@ -454,7 +454,7 @@ print "using tempdir", tmp config = py.test.config._reparse([tmp]) session = config.initsession() - l = suptest.eventappender(session) + l = suptest.eventappender(config.bus) items = list(session.genitems(getcolitems(config))) return items, l Modified: py/branch/pytestplugin/py/test/testing/test_event.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_event.py (original) +++ py/branch/pytestplugin/py/test/testing/test_event.py Wed Feb 18 13:08:33 2009 @@ -6,26 +6,23 @@ class TestEventBus: - def test_simple(self): + def test_anonymous_subscription(self): bus = EventBus() l = [] bus.subscribe(l.append) bus.notify(x=1) - bus.notify(x=2) - bus.notify(x=3) - assert l == [1,2,3] + assert len(l) == 1 + assert l[0] == ('x', 1) def test_multi_sub(self): bus = EventBus() - l1 = [] - l2 = [] + l1, l2 = [], [] bus.subscribe(l1.append) bus.subscribe(l2.append) bus.notify(x=1) bus.notify(x=2) - bus.notify(x=3) - assert l1 == [1,2,3] - assert l2 == [1,2,3] + assert l1 == l2 + assert l1 == [('x', 1), ('x', 2)] def test_remove(self): bus = EventBus() @@ -34,7 +31,7 @@ bus.notify(x=1) bus.unsubscribe(l.append) bus.notify(x=22) - assert l == [1] + assert l == [('x', 1)] def test_subscribe_methods(self): bus = EventBus() From hpk at codespeak.net Wed Feb 18 13:50:08 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 13:50:08 +0100 (CET) Subject: [py-svn] r62003 - in py/branch/pytestplugin/py/test: dsession/testing plugin Message-ID: <20090218125008.C0DBE16840F@codespeak.net> Author: hpk Date: Wed Feb 18 13:50:06 2009 New Revision: 62003 Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_unittest.py Log: unify and test EventRecording Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py Wed Feb 18 13:50:06 2009 @@ -24,53 +24,6 @@ while queue.qsize(): print queue.get() -class EventRecorder(object): - def __init__(self, bus): - self.events = [] - self.bus = bus - bus.subscribe(self.record) - - def record(self, (name, event)): - self.events.append((name, event)) - - def getfirstnamed(self, name): - for evname, event in self.events: - if evname == name: - return event - - def getfailures(self): - l = [] - for evname, ev in self.events: - if evname in ('itemtestreport', 'collectionreport'): - if ev.failed: - l.append(ev) - return l - - def clear(self): - self.events[:] = [] - - def unsubscribe(self): - self.bus.unsubscribe(self.record) - -def test_eventrecorder(): - bus = event.EventBus() - recorder = EventRecorder(bus) - bus.notify(anonymous=event.NOP()) - assert recorder.events - assert not recorder.getfailures() - rep = event.ItemTestReport(None, None) - rep.failed = True - bus.notify(itemtestreport=rep) - failures = recorder.getfailures() - assert failures == [rep] - recorder.clear() - assert not recorder.events - assert not recorder.getfailures() - recorder.unsubscribe() - bus.notify(itemtestreport=rep) - assert not recorder.events - assert not recorder.getfailures() - class TestDSession(InlineCollection): def test_fixoptions(self): config = self.parseconfig("--exec=xxx") @@ -199,7 +152,7 @@ dumpqueue(session.queue) assert loopstate.exitstatus == outcome.EXIT_NOHOSTS - def test_hostdown_causes_reschedule_pending(self): + def test_hostdown_causes_reschedule_pending(self, EventRecorder): modcol = self.getmodulecol(""" def test_crash(): assert 0 @@ -246,7 +199,7 @@ session.loop_once(loopstate) assert len(session.host2pending) == 1 - def test_event_propagation(self): + def test_event_propagation(self, EventRecorder): item = self.getitem("def test_func(): pass") session = DSession(item._config) @@ -314,7 +267,7 @@ assert loopstate.testsfailed assert loopstate.shuttingdown - def test_shuttingdown_filters_events(self): + def test_shuttingdown_filters_events(self, EventRecorder): item = self.getitem("def test_func(): pass") session = DSession(item._config) host = Host("localhost") @@ -330,7 +283,7 @@ session.loop_once(loopstate) assert evrec.getfirstnamed('hostdown') == ev - def test_filteritems(self): + def test_filteritems(self, EventRecorder): modcol = self.getmodulecol(""" def test_fail(): assert 0 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 18 13:50:06 2009 @@ -49,7 +49,7 @@ config = self.parseconfig(*args) config.pluginmanager.configure(config) session = config.initsession() - sorter = EventSorter(config, session) + sorter = EventRecorder(config.bus) session.main() config.pluginmanager.unconfigure(config) return sorter @@ -138,26 +138,52 @@ return self.config.getfsnode(path) -class EventSorter(object): - def __init__(self, config, session=None): - self.config = config - self.session = session - self.cls2events = d = {} - def app((name, event)): - print "[event]", event - for cls in py.std.inspect.getmro(event.__class__): - if cls is not object: - d.setdefault(cls, []).append(event) - session.bus.subscribe(app) +class EventRecorder(object): + def __init__(self, bus, debug=True): + self.events = [] + self.bus = bus + bus.subscribe(self.record) + self.debug = debug + + def record(self, (name, event)): + if self.debug: + print "[event] %s: %s" %(name, event) + self.events.append((name, event)) def get(self, cls): - return self.cls2events.get(cls, []) + l = [] + for name, value in self.events: + if isinstance(value, cls): + l.append(value) + return l + + def getnamed(self, *names): + l = [] + for evname, event in self.events: + if evname in names: + l.append(event) + return l + + def getfirstnamed(self, name): + for evname, event in self.events: + if evname == name: + return event + + def getfailures(self, names='itemtestreport collectionreport'): + l = [] + for ev in self.getnamed(*names.split()): + if ev.failed: + l.append(ev) + return l + + def getfailedcollections(self): + return self.getfailures('collectionreport') def listoutcomes(self): passed = [] skipped = [] failed = [] - for ev in self.get(event.ItemTestReport): + for ev in self.getnamed('itemtestreport'): # , 'collectionreport'): if ev.passed: passed.append(ev) elif ev.skipped: @@ -175,13 +201,6 @@ assert skipped == len(realskipped) assert failed == len(realfailed) - def getfailedcollections(self): - l = [] - for ev in self.get(event.CollectionReport): - if ev.failed: - l.append(ev) - return l - def getreport(self, inamepart): """ return a testreport whose dotted import path matches """ __tracebackhide__ = True @@ -197,6 +216,55 @@ inamepart, l)) return l[0] + def clear(self): + self.events[:] = [] + + def unsubscribe(self): + self.bus.unsubscribe(self.record) + +def test_eventrecorder(): + bus = event.EventBus() + recorder = EventRecorder(bus) + bus.notify(anonymous=event.NOP()) + assert recorder.events + assert not recorder.getfailures() + rep = event.ItemTestReport(None, None) + rep.passed = False + rep.failed = True + bus.notify(itemtestreport=rep) + failures = recorder.getfailures() + assert failures == [rep] + failures = recorder.get(event.ItemTestReport) + assert failures == [rep] + failures = recorder.getnamed("itemtestreport") + assert failures == [rep] + + rep = event.ItemTestReport(None, None) + rep.passed = False + rep.skipped = True + bus.notify(itemtestreport=rep) + + rep = event.CollectionReport(None, None) + rep.passed = False + rep.failed = True + bus.notify(itemtestreport=rep) + + passed, skipped, failed = recorder.listoutcomes() + assert not passed and skipped and failed + + numpassed, numskipped, numfailed = recorder.countoutcomes() + assert numpassed == 0 + assert numskipped == 1 + assert numfailed == 2 + + recorder.clear() + assert not recorder.events + assert not recorder.getfailures() + recorder.unsubscribe() + bus.notify(itemtestreport=rep) + assert not recorder.events + assert not recorder.getfailures() + class LineComp: def __init__(self): self.stringio = py.std.StringIO.StringIO() @@ -266,3 +334,5 @@ elif argname == "fstester": fstester = FSTester(pyfuncitem) return fstester, None + elif argname == "EventRecorder": + return EventRecorder, None # or do some finalization Modified: py/branch/pytestplugin/py/test/plugin/pytest_unittest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_unittest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_unittest.py Wed Feb 18 13:50:06 2009 @@ -117,7 +117,8 @@ """) sorter = fstester.inline_run(testpath) passed, skipped, failed = sorter.countoutcomes() - assert passed + skipped + failed == 2 + print "COUNTS", passed, skipped, failed assert failed == 0, failed assert passed == 2 + assert passed + skipped + failed == 2 From hpk at codespeak.net Wed Feb 18 13:59:12 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 13:59:12 +0100 (CET) Subject: [py-svn] r62004 - py/branch/pytestplugin/py/test/dsession/testing Message-ID: <20090218125912.39465168574@codespeak.net> Author: hpk Date: Wed Feb 18 13:59:10 2009 New Revision: 62004 Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py Log: remove usage of suptest.py file Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py Wed Feb 18 13:59:10 2009 @@ -1,4 +1,3 @@ -from py.__.test.testing.suptest import InlineCollection from py.__.test.dsession.dsession import DSession, LoopState from py.__.test.dsession.hostmanage import Host, makehostup from py.__.test.runner import basic_collect_report @@ -24,17 +23,17 @@ while queue.qsize(): print queue.get() -class TestDSession(InlineCollection): - def test_fixoptions(self): - config = self.parseconfig("--exec=xxx") +class TestDSession: + def test_fixoptions(self, fstester): + config = fstester.parseconfig("--exec=xxx") config.initsession().fixoptions() assert config.option.numprocesses == 1 - config = self.parseconfig("--exec=xxx", '-n3') + config = fstester.parseconfig("--exec=xxx", '-n3') config.initsession().fixoptions() assert config.option.numprocesses == 3 - def test_add_remove_host(self): - item = self.getitem("def test_func(): pass") + def test_add_remove_host(self, tsession): + item = tsession.getitem("def test_func(): pass") rep = run(item) session = DSession(item._config) host = Host("localhost") @@ -48,8 +47,8 @@ assert item not in session.item2host py.test.raises(Exception, "session.removehost(host)") - def test_senditems_removeitems(self): - item = self.getitem("def test_func(): pass") + def test_senditems_removeitems(self, tsession): + item = tsession.getitem("def test_func(): pass") rep = run(item) session = DSession(item._config) host = Host("localhost") @@ -62,8 +61,8 @@ assert not session.host2pending[host] assert not session.item2host - def test_triggertesting_collect(self): - modcol = self.getmodulecol(""" + def test_triggertesting_collect(self, tsession): + modcol = tsession.getmodulecol(""" def test_func(): pass """) @@ -71,11 +70,12 @@ session.triggertesting([modcol]) kw = session.queue.get(block=False) name, rep = kw.items()[0] + assert name == 'collectionreport' assert isinstance(rep, event.CollectionReport) assert len(rep.result) == 1 - def test_triggertesting_item(self): - item = self.getitem("def test_func(): pass") + def test_triggertesting_item(self, tsession): + item = tsession.getitem("def test_func(): pass") session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() @@ -95,24 +95,24 @@ assert isinstance(ev, event.RescheduleItems) assert ev.items == [item] - def test_keyboardinterrupt(self): - item = self.getitem("def test_func(): pass") + def test_keyboardinterrupt(self, tsession): + item = tsession.getitem("def test_func(): pass") session = DSession(item._config) def raise_(timeout=None): raise KeyboardInterrupt() session.queue.get = raise_ exitstatus = session.loop([]) assert exitstatus == outcome.EXIT_INTERRUPTED - def test_internalerror(self): - item = self.getitem("def test_func(): pass") + def test_internalerror(self, tsession): + item = tsession.getitem("def test_func(): pass") session = DSession(item._config) def raise_(): raise ValueError() session.queue.get = raise_ exitstatus = session.loop([]) assert exitstatus == outcome.EXIT_INTERNALERROR - def test_rescheduleevent(self): - item = self.getitem("def test_func(): pass") + def test_rescheduleevent(self, tsession): + item = tsession.getitem("def test_func(): pass") session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() @@ -134,8 +134,8 @@ assert loopstate.shuttingdown assert not loopstate.testsfailed - def test_no_hosts_remaining_for_tests(self): - item = self.getitem("def test_func(): pass") + def test_no_hosts_remaining_for_tests(self, tsession): + item = tsession.getitem("def test_func(): pass") # setup a session with one host session = DSession(item._config) host1 = Host("localhost") @@ -152,8 +152,8 @@ dumpqueue(session.queue) assert loopstate.exitstatus == outcome.EXIT_NOHOSTS - def test_hostdown_causes_reschedule_pending(self, EventRecorder): - modcol = self.getmodulecol(""" + def test_hostdown_causes_reschedule_pending(self, tsession, EventRecorder): + modcol = tsession.getmodulecol(""" def test_crash(): assert 0 def test_fail(): @@ -186,8 +186,8 @@ assert str(testrep.longrepr).find("crashed") != -1 assert str(testrep.longrepr).find(host.hostname) != -1 - def test_hostup_adds_to_available(self): - item = self.getitem("def test_func(): pass") + def test_hostup_adds_to_available(self, tsession): + item = tsession.getitem("def test_func(): pass") # setup a session with two hosts session = DSession(item._config) host1 = Host("localhost") @@ -199,8 +199,8 @@ session.loop_once(loopstate) assert len(session.host2pending) == 1 - def test_event_propagation(self, EventRecorder): - item = self.getitem("def test_func(): pass") + def test_event_propagation(self, tsession, EventRecorder): + item = tsession.getitem("def test_func(): pass") session = DSession(item._config) ev = event.NOP() @@ -229,18 +229,18 @@ dumpqueue(session.queue) return session, loopstate.exitstatus - def test_exit_completed_tests_ok(self): - item = self.getitem("def test_func(): pass") + def test_exit_completed_tests_ok(self, tsession): + item = tsession.getitem("def test_func(): pass") session, exitstatus = self.runthrough(item) assert exitstatus == outcome.EXIT_OK - def test_exit_completed_tests_fail(self): - item = self.getitem("def test_func(): 0/0") + def test_exit_completed_tests_fail(self, tsession): + item = tsession.getitem("def test_func(): 0/0") session, exitstatus = self.runthrough(item) assert exitstatus == outcome.EXIT_TESTSFAILED - def test_exit_on_first_failing(self): - modcol = self.getmodulecol(""" + def test_exit_on_first_failing(self, tsession): + modcol = tsession.getmodulecol(""" def test_fail(): assert 0 def test_pass(): @@ -267,8 +267,8 @@ assert loopstate.testsfailed assert loopstate.shuttingdown - def test_shuttingdown_filters_events(self, EventRecorder): - item = self.getitem("def test_func(): pass") + def test_shuttingdown_filters_events(self, tsession, EventRecorder): + item = tsession.getitem("def test_func(): pass") session = DSession(item._config) host = Host("localhost") session.addhost(host) @@ -283,8 +283,8 @@ session.loop_once(loopstate) assert evrec.getfirstnamed('hostdown') == ev - def test_filteritems(self, EventRecorder): - modcol = self.getmodulecol(""" + def test_filteritems(self, tsession, EventRecorder): + modcol = tsession.getmodulecol(""" def test_fail(): assert 0 def test_pass(): @@ -312,8 +312,8 @@ assert isinstance(ev, event.Deselected) assert ev.items == [items[1]] - def test_hostdown_shutdown_after_completion(self): - item = self.getitem("def test_func(): pass") + def test_hostdown_shutdown_after_completion(self, tsession): + item = tsession.getitem("def test_func(): pass") session = DSession(item._config) host = Host("localhost") @@ -330,8 +330,8 @@ session.loop_once(loopstate) assert loopstate.exitstatus == 0 - def test_nopending_but_collection_remains(self): - modcol = self.getmodulecol(""" + def test_nopending_but_collection_remains(self, tsession): + modcol = tsession.getmodulecol(""" def test_fail(): assert 0 def test_pass(): From hpk at codespeak.net Wed Feb 18 14:17:13 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 14:17:13 +0100 (CET) Subject: [py-svn] r62005 - py/branch/pytestplugin/py/test/testing Message-ID: <20090218131713.2DADE1684EE@codespeak.net> Author: hpk Date: Wed Feb 18 14:17:12 2009 New Revision: 62005 Modified: py/branch/pytestplugin/py/test/testing/test_collect.py Log: another file less that uses suptest Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Wed Feb 18 14:17:12 2009 @@ -1,7 +1,6 @@ from __future__ import generators import py from py.__.test import event, outcome -from py.__.test.testing import suptest from py.__.test.conftesthandle import Conftest from py.__.test.collect import SetupState from test_config import getcolitems @@ -284,9 +283,9 @@ assert cmp(f2, f1) > 0 -class TestCustomConftests(suptest.InlineCollection): - def test_extra_python_files_and_functions(self): - self.makepyfile(conftest=""" +class TestCustomConftests: + def test_extra_python_files_and_functions(self, fstester): + fstester.makepyfile(conftest=""" import py class MyFunction(py.test.collect.Function): pass @@ -305,7 +304,7 @@ class Instance(myfuncmixin, py.test.collect.Instance): pass """) - checkfile = self.makepyfile(check_file=""" + checkfile = fstester.makepyfile(check_file=""" def check_func(): assert 42 == 42 class CustomTestClass: @@ -313,14 +312,14 @@ assert 23 == 23 """) # check that directory collects "check_" files - config = self.parseconfig() + config = fstester.parseconfig() col = config.getfsnode(checkfile.dirpath()) colitems = col.collect() assert len(colitems) == 1 assert isinstance(colitems[0], py.test.collect.Module) # check that module collects "check_" functions and methods - config = self.parseconfig(checkfile) + config = fstester.parseconfig(checkfile) col = config.getfsnode(checkfile) assert isinstance(col, py.test.collect.Module) colitems = col.collect() @@ -334,8 +333,8 @@ assert len(colitems) == 1 assert colitems[0].name == "check_method" - def test_non_python_files(self): - self.makepyfile(conftest=""" + def test_non_python_files(self, fstester): + fstester.makepyfile(conftest=""" import py class CustomItem(py.test.collect.Item): def run(self): @@ -345,10 +344,10 @@ if fspath.ext == ".xxx": return CustomItem(fspath.basename, parent=self) """) - checkfile = self._makefile(ext="xxx", hello="world") - self.makepyfile(x="") - self.maketxtfile(x="") - config = self.parseconfig() + checkfile = fstester._makefile(ext="xxx", args=(), kwargs={'hello': "world"}) + fstester.makepyfile(x="") + fstester.maketxtfile(x="") + config = fstester.parseconfig() dircol = config.getfsnode(checkfile.dirpath()) colitems = dircol.collect() assert len(colitems) == 1 @@ -365,9 +364,8 @@ py.test.raises(py.error.ENOENT, col.collect) -def test_order_of_execution_generator_same_codeline(tmpdir): - o = tmpdir.ensure('genorder1', dir=1) - o.join("test_order1.py").write(py.code.Source(""" +def test_order_of_execution_generator_same_codeline(fstester, tmpdir): + o = fstester.makepyfile(""" def test_generative_order_of_execution(): test_list = [] expected_list = range(6) @@ -383,15 +381,14 @@ for i in expected_list: yield list_append, i yield assert_order_of_execution - """)) - sorter = suptest.events_from_cmdline([o]) + """) + sorter = fstester.inline_run(o) passed, skipped, failed = sorter.countoutcomes() assert passed == 7 assert not skipped and not failed -def test_order_of_execution_generator_different_codeline(tmpdir): - o = tmpdir.ensure('genorder2', dir=2) - o.join("test_genorder2.py").write(py.code.Source(""" +def test_order_of_execution_generator_different_codeline(fstester): + o = fstester.makepyfile(""" def test_generative_tests_different_codeline(): test_list = [] expected_list = range(3) @@ -414,8 +411,8 @@ yield list_append_1 yield list_append_2 yield assert_order_of_execution - """)) - sorter = suptest.events_from_cmdline([o]) + """) + sorter = fstester.inline_run(o) # .events_from_cmdline([o]) passed, skipped, failed = sorter.countoutcomes() assert passed == 4 assert not skipped and not failed @@ -448,17 +445,17 @@ def setup_method(self, method): self.tmp = self.classtemp.mkdir(method.func_name) - def _genitems(self, tmp=None): + def _genitems(self, EventRecorder, tmp=None): if tmp is None: tmp = self.tmp print "using tempdir", tmp config = py.test.config._reparse([tmp]) session = config.initsession() - l = suptest.eventappender(config.bus) + rec = EventRecorder(config.bus) items = list(session.genitems(getcolitems(config))) - return items, l + return items, rec - def test_check_collect_hashes(self): + def test_check_collect_hashes(self, EventRecorder): one = self.tmp.ensure("test_check_collect_hashes.py") one.write(py.code.Source(""" def test_1(): @@ -468,7 +465,7 @@ pass """)) one.copy(self.tmp.join("test_check_collect_hashes_2.py")) - items, events = self._genitems() + items, events = self._genitems(EventRecorder) assert len(items) == 4 for numi, i in enumerate(items): for numj, j in enumerate(items): @@ -476,23 +473,21 @@ assert hash(i) != hash(j) assert i != j - def test_root_conftest_syntax_error(self): + def test_root_conftest_syntax_error(self, EventRecorder): # do we want to unify behaviour with # test_subdir_conftest_error? self.tmp.ensure("conftest.py").write("raise SyntaxError\n") - py.test.raises(SyntaxError, self._genitems) + py.test.raises(SyntaxError, self._genitems, EventRecorder) - def test_subdir_conftest_error(self): + def test_subdir_conftest_error(self, EventRecorder): self.tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") - items, events = self._genitems() - failures = [x for x in events - if isinstance(x, event.CollectionReport) - and x.failed] - assert len(failures) == 1 - ev = failures[0] + items, events = self._genitems(EventRecorder) + collectionfailures = events.getfailedcollections() + assert len(collectionfailures) == 1 + ev = collectionfailures[0] assert ev.longrepr.reprcrash.message.startswith("SyntaxError") - def test_example_items1(self): + def test_example_items1(self, EventRecorder): self.tmp.ensure("test_example.py").write(py.code.Source(''' def testone(): pass @@ -504,7 +499,7 @@ class TestY(TestX): pass ''')) - items, events = self._genitems() + items, events = self._genitems(EventRecorder) assert len(items) == 3 assert items[0].name == 'testone' assert items[1].name == 'testmethod_one' @@ -519,7 +514,7 @@ assert s == "test_example_items1.test_example.testone" print s - def test_collect_doctest_files_with_test_prefix(self): + def test_collect_doctest_files_with_test_prefix(self, EventRecorder): self.tmp.ensure("whatever.txt") checkfile = self.tmp.ensure("test_something.txt") checkfile.write(py.code.Source(""" @@ -530,7 +525,7 @@ """)) for x in (self.tmp, checkfile): #print "checking that %s returns custom items" % (x,) - items, events = self._genitems(x) + items, events = self._genitems(tmp=x, EventRecorder=EventRecorder) assert len(items) == 1 assert isinstance(items[0], DoctestFileContent) @@ -562,9 +557,9 @@ py.test.raises(ValueError, "col3._totrail()") -class TestCollectorReprs(suptest.InlineCollection): - def test_repr_metainfo_basic_item(self): - modcol = self.getmodulecol("") +class TestCollectorReprs: + def test_repr_metainfo_basic_item(self, tsession): + modcol = tsession.getmodulecol("") Item = py.test.collect.Item item = Item("virtual", parent=modcol) info = item.repr_metainfo() @@ -572,15 +567,15 @@ assert not info.lineno assert info.modpath == "Item" - def test_repr_metainfo_func(self): - item = self.getitem("def test_func(): pass") + def test_repr_metainfo_func(self, tsession): + item = tsession.getitem("def test_func(): pass") info = item.repr_metainfo() assert info.fspath == item.fspath assert info.lineno == 0 assert info.modpath == "test_func" - def test_repr_metainfo_class(self): - modcol = self.getmodulecol(""" + def test_repr_metainfo_class(self, tsession): + modcol = tsession.getmodulecol(""" # lineno 0 class TestClass: def test_hello(self): pass @@ -591,8 +586,8 @@ assert info.lineno == 1 assert info.modpath == "TestClass" - def test_repr_metainfo_generator(self): - modcol = self.getmodulecol(""" + def test_repr_metainfo_generator(self, tsession): + modcol = tsession.getmodulecol(""" # lineno 0 def test_gen(): def check(x): @@ -634,9 +629,8 @@ def p2_to_p1(self, obj): return self.p1.loads(self.p2.dumps(obj)) -class TestPickling(suptest.InlineCollection): +class TestPickling: def setup_method(self, method): - super(TestPickling, self).setup_method(method) pt = PickleTransport() self.p1_to_p2 = pt.p1_to_p2 self.p2_to_p1 = pt.p2_to_p1 @@ -653,8 +647,8 @@ config_back = self.p2_to_p1(p2config) assert config_back is config1 - def test_pickle_module(self): - modcol1 = self.getmodulecol("def test_one(): pass") + def test_pickle_module(self, tsession): + modcol1 = tsession.getmodulecol("def test_one(): pass") self.unifyconfig(modcol1._config) modcol2a = self.p1_to_p2(modcol1) @@ -664,8 +658,8 @@ modcol1_back = self.p2_to_p1(modcol2a) assert modcol1_back - def test_pickle_func(self): - modcol1 = self.getmodulecol("def test_one(): pass") + def test_pickle_func(self, tsession): + modcol1 = tsession.getmodulecol("def test_one(): pass") self.unifyconfig(modcol1._config) item = modcol1.collect_by_name("test_one") item2a = self.p1_to_p2(item) From hpk at codespeak.net Wed Feb 18 14:43:37 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 14:43:37 +0100 (CET) Subject: [py-svn] r62006 - py/branch/pytestplugin/py/test/testing Message-ID: <20090218134337.DAEDB168501@codespeak.net> Author: hpk Date: Wed Feb 18 14:43:34 2009 New Revision: 62006 Modified: py/branch/pytestplugin/py/test/testing/test_collect.py Log: file by file, part 723 Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Wed Feb 18 14:43:34 2009 @@ -3,10 +3,10 @@ from py.__.test import event, outcome from py.__.test.conftesthandle import Conftest from py.__.test.collect import SetupState -from test_config import getcolitems from py.__.test.pycollect import DoctestFileContent + class DummyConfig: def __init__(self): self._conftest = Conftest() @@ -439,33 +439,16 @@ assert not f1 != f1_b class Testgenitems: - def setup_class(cls): - cls.classtemp = py.test.ensuretemp(cls.__name__) - - def setup_method(self, method): - self.tmp = self.classtemp.mkdir(method.func_name) - - def _genitems(self, EventRecorder, tmp=None): - if tmp is None: - tmp = self.tmp - print "using tempdir", tmp - config = py.test.config._reparse([tmp]) - session = config.initsession() - rec = EventRecorder(config.bus) - items = list(session.genitems(getcolitems(config))) - return items, rec - - def test_check_collect_hashes(self, EventRecorder): - one = self.tmp.ensure("test_check_collect_hashes.py") - one.write(py.code.Source(""" + def test_check_collect_hashes(self, fstester): + p = fstester.makepyfile(""" def test_1(): pass def test_2(): pass - """)) - one.copy(self.tmp.join("test_check_collect_hashes_2.py")) - items, events = self._genitems(EventRecorder) + """) + p.copy(p.dirpath(p.purebasename + "2" + ".py")) + items, events = fstester.inline_genitems(p.dirpath()) assert len(items) == 4 for numi, i in enumerate(items): for numj, j in enumerate(items): @@ -473,22 +456,23 @@ assert hash(i) != hash(j) assert i != j - def test_root_conftest_syntax_error(self, EventRecorder): + def test_root_conftest_syntax_error(self, fstester): # do we want to unify behaviour with # test_subdir_conftest_error? - self.tmp.ensure("conftest.py").write("raise SyntaxError\n") - py.test.raises(SyntaxError, self._genitems, EventRecorder) + p = fstester.makepyfile(conftest="raise SyntaxError\n") + py.test.raises(SyntaxError, fstester.inline_genitems, p.dirpath()) - def test_subdir_conftest_error(self, EventRecorder): - self.tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") - items, events = self._genitems(EventRecorder) + def test_subdir_conftest_error(self, fstester): + tmp = fstester.tmpdir + tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") + items, events = fstester.inline_genitems(tmp) collectionfailures = events.getfailedcollections() assert len(collectionfailures) == 1 ev = collectionfailures[0] assert ev.longrepr.reprcrash.message.startswith("SyntaxError") - def test_example_items1(self, EventRecorder): - self.tmp.ensure("test_example.py").write(py.code.Source(''' + def test_example_items1(self, fstester): + p = fstester.makepyfile(''' def testone(): pass @@ -498,8 +482,8 @@ class TestY(TestX): pass - ''')) - items, events = self._genitems(EventRecorder) + ''') + items, events = fstester.inline_genitems(p) assert len(items) == 3 assert items[0].name == 'testone' assert items[1].name == 'testmethod_one' @@ -511,21 +495,20 @@ assert items[2].getmodpath() == "TestY.testmethod_one" s = items[0].getmodpath(stopatmodule=False) - assert s == "test_example_items1.test_example.testone" + assert s.endswith("test_example_items1.testone") print s - def test_collect_doctest_files_with_test_prefix(self, EventRecorder): - self.tmp.ensure("whatever.txt") - checkfile = self.tmp.ensure("test_something.txt") - checkfile.write(py.code.Source(""" + def test_collect_doctest_files_with_test_prefix(self, fstester): + fstester.maketxtfile(whatever="") + checkfile = fstester.maketxtfile(test_something=""" alskdjalsdk >>> i = 5 >>> i-1 4 - """)) - for x in (self.tmp, checkfile): + """) + for x in (fstester.tmpdir, checkfile): #print "checking that %s returns custom items" % (x,) - items, events = self._genitems(tmp=x, EventRecorder=EventRecorder) + items, events = fstester.inline_genitems(x) assert len(items) == 1 assert isinstance(items[0], DoctestFileContent) From hpk at codespeak.net Wed Feb 18 15:43:53 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 15:43:53 +0100 (CET) Subject: [py-svn] r62007 - in py/branch/pytestplugin/py/test: plugin testing Message-ID: <20090218144353.1A61D168449@codespeak.net> Author: hpk Date: Wed Feb 18 15:43:50 2009 New Revision: 62007 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/testing/test_collect.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_deprecated_api.py Log: removing usage of suptest some more Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 18 15:43:50 2009 @@ -34,17 +34,31 @@ ret = p return ret + def makefile(self, ext, *args, **kwargs): + return self._makefile(ext, args, kwargs) + def makepyfile(self, *args, **kwargs): return self._makefile('.py', args, kwargs) def maketxtfile(self, *args, **kwargs): return self._makefile('.txt', args, kwargs) + def mkdir(self, name): + return self.tmpdir.mkdir(name) + def ensureplugin(self, impname): assert isinstance(impname, str) if not impname in self._plugins: self._plugins.append(impname) + def inline_genitems(self, *args): + config = self.parseconfig(*args) + session = config.initsession() + rec = EventRecorder(config.bus) + colitems = [config.getfsnode(arg) for arg in config.args] + items = list(session.genitems(colitems)) + return items, rec + def inline_run(self, *args): config = self.parseconfig(*args) config.pluginmanager.configure(config) @@ -139,7 +153,7 @@ class EventRecorder(object): - def __init__(self, bus, debug=True): + def __init__(self, bus, debug=False): # True): self.events = [] self.bus = bus bus.subscribe(self.record) Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Wed Feb 18 15:43:50 2009 @@ -65,7 +65,7 @@ assert modcol.name == x.name def test_listnames_getitembynames_custom(self, fstester): - hello = fstester._makefile(".xxx", (), {'hello':"world"}) + hello = fstester.makefile(".xxx", hello="world") fstester.makepyfile(conftest=""" import py class CustomFile(py.test.collect.File): @@ -344,7 +344,7 @@ if fspath.ext == ".xxx": return CustomItem(fspath.basename, parent=self) """) - checkfile = fstester._makefile(ext="xxx", args=(), kwargs={'hello': "world"}) + checkfile = fstester.makefile(ext="xxx", hello="world") fstester.makepyfile(x="") fstester.maketxtfile(x="") config = fstester.parseconfig() Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Wed Feb 18 15:43:50 2009 @@ -2,14 +2,10 @@ import py from py.__.test.config import gettopdir -from py.__.test.testing import suptest def getcolitems(config): return [config.getfsnode(arg) for arg in config.args] -def test_default_bus(): - assert py.test.config.bus._bus is py.event - def test_tmpdir(): d1 = py.test.ensuretemp('hello') d2 = py.test.ensuretemp('hello') @@ -166,21 +162,10 @@ assert config.option.gdest == 11 assert option.gdest == 11 -class TestSessionAndOptions(suptest.FileCreation): - def exampletestfile(self): - return self.makepyfile(file_test=""" - def test_one(): - assert 42 == 43 - - class TestClass(object): - def test_method_one(self): - assert 42 == 43 - """) - - def test_tracedir_tracer(self): - tracedir = self.tmpdir.join("tracedir") - config = py.test.config._reparse([self.tmpdir, - '--tracedir=%s' % tracedir]) +class TestSessionAndOptions: + def test_tracedir_tracer(self, fstester): + tracedir = fstester.mkdir("tracedir") + config = fstester.parseconfig("--tracedir=%s" % tracedir) assert config.gettracedir() == tracedir trace = config.maketrace("trace1.log") # flush=True by default @@ -193,57 +178,57 @@ assert lines[1].find("A") != -1 trace.close() - def test_trace_null(self): - config = py.test.config._reparse([self.tmpdir]) + def test_trace_null(self, fstester): + config = fstester.parseconfig(fstester.tmpdir) assert config.gettracedir() is None trace = config.maketrace("hello", flush=True) trace("hello", "world") trace.close() - def test_implied_dsession(self): + def test_implied_dsession(self, fstester): for x in 'startserver runbrowser rest'.split(): - config = py.test.config._reparse([self.tmpdir, '--dist', '--%s' % x]) + config = fstester.parseconfig(fstester.tmpdir, '--dist', '--%s' % x) assert config._getsessionname() == 'DSession' - def test_implied_different_sessions(self): - config = py.test.config._reparse([self.tmpdir]) + def test_implied_different_sessions(self, tmpdir): + config = py.test.config._reparse([tmpdir]) assert config._getsessionname() == 'Session' - config = py.test.config._reparse([self.tmpdir, '--dist']) + config = py.test.config._reparse([tmpdir, '--dist']) assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([self.tmpdir, '-n3']) + config = py.test.config._reparse([tmpdir, '-n3']) assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([self.tmpdir, '--looponfailing']) + config = py.test.config._reparse([tmpdir, '--looponfailing']) assert config._getsessionname() == 'LooponfailingSession' - config = py.test.config._reparse([self.tmpdir, '--exec=x']) + config = py.test.config._reparse([tmpdir, '--exec=x']) assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([self.tmpdir, '--dist', '--exec=x']) + config = py.test.config._reparse([tmpdir, '--dist', '--exec=x']) assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([self.tmpdir, '-f', + config = py.test.config._reparse([tmpdir, '-f', '--dist', '--exec=x']) assert config._getsessionname() == 'LooponfailingSession' - config = py.test.config._reparse([self.tmpdir, '-f', '-n3', + config = py.test.config._reparse([tmpdir, '-f', '-n3', '--dist', '--exec=x', '--collectonly']) assert config._getsessionname() == 'Session' - def test_sessionname_lookup_custom(self): - self.tmpdir.join("conftest.py").write(py.code.Source(""" + def test_sessionname_lookup_custom(self, fstester): + fstester.makepyfile(conftest=""" from py.__.test.session import Session class MySession(Session): pass - """)) - config = py.test.config._reparse(["--session=MySession", self.tmpdir]) + """) + config = fstester.parseconfig("--session=MySession", fstester.tmpdir) session = config.initsession() assert session.__class__.__name__ == 'MySession' - def test_initsession(self): - config = py.test.config._reparse([self.tmpdir]) + def test_initsession(self, tmpdir): + config = py.test.config._reparse([tmpdir]) session = config.initsession() assert session.config is config - def test_boxed_option_default(self): - self.tmpdir.join("conftest.py").write("dist_hosts=[]") - tmpdir = self.tmpdir.ensure("subdir", dir=1) + def test_boxed_option_default(self, fstester): + fstester.makepyfile(conftest="dist_hosts=[]") + tmpdir = fstester.tmpdir.ensure("subdir", dir=1) config = py.test.config._reparse([tmpdir]) config.initsession() assert not config.option.boxed @@ -251,9 +236,9 @@ config.initsession() assert not config.option.boxed - def test_boxed_option_from_conftest(self): - self.tmpdir.join("conftest.py").write("dist_hosts=[]") - tmpdir = self.tmpdir.ensure("subdir", dir=1) + def test_boxed_option_from_conftest(self, fstester): + fstester.makepyfile(conftest="dist_hosts=[]") + tmpdir = fstester.tmpdir.ensure("subdir", dir=1) tmpdir.join("conftest.py").write(py.code.Source(""" dist_hosts = [] dist_boxed = True @@ -262,18 +247,14 @@ config.initsession() assert config.option.boxed - def test_boxed_option_from_conftest(self): - tmpdir = self.tmpdir - tmpdir.join("conftest.py").write(py.code.Source(""" - dist_boxed = False - """)) - config = py.test.config._reparse([tmpdir, '--box']) + def test_boxed_option_from_conftest(self, fstester): + fstester.makepyfile(conftest="dist_boxed=False") + config = py.test.config._reparse([fstester.tmpdir, '--box']) assert config.option.boxed config.initsession() assert config.option.boxed - def test_getvalue_pathlist(self): - tmpdir = self.tmpdir + def test_getvalue_pathlist(self, tmpdir): somepath = tmpdir.join("x", "y", "z") p = tmpdir.join("conftest.py") p.write("pathlist = ['.', %r]" % str(somepath)) @@ -289,10 +270,10 @@ pl = config.getvalue_pathlist('mypathlist') assert pl == [py.path.local()] - def test_config_iocapturing(self): - config = py.test.config._reparse([self.tmpdir]) + def test_config_iocapturing(self, fstester): + config = fstester.parseconfig(fstester.tmpdir) assert config.getvalue("conf_iocapture") - tmpdir = self.tmpdir.ensure("sub-with-conftest", dir=1) + tmpdir = fstester.tmpdir.ensure("sub-with-conftest", dir=1) tmpdir.join("conftest.py").write(py.code.Source(""" conf_iocapture = "no" """)) @@ -314,8 +295,7 @@ def test_conflict_options(self): def check_conflict_option(opts): print "testing if options conflict:", " ".join(opts) - path = self.exampletestfile() - config = py.test.config._reparse(opts + [path]) + config = py.test.config._reparse(opts) py.test.raises((ValueError, SystemExit), """ config.initsession() """) @@ -329,48 +309,30 @@ opts = spec.split() yield check_conflict_option, opts - def test_implied_options(self): - def check_implied_option(opts, expr): - path = self.exampletestfile() - config = py.test.config._reparse(opts + [path]) - session = config.initsession() - assert eval(expr, session.config.option.__dict__) - - implied_options = { - '-v': 'verbose', - '-l': 'showlocals', - #'--runbrowser': 'startserver and runbrowser', XXX starts browser - } - for key, expr in implied_options.items(): - yield check_implied_option, [key], expr - - def test_default_session_options(self): + def test_default_session_options(self, fstester): def runfiletest(opts): - sorter = suptest.events_from_cmdline(opts) + sorter = fstester.inline_run(*opts) passed, skipped, failed = sorter.countoutcomes() assert failed == 2 assert skipped == passed == 0 - path = self.exampletestfile() + path = fstester.makepyfile(""" + def test_f1(): assert 0 + def test_f2(): assert 0 + """) + for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], ['--tb=long'], ['--fulltrace'], ['--nomagic'], ['--traceconfig'], ['-v'], ['-v', '-v']): - yield runfiletest, opts + [path] + runfiletest(opts + [path]) - def test_is_not_boxed_by_default(self): - path = self.exampletestfile() - config = py.test.config._reparse([path]) + def test_is_not_boxed_by_default(self, fstester): + config = py.test.config._reparse([fstester.tmpdir]) assert not config.option.boxed -class TestConfigColitems(suptest.FileCreation): - def setup_class(cls): - cls.tmproot = py.test.ensuretemp(cls.__name__) - - def setup_method(self, method): - self.tmpdir = self.tmproot.mkdir(method.__name__) - - def test_getcolitems_onedir(self): - config = py.test.config._reparse([self.tmpdir]) +class TestConfigColitems: + def test_getcolitems_onedir(self, tmpdir): + config = py.test.config._reparse([tmpdir]) colitems = getcolitems(config) assert len(colitems) == 1 col = colitems[0] @@ -378,39 +340,39 @@ for col in col.listchain(): assert col._config is config - def test_getcolitems_twodirs(self): - config = py.test.config._reparse([self.tmpdir, self.tmpdir]) + def test_getcolitems_twodirs(self, tmpdir): + config = py.test.config._reparse([tmpdir, tmpdir]) colitems = getcolitems(config) assert len(colitems) == 2 col1, col2 = colitems assert col1.name == col2.name assert col1.parent == col2.parent - def test_getcolitems_curdir_and_subdir(self): - a = self.tmpdir.ensure("a", dir=1) - config = py.test.config._reparse([self.tmpdir, a]) + def test_getcolitems_curdir_and_subdir(self, tmpdir): + a = tmpdir.ensure("a", dir=1) + config = py.test.config._reparse([tmpdir, a]) colitems = getcolitems(config) assert len(colitems) == 2 col1, col2 = colitems - assert col1.name == self.tmpdir.basename + assert col1.name == tmpdir.basename assert col2.name == 'a' for col in colitems: for subcol in col.listchain(): assert col._config is config - def test__getcol_global_file(self): - x = self.tmpdir.ensure("x.py") + def test__getcol_global_file(self, tmpdir): + x = tmpdir.ensure("x.py") config = py.test.config._reparse([x]) col = config.getfsnode(x) assert isinstance(col, py.test.collect.Module) assert col.name == 'x.py' - assert col.parent.name == self.tmpdir.basename + assert col.parent.name == tmpdir.basename assert col.parent.parent is None for col in col.listchain(): assert col._config is config - def test__getcol_global_dir(self): - x = self.tmpdir.ensure("a", dir=1) + def test__getcol_global_dir(self, tmpdir): + x = tmpdir.ensure("a", dir=1) config = py.test.config._reparse([x]) col = config.getfsnode(x) assert isinstance(col, py.test.collect.Directory) @@ -419,9 +381,9 @@ assert col.parent is None assert col._config is config - def test__getcol_pkgfile(self): - x = self.tmpdir.ensure("x.py") - self.tmpdir.ensure("__init__.py") + def test__getcol_pkgfile(self, tmpdir): + x = tmpdir.ensure("x.py") + tmpdir.ensure("__init__.py") config = py.test.config._reparse([x]) col = config.getfsnode(x) assert isinstance(col, py.test.collect.Module) @@ -431,9 +393,9 @@ for col in col.listchain(): assert col._config is config - def test_config_picklability(self): + def test_config_picklability(self, tmpdir): import cPickle - config = py.test.config._reparse([self.tmpdir]) + config = py.test.config._reparse([tmpdir]) s = cPickle.dumps(config) newconfig = cPickle.loads(s) assert not hasattr(newconfig, "topdir") @@ -442,11 +404,11 @@ newconfig._initafterpickle(config.topdir) assert newconfig.topdir == config.topdir assert newconfig._initialized - assert newconfig.args == [self.tmpdir] + assert newconfig.args == [tmpdir] - def test_config_and_collector_pickling_missing_initafter(self): + def test_config_and_collector_pickling_missing_initafter(self, tmpdir): from cPickle import Pickler, Unpickler - config = py.test.config._reparse([self.tmpdir]) + config = py.test.config._reparse([tmpdir]) col = config.getfsnode(config.topdir) io = py.std.cStringIO.StringIO() pickler = Pickler(io) @@ -458,10 +420,10 @@ # we don't call _initafterpickle ... so py.test.raises(ValueError, "unpickler.load()") - def test_config_and_collector_pickling(self): + def test_config_and_collector_pickling(self, tmpdir): from cPickle import Pickler, Unpickler - dir1 = self.tmpdir.ensure("somedir", dir=1) - config = py.test.config._reparse([self.tmpdir]) + dir1 = tmpdir.ensure("somedir", dir=1) + config = py.test.config._reparse([tmpdir]) col = config.getfsnode(config.topdir) col1 = col.join(dir1.basename) assert col1.parent is col @@ -474,7 +436,7 @@ io.seek(0) unpickler = Unpickler(io) newconfig = unpickler.load() - topdir = self.tmpdir.ensure("newtopdir", dir=1) + topdir = tmpdir.ensure("newtopdir", dir=1) newconfig._initafterpickle(topdir) topdir.ensure("somedir", dir=1) newcol = unpickler.load() @@ -489,3 +451,6 @@ assert newcol2.fspath.basename == dir1.basename assert newcol2.fspath.relto(topdir) +def test_default_bus(): + assert py.test.config.bus._bus is py.event + Modified: py/branch/pytestplugin/py/test/testing/test_deprecated_api.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_deprecated_api.py (original) +++ py/branch/pytestplugin/py/test/testing/test_deprecated_api.py Wed Feb 18 15:43:50 2009 @@ -1,18 +1,17 @@ import py -from py.__.test.testing import suptest -class TestCollectDeprecated(suptest.InlineCollection): - def test_directory_run_join_warnings(self): - p = self.makepyfile(test_one="") - config = self.parseconfig() +class TestCollectDeprecated: + def test_directory_run_join_warnings(self, fstester): + p = fstester.makepyfile(test_one="") + config = fstester.parseconfig(p) dirnode = config.getfsnode(p.dirpath()) py.test.deprecated_call(dirnode.run) # XXX for directories we still have join() #py.test.deprecated_call(dirnode.join, 'test_one') - def test_collect_with_deprecated_run_and_join(self): - self.makepyfile(conftest=""" + def test_collect_with_deprecated_run_and_join(self, fstester): + fstester.makepyfile(conftest=""" import py class MyInstance(py.test.collect.Instance): @@ -46,12 +45,12 @@ return self.Module(self.fspath.join(name), parent=self) Directory = MyDirectory """) - p = self.makepyfile(somefile=""" + p = fstester.makepyfile(somefile=""" def check(): pass class Cls: def check2(self): pass """) - config = self.parseconfig() + config = fstester.parseconfig() dirnode = config.getfsnode(p.dirpath()) colitems = py.test.deprecated_call(dirnode.collect) assert len(colitems) == 1 @@ -69,8 +68,9 @@ assert len(colitems) == 1 assert colitems[0].name == 'check2' - def test_collect_with_deprecated_join_but_no_run(self): - self.makepyfile(conftest=""" + def test_collect_with_deprecated_join_but_no_run(self, tsession): + fstester = tsession.fstester + fstester.makepyfile(conftest=""" import py class Module(py.test.collect.Module): @@ -83,7 +83,7 @@ return self.Function(name, parent=self) assert name != "SomeClass", "join should not be called with this name" """) - col = self.getmodulecol(""" + col = tsession.getmodulecol(""" def somefunc(): pass def check_one(): pass class SomeClass: pass @@ -93,51 +93,53 @@ funcitem = colitems[0] assert funcitem.name == "check_one" - def test_function_custom_run(self): - self.makepyfile(conftest=""" + def test_function_custom_run(self, tsession): + fstester = tsession.fstester + fstester.makepyfile(conftest=""" import py class MyFunction(py.test.collect.Function): def run(self): pass Function=MyFunction """) - modcol = self.getmodulecol("def test_func(): pass") + modcol = tsession.getmodulecol("def test_func(): pass") funcitem = modcol.collect()[0] assert funcitem.name == 'test_func' py.test.deprecated_call(funcitem.runtest) - def test_function_custom_execute(self): - self.makepyfile(conftest=""" + def test_function_custom_execute(self, tsession): + fstester = tsession.fstester + fstester.makepyfile(conftest=""" import py class MyFunction(py.test.collect.Function): def execute(self, obj, *args): pass Function=MyFunction """) - modcol = self.getmodulecol("def test_func(): pass") + modcol = tsession.getmodulecol("def test_func(): pass") funcitem = modcol.collect()[0] assert funcitem.name == 'test_func' py.test.deprecated_call(funcitem.runtest) - def test_function_deprecated_run_execute(self): - modcol = self.getmodulecol("def test_some(): pass") + def test_function_deprecated_run_execute(self, tsession): + modcol = tsession.getmodulecol("def test_some(): pass") funcitem = modcol.collect()[0] py.test.deprecated_call(funcitem.run) py.test.deprecated_call(funcitem.execute, funcitem.obj) - def test_function_deprecated_run_recursive(self): - self.makepyfile(conftest=""" + def test_function_deprecated_run_recursive(self, tsession): + tsession.fstester.makepyfile(conftest=""" import py class Module(py.test.collect.Module): def run(self): return super(Module, self).run() """) - modcol = self.getmodulecol("def test_some(): pass") + modcol = tsession.getmodulecol("def test_some(): pass") colitems = py.test.deprecated_call(modcol.collect) funcitem = colitems[0] - def test_conftest_subclasses_Module_with_non_pyfile(self): - self.makepyfile(conftest=""" + def test_conftest_subclasses_Module_with_non_pyfile(self, fstester): + fstester.makepyfile(conftest=""" import py class Module(py.test.collect.Module): def run(self): @@ -148,7 +150,7 @@ return Module(path, parent=self) return super(Directory, self).consider_file(path) """) - testme = self._makefile('xxx', testme="hello") - config = self.parseconfig(testme) + testme = fstester.makefile('xxx', testme="hello") + config = fstester.parseconfig(testme) col = config.getfsnode(testme) assert col.collect() == [] From hpk at codespeak.net Wed Feb 18 15:53:52 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 15:53:52 +0100 (CET) Subject: [py-svn] r62008 - in py/branch/pytestplugin/py/test: . dsession/testing plugin testing Message-ID: <20090218145352.521081684B6@codespeak.net> Author: hpk Date: Wed Feb 18 15:53:47 2009 New Revision: 62008 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py py/branch/pytestplugin/py/test/plugin/pytest_apigen.py py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/plugin/pytest_unittest.py py/branch/pytestplugin/py/test/plugin/pytest_xfail.py py/branch/pytestplugin/py/test/testing/acceptance_test.py py/branch/pytestplugin/py/test/testing/test_collect.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_deprecated_api.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py Log: unify fstester and tsession helper objects to "testdir" Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Wed Feb 18 15:53:47 2009 @@ -164,19 +164,19 @@ def initsession(self): """ return an initialized session object. """ - cls = self._getsessionclass() + cls = self._getestdirclass() session = cls(self) session.fixoptions() return session - def _getsessionclass(self): + def _getestdirclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. """ if self.option.session is not None: return self._conftest.rget(self.option.session) else: - name = self._getsessionname() + name = self._getestdirname() try: return self._conftest.rget(name) except KeyError: @@ -185,7 +185,7 @@ mod = __import__(importpath, None, None, '__doc__') return getattr(mod, name) - def _getsessionname(self): + def _getestdirname(self): """ return default session name as determined from options. """ if self.option.collectonly: name = 'Session' Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py Wed Feb 18 15:53:47 2009 @@ -24,16 +24,16 @@ print queue.get() class TestDSession: - def test_fixoptions(self, fstester): - config = fstester.parseconfig("--exec=xxx") + def test_fixoptions(self, testdir): + config = testdir.parseconfig("--exec=xxx") config.initsession().fixoptions() assert config.option.numprocesses == 1 - config = fstester.parseconfig("--exec=xxx", '-n3') + config = testdir.parseconfig("--exec=xxx", '-n3') config.initsession().fixoptions() assert config.option.numprocesses == 3 - def test_add_remove_host(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_add_remove_host(self, testdir): + item = testdir.getitem("def test_func(): pass") rep = run(item) session = DSession(item._config) host = Host("localhost") @@ -47,8 +47,8 @@ assert item not in session.item2host py.test.raises(Exception, "session.removehost(host)") - def test_senditems_removeitems(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_senditems_removeitems(self, testdir): + item = testdir.getitem("def test_func(): pass") rep = run(item) session = DSession(item._config) host = Host("localhost") @@ -61,8 +61,8 @@ assert not session.host2pending[host] assert not session.item2host - def test_triggertesting_collect(self, tsession): - modcol = tsession.getmodulecol(""" + def test_triggertesting_collect(self, testdir): + modcol = testdir.getmodulecol(""" def test_func(): pass """) @@ -74,8 +74,8 @@ assert isinstance(rep, event.CollectionReport) assert len(rep.result) == 1 - def test_triggertesting_item(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_triggertesting_item(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() @@ -95,24 +95,24 @@ assert isinstance(ev, event.RescheduleItems) assert ev.items == [item] - def test_keyboardinterrupt(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_keyboardinterrupt(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) def raise_(timeout=None): raise KeyboardInterrupt() session.queue.get = raise_ exitstatus = session.loop([]) assert exitstatus == outcome.EXIT_INTERRUPTED - def test_internalerror(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_internalerror(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) def raise_(): raise ValueError() session.queue.get = raise_ exitstatus = session.loop([]) assert exitstatus == outcome.EXIT_INTERNALERROR - def test_rescheduleevent(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_rescheduleevent(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() @@ -134,8 +134,8 @@ assert loopstate.shuttingdown assert not loopstate.testsfailed - def test_no_hosts_remaining_for_tests(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_no_hosts_remaining_for_tests(self, testdir): + item = testdir.getitem("def test_func(): pass") # setup a session with one host session = DSession(item._config) host1 = Host("localhost") @@ -152,8 +152,8 @@ dumpqueue(session.queue) assert loopstate.exitstatus == outcome.EXIT_NOHOSTS - def test_hostdown_causes_reschedule_pending(self, tsession, EventRecorder): - modcol = tsession.getmodulecol(""" + def test_hostdown_causes_reschedule_pending(self, testdir, EventRecorder): + modcol = testdir.getmodulecol(""" def test_crash(): assert 0 def test_fail(): @@ -186,8 +186,8 @@ assert str(testrep.longrepr).find("crashed") != -1 assert str(testrep.longrepr).find(host.hostname) != -1 - def test_hostup_adds_to_available(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_hostup_adds_to_available(self, testdir): + item = testdir.getitem("def test_func(): pass") # setup a session with two hosts session = DSession(item._config) host1 = Host("localhost") @@ -199,8 +199,8 @@ session.loop_once(loopstate) assert len(session.host2pending) == 1 - def test_event_propagation(self, tsession, EventRecorder): - item = tsession.getitem("def test_func(): pass") + def test_event_propagation(self, testdir, EventRecorder): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) ev = event.NOP() @@ -229,18 +229,18 @@ dumpqueue(session.queue) return session, loopstate.exitstatus - def test_exit_completed_tests_ok(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_exit_completed_tests_ok(self, testdir): + item = testdir.getitem("def test_func(): pass") session, exitstatus = self.runthrough(item) assert exitstatus == outcome.EXIT_OK - def test_exit_completed_tests_fail(self, tsession): - item = tsession.getitem("def test_func(): 0/0") + def test_exit_completed_tests_fail(self, testdir): + item = testdir.getitem("def test_func(): 0/0") session, exitstatus = self.runthrough(item) assert exitstatus == outcome.EXIT_TESTSFAILED - def test_exit_on_first_failing(self, tsession): - modcol = tsession.getmodulecol(""" + def test_exit_on_first_failing(self, testdir): + modcol = testdir.getmodulecol(""" def test_fail(): assert 0 def test_pass(): @@ -267,8 +267,8 @@ assert loopstate.testsfailed assert loopstate.shuttingdown - def test_shuttingdown_filters_events(self, tsession, EventRecorder): - item = tsession.getitem("def test_func(): pass") + def test_shuttingdown_filters_events(self, testdir, EventRecorder): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) host = Host("localhost") session.addhost(host) @@ -283,8 +283,8 @@ session.loop_once(loopstate) assert evrec.getfirstnamed('hostdown') == ev - def test_filteritems(self, tsession, EventRecorder): - modcol = tsession.getmodulecol(""" + def test_filteritems(self, testdir, EventRecorder): + modcol = testdir.getmodulecol(""" def test_fail(): assert 0 def test_pass(): @@ -312,8 +312,8 @@ assert isinstance(ev, event.Deselected) assert ev.items == [items[1]] - def test_hostdown_shutdown_after_completion(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_hostdown_shutdown_after_completion(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) host = Host("localhost") @@ -330,8 +330,8 @@ session.loop_once(loopstate) assert loopstate.exitstatus == 0 - def test_nopending_but_collection_remains(self, tsession): - modcol = tsession.getmodulecol(""" + def test_nopending_but_collection_remains(self, testdir): + modcol = testdir.getmodulecol(""" def test_fail(): assert 0 def test_pass(): Modified: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_apigen.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Wed Feb 18 15:53:47 2009 @@ -52,8 +52,8 @@ def test_generic(plugintester): plugintester.apicheck(Apigen) -def test_simple(fstester): - sub = fstester.tmpdir.mkdir("test_simple") +def test_simple(testdir): + sub = testdir.tmpdir.mkdir("test_simple") sub.join("__init__.py").write(py.code.Source(""" from py import initpkg initpkg(__name__, exportdefs={ @@ -67,8 +67,8 @@ def test_f(): f(42) """)) - fstester.makepyfile(conftest="pytest_plugins='apigen'") - result = fstester.runpytest(pyfile, "--apigen") + testdir.makepyfile(conftest="pytest_plugins='apigen'") + result = testdir.runpytest(pyfile, "--apigen") result.stdout.fnmatch_lines([ "*apigen: building documentation*", "apigen build completed", Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Wed Feb 18 15:53:47 2009 @@ -29,13 +29,13 @@ def test_generic(plugintester): plugintester.apicheck(Eventlog) - fstester = plugintester.fstester() - fstester.makepyfile(""" + testdir = plugintester.testdir() + testdir.makepyfile(""" def test_pass(): pass """) - fstester.runpytest("--eventlog=event.log") - s = fstester.tmpdir.join("event.log").read() + testdir.runpytest("--eventlog=event.log") + s = testdir.tmpdir.join("event.log").read() assert s.find("TestrunStart") != -1 assert s.find("ItemTestReport") != -1 assert s.find("TestrunFinish") != -1 Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Wed Feb 18 15:53:47 2009 @@ -25,7 +25,7 @@ """ called after test function finished execution""" class PluginTester(Support): - def fstester(self, cmdlineargs=()): + def testdir(self, cmdlineargs=()): # XXX import differently, eg. # FSTester = self.pyfuncitem._config.pluginmanager.getpluginattr("pytester", "FSTester") from pytest_pytester import FSTester Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Wed Feb 18 15:53:47 2009 @@ -39,9 +39,9 @@ def test_apicheck(plugintester): plugintester.apicheck(Pocoo) -def test_toproxy(fstester): - fstester.makepyfile(conftest="pytest_plugins='pytest_pocoo',") - testpath = fstester.makepyfile(""" +def test_toproxy(testdir): + testdir.makepyfile(conftest="pytest_plugins='pytest_pocoo',") + testpath = testdir.makepyfile(""" import py def test_pass(): pass @@ -58,7 +58,7 @@ old = Pocoo.getproxy Pocoo.getproxy = MockProxy try: - result = fstester.inline_run(testpath, "--pocoo-sendfailures") + result = testdir.inline_run(testpath, "--pocoo-sendfailures") finally: Pocoo.getproxy = old assert len(l) == 1 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 18 15:53:47 2009 @@ -13,7 +13,7 @@ self.stdout = LineMatcher(outlines) self.stderr = LineMatcher(errlines) -class FSTester: +class Testdir: def __init__(self, pyfuncitem): self.pyfuncitem = pyfuncitem self.tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) @@ -71,6 +71,22 @@ def parseconfig(self, *args): return py.test.config._reparse(list(args)) + def getitem(self, source, funcname="test_func"): + modcol = self.getmodulecol(source) + item = modcol.join(funcname) + assert item is not None, "%r item not found in module:\n%s" %(funcname, source) + return item + + def getmodulecol(self, source, configargs=(), withinit=False): + kw = {self.pyfuncitem.name: py.code.Source(source).strip()} + path = self.makepyfile(**kw) + if withinit: + self.makepyfile(__init__ = "#") + self.config = self.parseconfig(path, *configargs) + self.session = self.config.initsession() + return self.config.getfsnode(path) + + def prepare(self): p = self.tmpdir.join("conftest.py") if not p.check(): @@ -121,36 +137,6 @@ def runpytest(self, *args): return self.runpybin("py.test", *args) -class TSession: - def __init__(self, pyfuncitem): - self.pyfuncitem = pyfuncitem - self.fstester = FSTester(pyfuncitem) - #self.capture = py.io.StdCapture() - - def parseconfig(self, *args): - return py.test.config._reparse(list(args)) - - #def finalize(self): - # self.capture.reset() - # - def genitems(self, colitems): - return self.session.genitems(colitems) - - def getitem(self, source, funcname="test_func"): - modcol = self.getmodulecol(source) - item = modcol.join(funcname) - assert item is not None, "%r item not found in module:\n%s" %(funcname, source) - return item - - def getmodulecol(self, source, configargs=(), withinit=False): - kw = {self.pyfuncitem.name: py.code.Source(source).strip()} - path = self.fstester.makepyfile(**kw) - if withinit: - self.fstester.makepyfile(__init__ = "#") - self.config = self.parseconfig(path, *configargs) - self.session = self.config.initsession() - return self.config.getfsnode(path) - class EventRecorder(object): def __init__(self, bus, debug=False): # True): @@ -342,11 +328,8 @@ return LineComp(), None elif argname == "LineMatcher": return LineMatcher, None - elif argname == "tsession": - tsession = TSession(pyfuncitem) - return tsession, None - elif argname == "fstester": - fstester = FSTester(pyfuncitem) - return fstester, None + elif argname == "testdir": + testdir = Testdir(pyfuncitem) + return testdir, None elif argname == "EventRecorder": return EventRecorder, None # or do some finalization Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Wed Feb 18 15:53:47 2009 @@ -164,29 +164,29 @@ # XXX (hpk) i think that the resultlog plugin should # provide a Parser object so that one can remain # ignorant regarding formatting details. - def getresultlog(self, fstester, arg): - resultlog = fstester.tmpdir.join("resultlog") + def getresultlog(self, testdir, arg): + resultlog = testdir.tmpdir.join("resultlog") args = ["--resultlog=%s" % resultlog] + [arg] - fstester.runpytest(*args) + testdir.runpytest(*args) return filter(None, resultlog.readlines(cr=0)) def test_collection_report(self, plugintester): - fstester = plugintester.fstester() - ok = fstester.makepyfile(test_collection_ok="") - skip = fstester.makepyfile(test_collection_skip="import py ; py.test.skip('hello')") - fail = fstester.makepyfile(test_collection_fail="XXX") + testdir = plugintester.testdir() + ok = testdir.makepyfile(test_collection_ok="") + skip = testdir.makepyfile(test_collection_skip="import py ; py.test.skip('hello')") + fail = testdir.makepyfile(test_collection_fail="XXX") - lines = self.getresultlog(fstester, ok) + lines = self.getresultlog(testdir, ok) assert not lines - lines = self.getresultlog(fstester, skip) + lines = self.getresultlog(testdir, skip) assert len(lines) == 2 assert lines[0].startswith("S ") assert lines[0].endswith("test_collection_skip.py") assert lines[1].startswith(" ") assert lines[1].endswith("test_collection_skip.py:1: Skipped: 'hello'") - lines = self.getresultlog(fstester, fail) + lines = self.getresultlog(testdir, fail) assert lines assert lines[0].startswith("F ") assert lines[0].endswith("test_collection_fail.py"), lines[0] @@ -195,14 +195,14 @@ assert "XXX" in "".join(lines[1:]) def test_log_test_outcomes(self, plugintester): - fstester = plugintester.fstester() - mod = fstester.makepyfile(test_mod=""" + testdir = plugintester.testdir() + mod = testdir.makepyfile(test_mod=""" import py def test_pass(): pass def test_skip(): py.test.skip("hello") def test_fail(): raise ValueError("val") """) - lines = self.getresultlog(fstester, mod) + lines = self.getresultlog(testdir, mod) assert len(lines) >= 3 assert lines[0].startswith(". ") assert lines[0].endswith("test_pass") @@ -238,8 +238,8 @@ def test_generic(plugintester, LineMatcher): plugintester.apicheck(Resultlog) - fstester = plugintester.fstester() - fstester.makepyfile(""" + testdir = plugintester.testdir() + testdir.makepyfile(""" import py def test_pass(): pass @@ -248,8 +248,8 @@ def test_skip(): py.test.skip("") """) - fstester.runpytest("--resultlog=result.log") - lines = fstester.tmpdir.join("result.log").readlines(cr=0) + testdir.runpytest("--resultlog=result.log") + lines = testdir.tmpdir.join("result.log").readlines(cr=0) LineMatcher(lines).fnmatch_lines([ ". *:test_pass", "F *:test_fail", Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 18 15:53:47 2009 @@ -315,8 +315,8 @@ from py.__.test.dsession.hostmanage import Host, makehostup class TestTerminal: - def test_hostup(self, tsession, linecomp): - item = tsession.getitem("def test_func(): pass") + def test_hostup(self, testdir, linecomp): + item = testdir.getitem("def test_func(): pass") rep = TerminalReporter(item._config, linecomp.stringio) host = Host("localhost") rep.pyevent_hostup(makehostup(host)) @@ -325,8 +325,8 @@ sys.executable, repr_pythonversion(sys.version_info)) ]) - def test_pass_skip_fail(self, tsession, linecomp): - modcol = tsession.getmodulecol(""" + def test_pass_skip_fail(self, testdir, linecomp): + modcol = testdir.getmodulecol(""" import py def test_ok(): pass @@ -339,7 +339,7 @@ registerdispatcher(rep) rep.config.bus.notify(testrunstart=event.TestrunStart()) - for item in tsession.genitems([modcol]): + for item in testdir.genitems([modcol]): ev = basic_run_report(item) rep.config.bus.notify(itemtestreport=ev) linecomp.assert_contains_lines([ @@ -352,8 +352,8 @@ "E assert 0", ]) - def test_pass_skip_fail_verbose(self, tsession, linecomp): - modcol = tsession.getmodulecol(""" + def test_pass_skip_fail_verbose(self, testdir, linecomp): + modcol = testdir.getmodulecol(""" import py def test_ok(): pass @@ -384,12 +384,12 @@ "E assert 0", ]) - def test_collect_fail(self, tsession, linecomp): - modcol = tsession.getmodulecol("import xyz") + def test_collect_fail(self, testdir, linecomp): + modcol = testdir.getmodulecol("import xyz") rep = TerminalReporter(modcol._config, file=linecomp.stringio) registerdispatcher(rep) rep.config.bus.notify(testrunstart=event.TestrunStart()) - l = list(tsession.genitems([modcol])) + l = list(testdir.genitems([modcol])) assert len(l) == 0 linecomp.assert_contains_lines([ "*test_collect_fail.py F*" @@ -400,8 +400,8 @@ "E ImportError: No module named xyz" ]) - def test_internal_exception(self, tsession, linecomp): - modcol = tsession.getmodulecol("def test_one(): pass") + def test_internal_exception(self, testdir, linecomp): + modcol = testdir.getmodulecol("def test_one(): pass") rep = TerminalReporter(modcol._config, file=linecomp.stringio) excinfo = py.test.raises(ValueError, "raise ValueError('hello')") rep.pyevent_internalexception(event.InternalException(excinfo)) @@ -409,8 +409,8 @@ "InternalException: >*raise ValueError*" ]) - def test_hostready_crash(self, tsession, linecomp): - modcol = tsession.getmodulecol(""" + def test_hostready_crash(self, testdir, linecomp): + modcol = testdir.getmodulecol(""" def test_one(): pass """, configargs=("-v",)) @@ -425,8 +425,8 @@ "*HostDown*myerror*", ]) - def test_writeline(self, tsession, linecomp): - modcol = tsession.getmodulecol("def test_one(): pass") + def test_writeline(self, testdir, linecomp): + modcol = testdir.getmodulecol("def test_one(): pass") stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=linecomp.stringio) rep.write_fspath_result(py.path.local("xy.py"), '.') @@ -436,8 +436,8 @@ assert lines[1].endswith("xy.py .") assert lines[2] == "hello world" - def test_looponfailingreport(self, tsession, linecomp): - modcol = tsession.getmodulecol(""" + def test_looponfailingreport(self, testdir, linecomp): + modcol = testdir.getmodulecol(""" def test_fail(): assert 0 def test_fail2(): @@ -453,11 +453,11 @@ "*%s*" % (modcol._config.topdir), ]) - def test_tb_option(self, tsession, linecomp): - # XXX usage of tsession and event bus + def test_tb_option(self, testdir, linecomp): + # XXX usage of testdir and event bus for tbopt in ["long", "short", "no"]: print 'testing --tb=%s...' % tbopt - modcol = tsession.getmodulecol(""" + modcol = testdir.getmodulecol(""" import py def g(): raise IndexError @@ -468,7 +468,7 @@ rep = TerminalReporter(modcol._config, file=linecomp.stringio) registerdispatcher(rep) rep.config.bus.notify(testrunstart=event.TestrunStart()) - for item in tsession.genitems([modcol]): + for item in testdir.genitems([modcol]): rep.config.bus.notify(itemtestreport=basic_run_report(item)) rep.config.bus.notify(testrunfinish=event.TestrunFinish()) s = linecomp.stringio.getvalue() @@ -486,22 +486,22 @@ assert 'IndexError' not in s linecomp.stringio.truncate(0) - def test_show_path_before_running_test(self, tsession, linecomp): - modcol = tsession.getmodulecol(""" + def test_show_path_before_running_test(self, testdir, linecomp): + modcol = testdir.getmodulecol(""" def test_foobar(): pass """) rep = TerminalReporter(modcol._config, file=linecomp.stringio) registerdispatcher(rep) - l = list(tsession.genitems([modcol])) + l = list(testdir.genitems([modcol])) assert len(l) == 1 rep.config.bus.notify(itemstart=event.ItemStart(l[0])) linecomp.assert_contains_lines([ "*test_show_path_before_running_test.py*" ]) - def pseudo_keyboard_interrupt(self, tsession, linecomp, verbose=False): - modcol = tsession.getmodulecol(""" + def pseudo_keyboard_interrupt(self, testdir, linecomp, verbose=False): + modcol = testdir.getmodulecol(""" def test_foobar(): assert 0 def test_spamegg(): @@ -514,7 +514,7 @@ bus = modcol._config.bus bus.notify(testrunstart=event.TestrunStart()) try: - for item in tsession.genitems([modcol]): + for item in testdir.genitems([modcol]): bus.notify(itemtestreport=basic_run_report(item)) except KeyboardInterrupt: excinfo = py.code.ExceptionInfo() @@ -535,11 +535,11 @@ see_details = "raise KeyboardInterrupt # simulating the user" in text assert see_details == verbose - def test_keyboard_interrupt(self, tsession, linecomp): - self.pseudo_keyboard_interrupt(tsession, linecomp) + def test_keyboard_interrupt(self, testdir, linecomp): + self.pseudo_keyboard_interrupt(testdir, linecomp) - def test_verbose_keyboard_interrupt(self, tsession, linecomp): - self.pseudo_keyboard_interrupt(tsession, linecomp, verbose=True) + def test_verbose_keyboard_interrupt(self, testdir, linecomp): + self.pseudo_keyboard_interrupt(testdir, linecomp, verbose=True) def test_skip_reasons_folding(self): class longrepr: @@ -565,8 +565,8 @@ assert reason == longrepr.reprcrash.message class TestCollectonly: - def test_collectonly_basic(self, tsession, linecomp): - modcol = tsession.getmodulecol(configargs=['--collectonly'], source=""" + def test_collectonly_basic(self, testdir, linecomp): + modcol = testdir.getmodulecol(configargs=['--collectonly'], source=""" def test_func(): pass """) @@ -587,27 +587,27 @@ collectionreport=event.CollectionReport(modcol, [], excinfo=None)) assert rep.indent == indent - def test_collectonly_skipped_module(self, tsession, linecomp): - modcol = tsession.getmodulecol(configargs=['--collectonly'], source=""" + def test_collectonly_skipped_module(self, testdir, linecomp): + modcol = testdir.getmodulecol(configargs=['--collectonly'], source=""" import py py.test.skip("nomod") """) rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) registerdispatcher(rep) - cols = list(tsession.genitems([modcol])) + cols = list(testdir.genitems([modcol])) assert len(cols) == 0 linecomp.assert_contains_lines(""" !!! Skipped: 'nomod' !!! """) - def test_collectonly_failed_module(self, tsession, linecomp): - modcol = tsession.getmodulecol(configargs=['--collectonly'], source=""" + def test_collectonly_failed_module(self, testdir, linecomp): + modcol = testdir.getmodulecol(configargs=['--collectonly'], source=""" raise ValueError(0) """) rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) registerdispatcher(rep) - cols = list(tsession.genitems([modcol])) + cols = list(testdir.genitems([modcol])) assert len(cols) == 0 linecomp.assert_contains_lines(""" Modified: py/branch/pytestplugin/py/test/plugin/pytest_unittest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_unittest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_unittest.py Wed Feb 18 15:53:47 2009 @@ -73,8 +73,8 @@ def test_generic(plugintester): plugintester.apicheck(Unittest) -def test_simple_unittest(fstester): - testpath = fstester.makepyfile(test_simple_unittest=""" +def test_simple_unittest(testdir): + testpath = testdir.makepyfile(test_simple_unittest=""" import unittest pytest_plugins = "pytest_unittest" # XXX class MyTestCase(unittest.TestCase): @@ -83,12 +83,12 @@ def test_failing(self): self.assertEquals('foo', 'bar') """) - sorter = fstester.inline_run(testpath) + sorter = testdir.inline_run(testpath) assert sorter.getreport("testpassing").passed assert sorter.getreport("test_failing").failed -def test_setup(fstester): - testpath = fstester.makepyfile(test_two=""" +def test_setup(testdir): + testpath = testdir.makepyfile(test_two=""" import unittest pytest_plugins = "pytest_unittest" # XXX class MyTestCase(unittest.TestCase): @@ -97,12 +97,12 @@ def test_setUp(self): self.assertEquals(1, self.foo) """) - sorter = fstester.inline_run(testpath) + sorter = testdir.inline_run(testpath) rep = sorter.getreport("test_setUp") assert rep.passed -def test_teardown(fstester): - testpath = fstester.makepyfile(test_three=""" +def test_teardown(testdir): + testpath = testdir.makepyfile(test_three=""" import unittest pytest_plugins = "pytest_unittest" # XXX class MyTestCase(unittest.TestCase): @@ -115,7 +115,7 @@ def test_check(self): self.assertEquals(MyTestCase.l, [None]) """) - sorter = fstester.inline_run(testpath) + sorter = testdir.inline_run(testpath) passed, skipped, failed = sorter.countoutcomes() print "COUNTS", passed, skipped, failed assert failed == 0, failed Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Wed Feb 18 15:53:47 2009 @@ -46,15 +46,15 @@ plugintester.apicheck(impname) def test_xfail(plugintester, linecomp): - fstester = plugintester.fstester() - p = fstester.makepyfile(test_one=""" + testdir = plugintester.testdir() + p = testdir.makepyfile(test_one=""" import py pytest_plugins="pytest_xfail", @py.test.keywords(xfail=True) def test_this(): assert 0 """) - result = fstester.runpytest(p) + result = testdir.runpytest(p) extra = result.stdout.fnmatch_lines([ "*XFAILURES*", "*test_one.test_this*test_one.py:5: assert 0*", Modified: py/branch/pytestplugin/py/test/testing/acceptance_test.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/acceptance_test.py (original) +++ py/branch/pytestplugin/py/test/testing/acceptance_test.py Wed Feb 18 15:53:47 2009 @@ -5,28 +5,28 @@ EXPECTTIMEOUT=10.0 class TestPyTest: - def test_assertion_magic(self, fstester): - p = fstester.makepyfile(""" + def test_assertion_magic(self, testdir): + p = testdir.makepyfile(""" def test_this(): x = 0 assert x """) - result = fstester.runpytest(p) + result = testdir.runpytest(p) extra = result.stdout.fnmatch_lines([ "> assert x", "E assert 0", ]) assert result.ret == 1 - def test_collectonly_simple(self, fstester): - p = fstester.makepyfile(""" + def test_collectonly_simple(self, testdir): + p = testdir.makepyfile(""" def test_func1(): pass class TestClass: def test_method(self): pass """) - result = fstester.runpytest("--collectonly", p) + result = testdir.runpytest("--collectonly", p) stderr = result.stderr.str().strip() assert stderr.startswith("inserting into sys.path") assert result.ret == 0 @@ -38,22 +38,22 @@ """).strip()) - def test_nested_import_error(self, fstester): - p = fstester.makepyfile(""" + def test_nested_import_error(self, testdir): + p = testdir.makepyfile(""" import import_fails def test_this(): assert import_fails.a == 1 """) - fstester.makepyfile(import_fails="import does_not_work") - result = fstester.runpytest(p) + testdir.makepyfile(import_fails="import does_not_work") + result = testdir.runpytest(p) extra = result.stdout.fnmatch_lines([ "> import import_fails", "E ImportError: No module named does_not_work", ]) assert result.ret == 1 - def test_skipped_reasons(self, fstester): - fstester.makepyfile( + def test_skipped_reasons(self, testdir): + testdir.makepyfile( test_one=""" from conftest import doskip def setup_function(func): @@ -74,7 +74,7 @@ py.test.skip('test') """ ) - result = fstester.runpytest() + result = testdir.runpytest() extra = result.stdout.fnmatch_lines([ "*test_one.py ss", "*test_two.py S", @@ -83,8 +83,8 @@ ]) assert result.ret == 0 - def test_deselected(self, fstester): - testpath = fstester.makepyfile(""" + def test_deselected(self, testdir): + testpath = testdir.makepyfile(""" def test_one(): pass def test_two(): @@ -93,15 +93,15 @@ pass """ ) - result = fstester.runpytest("-k", "test_two:", testpath) + result = testdir.runpytest("-k", "test_two:", testpath) extra = result.stdout.fnmatch_lines([ "*test_deselected.py ..", "=* 1 test*deselected by 'test_two:'*=", ]) assert result.ret == 0 - def test_no_skip_summary_if_failure(self, fstester): - fstester.makepyfile(""" + def test_no_skip_summary_if_failure(self, testdir): + testdir.makepyfile(""" import py def test_ok(): pass @@ -110,12 +110,12 @@ def test_skip(): py.test.skip("dontshow") """) - result = fstester.runpytest() + result = testdir.runpytest() assert result.stdout.str().find("skip test summary") == -1 assert result.ret == 1 - def test_passes(self, fstester): - p1 = fstester.makepyfile(""" + def test_passes(self, testdir): + p1 = testdir.makepyfile(""" def test_passes(): pass class TestClass: @@ -124,7 +124,7 @@ """) old = p1.dirpath().chdir() try: - result = fstester.runpytest() + result = testdir.runpytest() finally: old.chdir() extra = result.stdout.fnmatch_lines([ @@ -133,12 +133,12 @@ ]) assert result.ret == 0 - def test_header_trailer_info(self, fstester): - p1 = fstester.makepyfile(""" + def test_header_trailer_info(self, testdir): + p1 = testdir.makepyfile(""" def test_passes(): pass """) - result = fstester.runpytest() + result = testdir.runpytest() verinfo = ".".join(map(str, py.std.sys.version_info[:3])) extra = result.stdout.fnmatch_lines([ "*===== test session starts ====*", @@ -148,8 +148,8 @@ "=* 1 passed in *.[0-9][0-9] seconds *=", ]) - def test_traceback_failure(self, fstester): - p1 = fstester.makepyfile(""" + def test_traceback_failure(self, testdir): + p1 = testdir.makepyfile(""" def g(): return 2 def f(x): @@ -157,7 +157,7 @@ def test_onefails(): f(3) """) - result = fstester.runpytest(p1) + result = testdir.runpytest(p1) result.stdout.fnmatch_lines([ "*test_traceback_failure.py F", "====* FAILURES *====", @@ -177,8 +177,8 @@ "*test_traceback_failure.py:4: AssertionError" ]) - def test_capturing_outerr(self, fstester): - p1 = fstester.makepyfile(""" + def test_capturing_outerr(self, testdir): + p1 = testdir.makepyfile(""" import sys def test_capturing(): print 42 @@ -188,7 +188,7 @@ print >>sys.stderr, 2 raise ValueError """) - result = fstester.runpytest(p1) + result = testdir.runpytest(p1) result.stdout.fnmatch_lines([ "*test_capturing_outerr.py .F", "====* FAILURES *====", @@ -200,27 +200,27 @@ "2", ]) - def test_showlocals(self, fstester): - p1 = fstester.makepyfile(""" + def test_showlocals(self, testdir): + p1 = testdir.makepyfile(""" def test_showlocals(): x = 3 y = "x" * 5000 assert 0 """) - result = fstester.runpytest(p1, '-l') + result = testdir.runpytest(p1, '-l') result.stdout.fnmatch_lines([ #"_ _ * Locals *", "x* = 3", "y* = 'xxxxxx*" ]) - def test_doctest_simple_failing(self, fstester): - p = fstester.maketxtfile(""" + def test_doctest_simple_failing(self, testdir): + p = testdir.maketxtfile(""" >>> i = 0 >>> i + 1 2 """) - result = fstester.runpytest(p) + result = testdir.runpytest(p) result.stdout.fnmatch_lines([ '001 >>> i = 0', '002 >>> i + 1', @@ -231,8 +231,8 @@ "*test_doctest_simple_failing.txt:2: DocTestFailure" ]) - def test_dist_testing(self, fstester): - p1 = fstester.makepyfile(""" + def test_dist_testing(self, testdir): + p1 = testdir.makepyfile(""" import py def test_fail0(): assert 0 @@ -247,7 +247,7 @@ dist_hosts = ['localhost'] * 3 """ ) - result = fstester.runpytest(p1, '-d') + result = testdir.runpytest(p1, '-d') result.stdout.fnmatch_lines([ "HOSTUP: localhost*Python*", #"HOSTUP: localhost*Python*", @@ -256,11 +256,11 @@ ]) assert result.ret == 1 - def test_dist_tests_with_crash(self, fstester): + def test_dist_tests_with_crash(self, testdir): if not hasattr(py.std.os, 'kill'): py.test.skip("no os.kill") - p1 = fstester.makepyfile(""" + p1 = testdir.makepyfile(""" import py def test_fail0(): assert 0 @@ -280,7 +280,7 @@ dist_hosts = ['localhost'] * 3 """ ) - result = fstester.runpytest(p1, '-d') + result = testdir.runpytest(p1, '-d') result.stdout.fnmatch_lines([ "*localhost*Python*", "*localhost*Python*", @@ -290,23 +290,23 @@ ]) assert result.ret == 1 - def test_keyboard_interrupt(self, fstester): - p1 = fstester.makepyfile(""" + def test_keyboard_interrupt(self, testdir): + p1 = testdir.makepyfile(""" import py def test_fail(): raise ValueError() def test_inter(): raise KeyboardInterrupt() """) - result = fstester.runpytest(p1) + result = testdir.runpytest(p1) result.stdout.fnmatch_lines([ #"*test_inter() INTERRUPTED", "*KEYBOARD INTERRUPT*", "*1 failed*", ]) - def test_verbose_reporting(self, fstester): - p1 = fstester.makepyfile(""" + def test_verbose_reporting(self, testdir): + p1 = testdir.makepyfile(""" import py def test_fail(): raise ValueError() @@ -320,7 +320,7 @@ assert x == 1 yield check, 0 """) - result = fstester.runpytest(p1, '-v') + result = testdir.runpytest(p1, '-v') result.stdout.fnmatch_lines([ "*test_verbose_reporting.py:2: test_fail*FAIL", "*test_verbose_reporting.py:4: test_pass*PASS", @@ -342,10 +342,10 @@ if ver < version_needed: py.test.skip("pexpect version %s needed" %(".".join(map(str, version_needed)))) - def test_pdb_interaction(self, fstester): + def test_pdb_interaction(self, testdir): self.requirespexpect((2,3)) - spawn = self.getspawn(fstester.tmpdir) - p1 = fstester.makepyfile(""" + spawn = self.getspawn(testdir.tmpdir) + p1 = testdir.makepyfile(""" def test_1(): i = 0 assert i == 1 @@ -361,9 +361,9 @@ if child.isalive(): child.wait() - def test_simple_looponfailing_interaction(self, fstester): - spawn = self.getspawn(fstester.tmpdir) - p1 = fstester.makepyfile(""" + def test_simple_looponfailing_interaction(self, testdir): + spawn = self.getspawn(testdir.tmpdir) + p1 = testdir.makepyfile(""" def test_1(): assert 1 == 0 """) Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Wed Feb 18 15:53:47 2009 @@ -43,20 +43,20 @@ assert 'test_found.py' in names class TestCollect: - def test_failing_import(self, tsession): - modcol = tsession.getmodulecol("import alksdjalskdjalkjals") + def test_failing_import(self, testdir): + modcol = testdir.getmodulecol("import alksdjalskdjalkjals") py.test.raises(ImportError, modcol.collect) py.test.raises(ImportError, modcol.collect) py.test.raises(ImportError, modcol.run) - def test_syntax_error_in_module(self, tsession): - modcol = tsession.getmodulecol("this is a syntax error") + def test_syntax_error_in_module(self, testdir): + modcol = testdir.getmodulecol("this is a syntax error") py.test.raises(SyntaxError, modcol.collect) py.test.raises(SyntaxError, modcol.collect) py.test.raises(SyntaxError, modcol.run) - def test_listnames_and__getitembynames(self, tsession): - modcol = tsession.getmodulecol("pass", withinit=True) + def test_listnames_and__getitembynames(self, testdir): + modcol = testdir.getmodulecol("pass", withinit=True) names = modcol.listnames() print names dircol = modcol._config.getfsnode(modcol._config.topdir) @@ -64,9 +64,9 @@ assert modcol.name == x.name assert modcol.name == x.name - def test_listnames_getitembynames_custom(self, fstester): - hello = fstester.makefile(".xxx", hello="world") - fstester.makepyfile(conftest=""" + def test_listnames_getitembynames_custom(self, testdir): + hello = testdir.makefile(".xxx", hello="world") + testdir.makepyfile(conftest=""" import py class CustomFile(py.test.collect.File): pass @@ -75,7 +75,7 @@ return [CustomFile(self.fspath.join("hello.xxx"), parent=self)] Directory = MyDirectory """) - config = fstester.parseconfig(hello) + config = testdir.parseconfig(hello) node = config.getfsnode(hello) assert isinstance(node, py.test.collect.File) assert node.name == "hello.xxx" @@ -84,16 +84,16 @@ node = dircol._getitembynames(names) assert isinstance(node, py.test.collect.File) - def test_found_certain_testfiles(self, fstester): - p1 = fstester.makepyfile(test_found = "pass", found_test="pass") + def test_found_certain_testfiles(self, testdir): + p1 = testdir.makepyfile(test_found = "pass", found_test="pass") col = py.test.collect.Directory(p1.dirpath(), config=dummyconfig) items = col.collect() # Directory collect returns files sorted by name assert len(items) == 2 assert items[1].name == 'test_found.py' assert items[0].name == 'found_test.py' - def test_disabled_class(self, tsession): - modcol = tsession.getmodulecol(""" + def test_disabled_class(self, testdir): + modcol = testdir.getmodulecol(""" class TestClass: disabled = True def test_method(self): @@ -105,8 +105,8 @@ assert isinstance(modcol, py.test.collect.Class) assert not modcol.collect() - def test_disabled_module(self, tsession): - modcol = tsession.getmodulecol(""" + def test_disabled_module(self, testdir): + modcol = testdir.getmodulecol(""" disabled = True def setup_module(mod): raise ValueError @@ -114,8 +114,8 @@ assert not modcol.collect() assert not modcol.run() - def test_generative_functions(self, tsession): - modcol = tsession.getmodulecol(""" + def test_generative_functions(self, testdir): + modcol = testdir.getmodulecol(""" def func1(arg, arg2): assert arg == arg2 @@ -134,8 +134,8 @@ assert gencolitems[0].name == '[0]' assert gencolitems[0].obj.func_name == 'func1' - def test_generative_methods(self, tsession): - modcol = tsession.getmodulecol(""" + def test_generative_methods(self, testdir): + modcol = testdir.getmodulecol(""" def func1(arg, arg2): assert arg == arg2 class TestGenMethods: @@ -152,8 +152,8 @@ assert gencolitems[0].name == '[0]' assert gencolitems[0].obj.func_name == 'func1' - def test_generative_functions_with_explicit_names(self, tsession): - modcol = tsession.getmodulecol(""" + def test_generative_functions_with_explicit_names(self, testdir): + modcol = testdir.getmodulecol(""" def func1(arg, arg2): assert arg == arg2 @@ -174,8 +174,8 @@ assert gencolitems[1].name == "['fortytwo']" assert gencolitems[1].obj.func_name == 'func1' - def test_generative_methods_with_explicit_names(self, tsession): - modcol = tsession.getmodulecol(""" + def test_generative_methods_with_explicit_names(self, testdir): + modcol = testdir.getmodulecol(""" def func1(arg, arg2): assert arg == arg2 class TestGenMethods: @@ -194,8 +194,8 @@ assert gencolitems[1].name == "['m2']" assert gencolitems[1].obj.func_name == 'func1' - def test_module_assertion_setup(self, tsession): - modcol = tsession.getmodulecol("pass") + def test_module_assertion_setup(self, testdir): + modcol = testdir.getmodulecol("pass") from py.__.magic import assertion l = [] py.magic.patch(assertion, "invoke", lambda: l.append(None)) @@ -213,8 +213,8 @@ x = l.pop() assert x is None - def test_check_equality_and_cmp_basic(self, tsession): - modcol = tsession.getmodulecol(""" + def test_check_equality_and_cmp_basic(self, testdir): + modcol = testdir.getmodulecol(""" def test_pass(): pass def test_fail(): assert 0 """) @@ -244,25 +244,25 @@ assert [1,2,3] != fn assert modcol != fn - def test_directory_file_sorting(self, fstester): - p1 = fstester.makepyfile(test_one="hello") + def test_directory_file_sorting(self, testdir): + p1 = testdir.makepyfile(test_one="hello") p1.dirpath().mkdir("x") p1.dirpath().mkdir("dir1") - fstester.makepyfile(test_two="hello") + testdir.makepyfile(test_two="hello") p1.dirpath().mkdir("dir2") - config = fstester.parseconfig() + config = testdir.parseconfig() col = config.getfsnode(p1.dirpath()) names = [x.name for x in col.collect()] assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"] - def test_collector_deprecated_run_method(self, tsession): - modcol = tsession.getmodulecol("pass") + def test_collector_deprecated_run_method(self, testdir): + modcol = testdir.getmodulecol("pass") res1 = py.test.deprecated_call(modcol.run) res2 = modcol.collect() assert res1 == [x.name for x in res2] - def test_allow_sane_sorting_for_decorators(self, tsession): - modcol = tsession.getmodulecol(""" + def test_allow_sane_sorting_for_decorators(self, testdir): + modcol = testdir.getmodulecol(""" def dec(f): g = lambda: f(2) g.place_as = f @@ -284,8 +284,8 @@ class TestCustomConftests: - def test_extra_python_files_and_functions(self, fstester): - fstester.makepyfile(conftest=""" + def test_extra_python_files_and_functions(self, testdir): + testdir.makepyfile(conftest=""" import py class MyFunction(py.test.collect.Function): pass @@ -304,7 +304,7 @@ class Instance(myfuncmixin, py.test.collect.Instance): pass """) - checkfile = fstester.makepyfile(check_file=""" + checkfile = testdir.makepyfile(check_file=""" def check_func(): assert 42 == 42 class CustomTestClass: @@ -312,14 +312,14 @@ assert 23 == 23 """) # check that directory collects "check_" files - config = fstester.parseconfig() + config = testdir.parseconfig() col = config.getfsnode(checkfile.dirpath()) colitems = col.collect() assert len(colitems) == 1 assert isinstance(colitems[0], py.test.collect.Module) # check that module collects "check_" functions and methods - config = fstester.parseconfig(checkfile) + config = testdir.parseconfig(checkfile) col = config.getfsnode(checkfile) assert isinstance(col, py.test.collect.Module) colitems = col.collect() @@ -333,8 +333,8 @@ assert len(colitems) == 1 assert colitems[0].name == "check_method" - def test_non_python_files(self, fstester): - fstester.makepyfile(conftest=""" + def test_non_python_files(self, testdir): + testdir.makepyfile(conftest=""" import py class CustomItem(py.test.collect.Item): def run(self): @@ -344,10 +344,10 @@ if fspath.ext == ".xxx": return CustomItem(fspath.basename, parent=self) """) - checkfile = fstester.makefile(ext="xxx", hello="world") - fstester.makepyfile(x="") - fstester.maketxtfile(x="") - config = fstester.parseconfig() + checkfile = testdir.makefile(ext="xxx", hello="world") + testdir.makepyfile(x="") + testdir.maketxtfile(x="") + config = testdir.parseconfig() dircol = config.getfsnode(checkfile.dirpath()) colitems = dircol.collect() assert len(colitems) == 1 @@ -364,8 +364,8 @@ py.test.raises(py.error.ENOENT, col.collect) -def test_order_of_execution_generator_same_codeline(fstester, tmpdir): - o = fstester.makepyfile(""" +def test_order_of_execution_generator_same_codeline(testdir, tmpdir): + o = testdir.makepyfile(""" def test_generative_order_of_execution(): test_list = [] expected_list = range(6) @@ -382,13 +382,13 @@ yield list_append, i yield assert_order_of_execution """) - sorter = fstester.inline_run(o) + sorter = testdir.inline_run(o) passed, skipped, failed = sorter.countoutcomes() assert passed == 7 assert not skipped and not failed -def test_order_of_execution_generator_different_codeline(fstester): - o = fstester.makepyfile(""" +def test_order_of_execution_generator_different_codeline(testdir): + o = testdir.makepyfile(""" def test_generative_tests_different_codeline(): test_list = [] expected_list = range(3) @@ -412,7 +412,7 @@ yield list_append_2 yield assert_order_of_execution """) - sorter = fstester.inline_run(o) # .events_from_cmdline([o]) + sorter = testdir.inline_run(o) # .events_from_cmdline([o]) passed, skipped, failed = sorter.countoutcomes() assert passed == 4 assert not skipped and not failed @@ -439,8 +439,8 @@ assert not f1 != f1_b class Testgenitems: - def test_check_collect_hashes(self, fstester): - p = fstester.makepyfile(""" + def test_check_collect_hashes(self, testdir): + p = testdir.makepyfile(""" def test_1(): pass @@ -448,7 +448,7 @@ pass """) p.copy(p.dirpath(p.purebasename + "2" + ".py")) - items, events = fstester.inline_genitems(p.dirpath()) + items, events = testdir.inline_genitems(p.dirpath()) assert len(items) == 4 for numi, i in enumerate(items): for numj, j in enumerate(items): @@ -456,23 +456,23 @@ assert hash(i) != hash(j) assert i != j - def test_root_conftest_syntax_error(self, fstester): + def test_root_conftest_syntax_error(self, testdir): # do we want to unify behaviour with # test_subdir_conftest_error? - p = fstester.makepyfile(conftest="raise SyntaxError\n") - py.test.raises(SyntaxError, fstester.inline_genitems, p.dirpath()) + p = testdir.makepyfile(conftest="raise SyntaxError\n") + py.test.raises(SyntaxError, testdir.inline_genitems, p.dirpath()) - def test_subdir_conftest_error(self, fstester): - tmp = fstester.tmpdir + def test_subdir_conftest_error(self, testdir): + tmp = testdir.tmpdir tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") - items, events = fstester.inline_genitems(tmp) + items, events = testdir.inline_genitems(tmp) collectionfailures = events.getfailedcollections() assert len(collectionfailures) == 1 ev = collectionfailures[0] assert ev.longrepr.reprcrash.message.startswith("SyntaxError") - def test_example_items1(self, fstester): - p = fstester.makepyfile(''' + def test_example_items1(self, testdir): + p = testdir.makepyfile(''' def testone(): pass @@ -483,7 +483,7 @@ class TestY(TestX): pass ''') - items, events = fstester.inline_genitems(p) + items, events = testdir.inline_genitems(p) assert len(items) == 3 assert items[0].name == 'testone' assert items[1].name == 'testmethod_one' @@ -498,17 +498,17 @@ assert s.endswith("test_example_items1.testone") print s - def test_collect_doctest_files_with_test_prefix(self, fstester): - fstester.maketxtfile(whatever="") - checkfile = fstester.maketxtfile(test_something=""" + def test_collect_doctest_files_with_test_prefix(self, testdir): + testdir.maketxtfile(whatever="") + checkfile = testdir.maketxtfile(test_something=""" alskdjalsdk >>> i = 5 >>> i-1 4 """) - for x in (fstester.tmpdir, checkfile): + for x in (testdir.tmpdir, checkfile): #print "checking that %s returns custom items" % (x,) - items, events = fstester.inline_genitems(x) + items, events = testdir.inline_genitems(x) assert len(items) == 1 assert isinstance(items[0], DoctestFileContent) @@ -541,8 +541,8 @@ "col3._totrail()") class TestCollectorReprs: - def test_repr_metainfo_basic_item(self, tsession): - modcol = tsession.getmodulecol("") + def test_repr_metainfo_basic_item(self, testdir): + modcol = testdir.getmodulecol("") Item = py.test.collect.Item item = Item("virtual", parent=modcol) info = item.repr_metainfo() @@ -550,15 +550,15 @@ assert not info.lineno assert info.modpath == "Item" - def test_repr_metainfo_func(self, tsession): - item = tsession.getitem("def test_func(): pass") + def test_repr_metainfo_func(self, testdir): + item = testdir.getitem("def test_func(): pass") info = item.repr_metainfo() assert info.fspath == item.fspath assert info.lineno == 0 assert info.modpath == "test_func" - def test_repr_metainfo_class(self, tsession): - modcol = tsession.getmodulecol(""" + def test_repr_metainfo_class(self, testdir): + modcol = testdir.getmodulecol(""" # lineno 0 class TestClass: def test_hello(self): pass @@ -569,8 +569,8 @@ assert info.lineno == 1 assert info.modpath == "TestClass" - def test_repr_metainfo_generator(self, tsession): - modcol = tsession.getmodulecol(""" + def test_repr_metainfo_generator(self, testdir): + modcol = testdir.getmodulecol(""" # lineno 0 def test_gen(): def check(x): @@ -630,8 +630,8 @@ config_back = self.p2_to_p1(p2config) assert config_back is config1 - def test_pickle_module(self, tsession): - modcol1 = tsession.getmodulecol("def test_one(): pass") + def test_pickle_module(self, testdir): + modcol1 = testdir.getmodulecol("def test_one(): pass") self.unifyconfig(modcol1._config) modcol2a = self.p1_to_p2(modcol1) @@ -641,8 +641,8 @@ modcol1_back = self.p2_to_p1(modcol2a) assert modcol1_back - def test_pickle_func(self, tsession): - modcol1 = tsession.getmodulecol("def test_one(): pass") + def test_pickle_func(self, testdir): + modcol1 = testdir.getmodulecol("def test_one(): pass") self.unifyconfig(modcol1._config) item = modcol1.collect_by_name("test_one") item2a = self.p1_to_p2(item) Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Wed Feb 18 15:53:47 2009 @@ -163,9 +163,9 @@ assert option.gdest == 11 class TestSessionAndOptions: - def test_tracedir_tracer(self, fstester): - tracedir = fstester.mkdir("tracedir") - config = fstester.parseconfig("--tracedir=%s" % tracedir) + def test_tracedir_tracer(self, testdir): + tracedir = testdir.mkdir("tracedir") + config = testdir.parseconfig("--tracedir=%s" % tracedir) assert config.gettracedir() == tracedir trace = config.maketrace("trace1.log") # flush=True by default @@ -178,46 +178,46 @@ assert lines[1].find("A") != -1 trace.close() - def test_trace_null(self, fstester): - config = fstester.parseconfig(fstester.tmpdir) + def test_trace_null(self, testdir): + config = testdir.parseconfig(testdir.tmpdir) assert config.gettracedir() is None trace = config.maketrace("hello", flush=True) trace("hello", "world") trace.close() - def test_implied_dsession(self, fstester): + def test_implied_dsession(self, testdir): for x in 'startserver runbrowser rest'.split(): - config = fstester.parseconfig(fstester.tmpdir, '--dist', '--%s' % x) - assert config._getsessionname() == 'DSession' + config = testdir.parseconfig(testdir.tmpdir, '--dist', '--%s' % x) + assert config._getestdirname() == 'DSession' def test_implied_different_sessions(self, tmpdir): config = py.test.config._reparse([tmpdir]) - assert config._getsessionname() == 'Session' + assert config._getestdirname() == 'Session' config = py.test.config._reparse([tmpdir, '--dist']) - assert config._getsessionname() == 'DSession' + assert config._getestdirname() == 'DSession' config = py.test.config._reparse([tmpdir, '-n3']) - assert config._getsessionname() == 'DSession' + assert config._getestdirname() == 'DSession' config = py.test.config._reparse([tmpdir, '--looponfailing']) - assert config._getsessionname() == 'LooponfailingSession' + assert config._getestdirname() == 'LooponfailingSession' config = py.test.config._reparse([tmpdir, '--exec=x']) - assert config._getsessionname() == 'DSession' + assert config._getestdirname() == 'DSession' config = py.test.config._reparse([tmpdir, '--dist', '--exec=x']) - assert config._getsessionname() == 'DSession' + assert config._getestdirname() == 'DSession' config = py.test.config._reparse([tmpdir, '-f', '--dist', '--exec=x']) - assert config._getsessionname() == 'LooponfailingSession' + assert config._getestdirname() == 'LooponfailingSession' config = py.test.config._reparse([tmpdir, '-f', '-n3', '--dist', '--exec=x', '--collectonly']) - assert config._getsessionname() == 'Session' + assert config._getestdirname() == 'Session' - def test_sessionname_lookup_custom(self, fstester): - fstester.makepyfile(conftest=""" + def test_sessionname_lookup_custom(self, testdir): + testdir.makepyfile(conftest=""" from py.__.test.session import Session class MySession(Session): pass """) - config = fstester.parseconfig("--session=MySession", fstester.tmpdir) + config = testdir.parseconfig("--session=MySession", testdir.tmpdir) session = config.initsession() assert session.__class__.__name__ == 'MySession' @@ -226,9 +226,9 @@ session = config.initsession() assert session.config is config - def test_boxed_option_default(self, fstester): - fstester.makepyfile(conftest="dist_hosts=[]") - tmpdir = fstester.tmpdir.ensure("subdir", dir=1) + def test_boxed_option_default(self, testdir): + testdir.makepyfile(conftest="dist_hosts=[]") + tmpdir = testdir.tmpdir.ensure("subdir", dir=1) config = py.test.config._reparse([tmpdir]) config.initsession() assert not config.option.boxed @@ -236,9 +236,9 @@ config.initsession() assert not config.option.boxed - def test_boxed_option_from_conftest(self, fstester): - fstester.makepyfile(conftest="dist_hosts=[]") - tmpdir = fstester.tmpdir.ensure("subdir", dir=1) + def test_boxed_option_from_conftest(self, testdir): + testdir.makepyfile(conftest="dist_hosts=[]") + tmpdir = testdir.tmpdir.ensure("subdir", dir=1) tmpdir.join("conftest.py").write(py.code.Source(""" dist_hosts = [] dist_boxed = True @@ -247,9 +247,9 @@ config.initsession() assert config.option.boxed - def test_boxed_option_from_conftest(self, fstester): - fstester.makepyfile(conftest="dist_boxed=False") - config = py.test.config._reparse([fstester.tmpdir, '--box']) + def test_boxed_option_from_conftest(self, testdir): + testdir.makepyfile(conftest="dist_boxed=False") + config = py.test.config._reparse([testdir.tmpdir, '--box']) assert config.option.boxed config.initsession() assert config.option.boxed @@ -270,10 +270,10 @@ pl = config.getvalue_pathlist('mypathlist') assert pl == [py.path.local()] - def test_config_iocapturing(self, fstester): - config = fstester.parseconfig(fstester.tmpdir) + def test_config_iocapturing(self, testdir): + config = testdir.parseconfig(testdir.tmpdir) assert config.getvalue("conf_iocapture") - tmpdir = fstester.tmpdir.ensure("sub-with-conftest", dir=1) + tmpdir = testdir.tmpdir.ensure("sub-with-conftest", dir=1) tmpdir.join("conftest.py").write(py.code.Source(""" conf_iocapture = "no" """)) @@ -309,13 +309,13 @@ opts = spec.split() yield check_conflict_option, opts - def test_default_session_options(self, fstester): + def test_default_session_options(self, testdir): def runfiletest(opts): - sorter = fstester.inline_run(*opts) + sorter = testdir.inline_run(*opts) passed, skipped, failed = sorter.countoutcomes() assert failed == 2 assert skipped == passed == 0 - path = fstester.makepyfile(""" + path = testdir.makepyfile(""" def test_f1(): assert 0 def test_f2(): assert 0 """) @@ -325,8 +325,8 @@ ['--traceconfig'], ['-v'], ['-v', '-v']): runfiletest(opts + [path]) - def test_is_not_boxed_by_default(self, fstester): - config = py.test.config._reparse([fstester.tmpdir]) + def test_is_not_boxed_by_default(self, testdir): + config = py.test.config._reparse([testdir.tmpdir]) assert not config.option.boxed Modified: py/branch/pytestplugin/py/test/testing/test_deprecated_api.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_deprecated_api.py (original) +++ py/branch/pytestplugin/py/test/testing/test_deprecated_api.py Wed Feb 18 15:53:47 2009 @@ -2,16 +2,16 @@ import py class TestCollectDeprecated: - def test_directory_run_join_warnings(self, fstester): - p = fstester.makepyfile(test_one="") - config = fstester.parseconfig(p) + def test_directory_run_join_warnings(self, testdir): + p = testdir.makepyfile(test_one="") + config = testdir.parseconfig(p) dirnode = config.getfsnode(p.dirpath()) py.test.deprecated_call(dirnode.run) # XXX for directories we still have join() #py.test.deprecated_call(dirnode.join, 'test_one') - def test_collect_with_deprecated_run_and_join(self, fstester): - fstester.makepyfile(conftest=""" + def test_collect_with_deprecated_run_and_join(self, testdir): + testdir.makepyfile(conftest=""" import py class MyInstance(py.test.collect.Instance): @@ -45,12 +45,12 @@ return self.Module(self.fspath.join(name), parent=self) Directory = MyDirectory """) - p = fstester.makepyfile(somefile=""" + p = testdir.makepyfile(somefile=""" def check(): pass class Cls: def check2(self): pass """) - config = fstester.parseconfig() + config = testdir.parseconfig() dirnode = config.getfsnode(p.dirpath()) colitems = py.test.deprecated_call(dirnode.collect) assert len(colitems) == 1 @@ -68,9 +68,8 @@ assert len(colitems) == 1 assert colitems[0].name == 'check2' - def test_collect_with_deprecated_join_but_no_run(self, tsession): - fstester = tsession.fstester - fstester.makepyfile(conftest=""" + def test_collect_with_deprecated_join_but_no_run(self, testdir): + testdir.makepyfile(conftest=""" import py class Module(py.test.collect.Module): @@ -83,7 +82,7 @@ return self.Function(name, parent=self) assert name != "SomeClass", "join should not be called with this name" """) - col = tsession.getmodulecol(""" + col = testdir.getmodulecol(""" def somefunc(): pass def check_one(): pass class SomeClass: pass @@ -93,53 +92,51 @@ funcitem = colitems[0] assert funcitem.name == "check_one" - def test_function_custom_run(self, tsession): - fstester = tsession.fstester - fstester.makepyfile(conftest=""" + def test_function_custom_run(self, testdir): + testdir.makepyfile(conftest=""" import py class MyFunction(py.test.collect.Function): def run(self): pass Function=MyFunction """) - modcol = tsession.getmodulecol("def test_func(): pass") + modcol = testdir.getmodulecol("def test_func(): pass") funcitem = modcol.collect()[0] assert funcitem.name == 'test_func' py.test.deprecated_call(funcitem.runtest) - def test_function_custom_execute(self, tsession): - fstester = tsession.fstester - fstester.makepyfile(conftest=""" + def test_function_custom_execute(self, testdir): + testdir.makepyfile(conftest=""" import py class MyFunction(py.test.collect.Function): def execute(self, obj, *args): pass Function=MyFunction """) - modcol = tsession.getmodulecol("def test_func(): pass") + modcol = testdir.getmodulecol("def test_func(): pass") funcitem = modcol.collect()[0] assert funcitem.name == 'test_func' py.test.deprecated_call(funcitem.runtest) - def test_function_deprecated_run_execute(self, tsession): - modcol = tsession.getmodulecol("def test_some(): pass") + def test_function_deprecated_run_execute(self, testdir): + modcol = testdir.getmodulecol("def test_some(): pass") funcitem = modcol.collect()[0] py.test.deprecated_call(funcitem.run) py.test.deprecated_call(funcitem.execute, funcitem.obj) - def test_function_deprecated_run_recursive(self, tsession): - tsession.fstester.makepyfile(conftest=""" + def test_function_deprecated_run_recursive(self, testdir): + testdir.makepyfile(conftest=""" import py class Module(py.test.collect.Module): def run(self): return super(Module, self).run() """) - modcol = tsession.getmodulecol("def test_some(): pass") + modcol = testdir.getmodulecol("def test_some(): pass") colitems = py.test.deprecated_call(modcol.collect) funcitem = colitems[0] - def test_conftest_subclasses_Module_with_non_pyfile(self, fstester): - fstester.makepyfile(conftest=""" + def test_conftest_subclasses_Module_with_non_pyfile(self, testdir): + testdir.makepyfile(conftest=""" import py class Module(py.test.collect.Module): def run(self): @@ -150,7 +147,7 @@ return Module(path, parent=self) return super(Directory, self).consider_file(path) """) - testme = fstester.makefile('xxx', testme="hello") - config = fstester.parseconfig(testme) + testme = testdir.makefile('xxx', testme="hello") + config = testdir.parseconfig(testme) col = config.getfsnode(testme) assert col.collect() == [] Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Wed Feb 18 15:53:47 2009 @@ -78,10 +78,10 @@ assert mod is py.__.test.plugin.pytest_terminal class TestPluginManager: - def test_importplugin(self, fstester): + def test_importplugin(self, testdir): mod = py.std.new.module("x") mod.pytest_plugins = "pytest_a" - aplugin = fstester.makepyfile(pytest_a="""class A: pass""") + aplugin = testdir.makepyfile(pytest_a="""class A: pass""") pm = PytestPluginManager() l = [] pm.pm._bus.subscribe(pluginregistered=l.append) @@ -306,8 +306,8 @@ else: os.environ[KEY] = old - def test_conftest_specifies_plugin(self, fstester): - fstester.makepyfile( + def test_conftest_specifies_plugin(self, testdir): + testdir.makepyfile( conftest=""" import py class MyPlugin: @@ -319,7 +319,7 @@ pytest_plugins = MyPlugin """ ) - result = fstester.runpytest(fstester.tmpdir, '-h') + result = testdir.runpytest(testdir.tmpdir, '-h') result.stdout.fnmatch_lines([ "*--myplugin-option*", ]) From hpk at codespeak.net Wed Feb 18 16:03:58 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 16:03:58 +0100 (CET) Subject: [py-svn] r62009 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090218150358.8F7A01684B6@codespeak.net> Author: hpk Date: Wed Feb 18 16:03:57 2009 New Revision: 62009 Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Log: some fixes forgotten with last checkin Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Wed Feb 18 16:03:57 2009 @@ -25,11 +25,11 @@ """ called after test function finished execution""" class PluginTester(Support): - def testdir(self, cmdlineargs=()): + def testdir(self): # XXX import differently, eg. # FSTester = self.pyfuncitem._config.pluginmanager.getpluginattr("pytester", "FSTester") - from pytest_pytester import FSTester - crunner = FSTester(self.pyfuncitem) + from pytest_pytester import TmpTestdir + crunner = TmpTestdir(self.pyfuncitem) # #crunner.ensureplugin(impname) return crunner Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 18 16:03:57 2009 @@ -13,7 +13,7 @@ self.stdout = LineMatcher(outlines) self.stderr = LineMatcher(errlines) -class Testdir: +class TmpTestdir: def __init__(self, pyfuncitem): self.pyfuncitem = pyfuncitem self.tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) @@ -51,6 +51,9 @@ if not impname in self._plugins: self._plugins.append(impname) + def genitems(self, colitems): + return list(self.session.genitems(colitems)) + def inline_genitems(self, *args): config = self.parseconfig(*args) session = config.initsession() @@ -86,7 +89,6 @@ self.session = self.config.initsession() return self.config.getfsnode(path) - def prepare(self): p = self.tmpdir.join("conftest.py") if not p.check(): @@ -329,7 +331,7 @@ elif argname == "LineMatcher": return LineMatcher, None elif argname == "testdir": - testdir = Testdir(pyfuncitem) + testdir = TmpTestdir(pyfuncitem) return testdir, None elif argname == "EventRecorder": return EventRecorder, None # or do some finalization From hpk at codespeak.net Wed Feb 18 17:26:22 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 17:26:22 +0100 (CET) Subject: [py-svn] r62012 - in py/branch/pytestplugin/py/test: dsession/testing looponfail/testing plugin testing Message-ID: <20090218162622.340561684F7@codespeak.net> Author: hpk Date: Wed Feb 18 17:26:19 2009 New Revision: 62012 Modified: py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/testing/test_doctest.py py/branch/pytestplugin/py/test/testing/test_event.py py/branch/pytestplugin/py/test/testing/test_recording.py py/branch/pytestplugin/py/test/testing/test_runner_functional.py py/branch/pytestplugin/py/test/testing/test_session.py py/branch/pytestplugin/py/test/testing/test_setup_nested.py Log: fixed some more - only two files with old style setup and suptest remain ... Modified: py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py Wed Feb 18 17:26:19 2009 @@ -6,7 +6,6 @@ from py.__.test import event from py.__.test.dsession.dsession import DSession from py.__.test.dsession.hostmanage import HostManager, Host -from py.__.test.testing import suptest import os def eventreader(session): @@ -27,41 +26,41 @@ events.append(ev) return readevent -class TestAsyncFunctional(suptest.InlineCollection): - def test_dist_no_disthost(self): - config = self.parseconfig(self.tmpdir, '-d') +class TestAsyncFunctional: + def test_dist_no_disthost(self, testdir): + config = testdir.parseconfig(testdir.tmpdir, '-d') py.test.raises(SystemExit, "config.initsession()") - def test_conftest_options(self): - self.makepyfile(conftest=""" + def test_conftest_options(self, testdir): + testdir.makepyfile(conftest=""" print "importing conftest" import py Option = py.test.config.Option option = py.test.config.addoptions("someopt", Option('', '--forcegen', action="store_true", dest="forcegen", default=False)) - """) - self.makepyfile(__init__="#") - p1 = self.makepyfile(test_one=""" + """, + ) + p1 = testdir.makepyfile(""" def test_1(): import py, conftest print "test_1: py.test.config.option.forcegen", py.test.config.option.forcegen print "test_1: conftest", conftest print "test_1: conftest.option.forcegen", conftest.option.forcegen assert conftest.option.forcegen - """) + """, __init__="#") print p1 - config = self.parseconfig('-n1', p1, '--forcegen') + config = testdir.parseconfig('-n1', p1, '--forcegen') dsession = DSession(config) readevent = eventreader(dsession) dsession.main() ev = readevent(event.ItemTestReport) if not ev.passed: - print ev.outcome.longrepr + print ev assert 0 - def test_dist_some_tests(self): - self.makepyfile(conftest="dist_hosts=['localhost']\n") - p1 = self.makepyfile(test_one=""" + def test_dist_some_tests(self, testdir): + testdir.makepyfile(conftest="dist_hosts=['localhost']\n") + p1 = testdir.makepyfile(test_one=""" def test_1(): pass def test_x(): @@ -70,7 +69,7 @@ def test_fail(): assert 0 """) - config = self.parseconfig('-d', p1) + config = testdir.parseconfig('-d', p1) dsession = DSession(config) readevent = eventreader(dsession) dsession.main([config.getfsnode(p1)]) @@ -85,7 +84,7 @@ assert ev.host.hostname == "localhost" ev = readevent(event.TestrunFinish) - def test_distribution_rsync_roots_example(self): + def test_distribution_rsync_roots_example(self, testdir): py.test.skip("testing for root rsync needs rework") destdir = py.test.ensuretemp("example_dist_destdir") subdir = "sub_example_dist" @@ -115,28 +114,26 @@ assert config.topdir == tmpdir assert not tmpdir.join("__init__.py").check() dist = DSession(config) - sorter = suptest.events_from_session(dist) + sorter = testdir.inline_runsession(dist) testevents = sorter.get(event.ItemTestReport) assert len([x for x in testevents if x.passed]) == 2 assert len([x for x in testevents if x.failed]) == 3 assert len([x for x in testevents if x.skipped]) == 0 - def test_nice_level(self): + def test_nice_level(self, testdir): """ Tests if nice level behaviour is ok """ if not hasattr(os, 'nice'): py.test.skip("no os.nice() available") - self.makepyfile(conftest=""" + testdir.makepyfile(conftest=""" dist_hosts=['localhost'] dist_nicelevel = 10 """) - p1 = self.makepyfile(test_nice=""" + p1 = testdir.makepyfile(""" def test_nice(): import os assert os.nice(0) == 10 """) - config = self.parseconfig('-d', p1) - session = config.initsession() - events = suptest.events_from_session(session) - ev = events.getreport('test_nice') + evrec = testdir.inline_run('-d', p1) + ev = evrec.getreport('test_nice') assert ev.passed Modified: py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py (original) +++ py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py Wed Feb 18 17:26:19 2009 @@ -1,17 +1,16 @@ import py -from py.__.test.testing import suptest from py.__.test.looponfail.remote import LooponfailingSession, LoopState, RemoteControl -class TestRemoteControl(suptest.InlineCollection): - def test_nofailures(self): - item = self.getitem("def test_func(): pass\n") +class TestRemoteControl: + def test_nofailures(self, testdir): + item = testdir.getitem("def test_func(): pass\n") control = RemoteControl(item._config) control.setup() failures = control.runsession() assert not failures - def test_failures(self): - item = self.getitem("def test_func(): assert 0\n") + def test_failures(self, testdir): + item = testdir.getitem("def test_func(): assert 0\n") control = RemoteControl(item._config) control.setup() failures = control.runsession() @@ -22,8 +21,8 @@ failures = control.runsession(failures) assert not failures - def test_failure_change(self): - modcol = self.getitem(""" + def test_failure_change(self, testdir): + modcol = testdir.getitem(""" def test_func(): assert 0 """) @@ -46,9 +45,9 @@ assert failures assert str(failures).find("test_new") != -1 -class TestLooponFailing(suptest.InlineCollection): - def test_looponfailing_from_fail_to_ok(self): - modcol = self.getmodulecol(""" +class TestLooponFailing: + def test_looponfailing_from_fail_to_ok(self, testdir): + modcol = testdir.getmodulecol(""" def test_one(): x = 0 assert x == 1 @@ -72,8 +71,8 @@ session.loop_once(loopstate) assert not loopstate.colitems - def test_looponfailing_from_one_to_two_tests(self): - modcol = self.getmodulecol(""" + def test_looponfailing_from_one_to_two_tests(self, testdir): + modcol = testdir.getmodulecol(""" def test_one(): assert 0 """) @@ -97,8 +96,8 @@ session.loop_once(loopstate) assert len(loopstate.colitems) == 1 - def test_looponfailing_removed_test(self): - modcol = self.getmodulecol(""" + def test_looponfailing_removed_test(self, testdir): + modcol = testdir.getmodulecol(""" def test_one(): assert 0 def test_two(): Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 18 17:26:19 2009 @@ -16,7 +16,11 @@ class TmpTestdir: def __init__(self, pyfuncitem): self.pyfuncitem = pyfuncitem - self.tmpdir = py.test.ensuretemp("_".join(pyfuncitem.listnames())) + name = pyfuncitem.getmodpath(stopatmodule=False) + #name = "_".join(pyfuncitem.getmodpath()) # listnames()) + name = name.replace("()", "_") + name = name.replace(".", "_") + self.tmpdir = py.test.ensuretemp(name) self._plugins = [] def _makefile(self, ext, args, kwargs): @@ -62,6 +66,31 @@ items = list(session.genitems(colitems)) return items, rec + def runitem(self, source, **runnerargs): + # used from runner functional + item = self.getitem(source) + # XXX + runner = self.pyfuncitem.obj.im_self.getrunner() + return runner(item, **runnerargs) + + #evrec = self.inline_runsource(source, *cmdlineargs) + #flist = evrec.getnamed("itemtestreport") + #assert len(flist) == 1 + #return flist[0] + + def inline_runsource(self, source, *cmdlineargs): + p = self.makepyfile(source) + l = list(cmdlineargs) + [p] + return self.inline_run(*l) + + def inline_runsession(self, session): + config = session.config + config.pluginmanager.configure(config) + sorter = EventRecorder(config.bus) + session.main() + config.pluginmanager.unconfigure(config) + return sorter + def inline_run(self, *args): config = self.parseconfig(*args) config.pluginmanager.configure(config) @@ -80,6 +109,11 @@ assert item is not None, "%r item not found in module:\n%s" %(funcname, source) return item + def getfscol(self, path, configargs=()): + self.config = self.parseconfig(path, *configargs) + self.session = self.config.initsession() + return self.config.getfsnode(path) + def getmodulecol(self, source, configargs=(), withinit=False): kw = {self.pyfuncitem.name: py.code.Source(source).strip()} path = self.makepyfile(**kw) Modified: py/branch/pytestplugin/py/test/testing/test_doctest.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_doctest.py (original) +++ py/branch/pytestplugin/py/test/testing/test_doctest.py Wed Feb 18 17:26:19 2009 @@ -1,34 +1,28 @@ import py from py.__.test.outcome import Failed -from py.__.test.testing.suptest import InlineCollection -def setup_module(mod): - mod.tmp = py.test.ensuretemp(__name__) - -class TestDoctests(InlineCollection): - def test_simple_docteststring(self): - txtfile = self.maketxtfile(test_doc=""" +class TestDoctests: + def test_simple_docteststring(self, testdir): + txtfile = testdir.maketxtfile(test_doc=""" >>> i = 0 >>> i + 1 1 """) - config = self.parseconfig(txtfile) - col = config.getfsnode(txtfile) + col = testdir.getfscol(path=txtfile) testitem = col.join(txtfile.basename) res = testitem.runtest() assert res is None - - def test_doctest_unexpected_exception(self): - py.test.skip("implement nice doctest repr for unexpected exceptions") - p = tmp.join("test_doctest_unexpected_exception") - p.write(py.code.Source(""" + @py.test.keywords(xfail=True) + def test_doctest_unexpected_exception(self, testdir): + p = testdir.maketxtfile(""" >>> i = 0 >>> x 2 - """)) - testitem = py.test.collect.DoctestFile(p).join(p.basename) + """) + col = testdir.getfscol(p) + testitem = col.join(p.basename) excinfo = py.test.raises(Failed, "testitem.runtest()") repr = testitem.repr_failure(excinfo, ("", "")) assert repr.reprlocation Modified: py/branch/pytestplugin/py/test/testing/test_event.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_event.py (original) +++ py/branch/pytestplugin/py/test/testing/test_event.py Wed Feb 18 17:26:19 2009 @@ -1,7 +1,6 @@ import py from py.__.test.event import EventBus from py.__.test import event -import suptest from py.__.code.testing.test_excinfo import TWMock class TestEventBus: @@ -76,17 +75,17 @@ if py.std.inspect.isclass(value) and issubclass(value, event.BaseEvent): assert hasattr(event.BaseEvent, value.__name__) -class TestItemTestReport(suptest.InlineCollection): - def test_excinfo_is_longrepr(self): - modcol = self.getmodulecol("def test_ok(): pass") +class TestItemTestReport: + def test_excinfo_is_longrepr(self, testdir): + modcol = testdir.getmodulecol("def test_ok(): pass") ev = event.ItemTestReport(modcol, excinfo="hello") twmock = TWMock() ev.toterminal(twmock) assert twmock.lines assert twmock.lines[0] == "hello" - def test_toterminal(self): - sorter = suptest.events_from_runsource(""" + def test_toterminal(self, testdir): + sorter = testdir.inline_runsource(""" def test_one(): assert 42 == 43 """) Modified: py/branch/pytestplugin/py/test/testing/test_recording.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_recording.py (original) +++ py/branch/pytestplugin/py/test/testing/test_recording.py Wed Feb 18 17:26:19 2009 @@ -1,12 +1,9 @@ import py,sys py.test.skip("implementation missing: recording") -from py.__.test.testing import suptest -from py.__.test.acceptance_test import AcceptBase - -class TestRecordingAccept(AcceptBase): - def test_recording_and_back(self): - p = self.makepyfile(test_one=""" +class TestRecordingAccept: + def test_recording_and_back(self, testdir): + p = testdir.makepyfile(""" import py def test_fail(): assert x Modified: py/branch/pytestplugin/py/test/testing/test_runner_functional.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_runner_functional.py (original) +++ py/branch/pytestplugin/py/test/testing/test_runner_functional.py Wed Feb 18 17:26:19 2009 @@ -1,12 +1,11 @@ import py -from py.__.test.testing.suptest import InlineCollection, getItemTestReport from py.__.test.runner import basic_run_report, forked_run_report, basic_collect_report from py.__.test.runner import RobustRun from py.__.code.excinfo import ReprExceptionInfo -class BaseTests(InlineCollection): - def test_getwkargs_function(self): - ev, fn = getItemTestReport(""" +class BaseTests: + def test_getwkargs_function(self, testdir): + evrec = testdir.inline_runsource(""" import py provided = [] @@ -26,10 +25,11 @@ assert item._argfinalizers assert len(provided) == 1 """) + ev, = evrec.getnamed("itemtestreport") assert ev.passed, ev.longrepr - def test_getwkargs_boundmethod(self): - ev, fn = getItemTestReport(""" + def test_getwkargs_boundmethod(self, testdir): + evrec = testdir.inline_runsource(""" import py provided = [] @@ -45,10 +45,11 @@ def test_func(self, hello): assert hello == "world" """) + ev, = evrec.getnamed("itemtestreport") assert ev.passed, ev.longrepr - def test_funcattr(self): - ev = self.runitem(""" + def test_funcattr(self, testdir): + ev = testdir.runitem(""" import py @py.test.keywords(xfail="needs refactoring") def test_func(): @@ -56,8 +57,8 @@ """) assert ev.keywords['xfail'] == "needs refactoring" - def test_passfunction(self): - ev = self.runitem(""" + def test_passfunction(self, testdir): + ev = testdir.runitem(""" def test_func(): pass """) @@ -66,8 +67,8 @@ assert ev.shortrepr == "." assert not hasattr(ev, 'longrepr') - def test_failfunction(self): - ev = self.runitem(""" + def test_failfunction(self, testdir): + ev = testdir.runitem(""" def test_func(): assert 0 """) @@ -78,8 +79,8 @@ assert isinstance(ev.longrepr, ReprExceptionInfo) assert str(ev.shortrepr) == "F" - def test_skipfunction(self): - ev = self.runitem(""" + def test_skipfunction(self, testdir): + ev = testdir.runitem(""" import py def test_func(): py.test.skip("hello") @@ -94,8 +95,8 @@ #assert ev.skipped.location.path #assert not ev.skipped.failurerepr - def test_skip_in_setup_function(self): - ev = self.runitem(""" + def test_skip_in_setup_function(self, testdir): + ev = testdir.runitem(""" import py def setup_function(func): py.test.skip("hello") @@ -110,8 +111,8 @@ #assert ev.skipped.location.lineno == 3 #assert ev.skipped.location.lineno == 3 - def test_failure_in_setup_function(self): - ev = self.runitem(""" + def test_failure_in_setup_function(self, testdir): + ev = testdir.runitem(""" import py def setup_function(func): raise ValueError(42) @@ -124,8 +125,8 @@ assert ev.failed assert ev.when == "setup" - def test_failure_in_teardown_function(self): - ev = self.runitem(""" + def test_failure_in_teardown_function(self, testdir): + ev = testdir.runitem(""" import py def teardown_function(func): raise ValueError(42) @@ -140,14 +141,14 @@ assert ev.longrepr.reprcrash.lineno == 3 assert ev.longrepr.reprtraceback.reprentries - def test_custom_failure_repr(self): - self.makepyfile(conftest=""" + def test_custom_failure_repr(self, testdir): + testdir.makepyfile(conftest=""" import py class Function(py.test.collect.Function): def repr_failure(self, excinfo, outerr): return "hello" """) - ev = self.runitem(""" + ev = testdir.runitem(""" import py def test_func(): assert 0 @@ -160,14 +161,14 @@ #assert ev.failed.where.path.basename == "test_func.py" #assert ev.failed.failurerepr == "hello" - def test_failure_in_setup_function_ignores_custom_failure_repr(self): - self.makepyfile(conftest=""" + def test_failure_in_setup_function_ignores_custom_failure_repr(self, testdir): + testdir.makepyfile(conftest=""" import py class Function(py.test.collect.Function): def repr_failure(self, excinfo): assert 0 """) - ev = self.runitem(""" + ev = testdir.runitem(""" import py def setup_function(func): raise ValueError(42) @@ -183,8 +184,8 @@ #assert ev.outcome.where.path.basename == "test_func.py" #assert instanace(ev.failed.failurerepr, PythonFailureRepr) - def test_capture_in_func(self): - ev = self.runitem(""" + def test_capture_in_func(self, testdir): + ev = testdir.runitem(""" import py def setup_function(func): print >>py.std.sys.stderr, "in setup" @@ -199,9 +200,9 @@ # assert out == ['in function\nin teardown\n'] # assert err == ['in setup\n'] - def test_systemexit_does_not_bail_out(self): + def test_systemexit_does_not_bail_out(self, testdir): try: - ev = self.runitem(""" + ev = testdir.runitem(""" def test_func(): raise SystemExit(42) """) @@ -210,10 +211,10 @@ assert ev.failed assert ev.when == "execute" - def test_exit_propagates(self): + def test_exit_propagates(self, testdir): from py.__.test.outcome import Exit try: - self.runitem(""" + testdir.runitem(""" from py.__.test.outcome import Exit def test_func(): raise Exit() @@ -228,10 +229,10 @@ def getrunner(self): return basic_run_report - def test_keyboardinterrupt_propagates(self): + def test_keyboardinterrupt_propagates(self, testdir): from py.__.test.outcome import Exit try: - self.runitem(""" + testdir.runitem(""" def test_func(): raise KeyboardInterrupt("fake") """) @@ -240,9 +241,9 @@ else: py.test.fail("did not raise") - def test_pdb_on_fail(self): + def test_pdb_on_fail(self, testdir): l = [] - ev = self.runitem(""" + ev = testdir.runitem(""" def test_func(): assert 0 """, pdb=l.append) @@ -250,9 +251,9 @@ assert ev.when == "execute" assert len(l) == 1 - def test_pdb_on_skip(self): + def test_pdb_on_skip(self, testdir): l = [] - ev = self.runitem(""" + ev = testdir.runitem(""" import py def test_func(): py.test.skip("hello") @@ -266,8 +267,8 @@ py.test.skip("no os.fork available") return forked_run_report - def test_suicide(self): - ev = self.runitem(""" + def test_suicide(self, testdir): + ev = testdir.runitem(""" def test_func(): import os os.kill(os.getpid(), 15) @@ -275,9 +276,9 @@ assert ev.failed assert ev.when == "???" -class TestCollectionEvent(InlineCollection): - def test_collect_result(self): - col = self.getmodulecol(""" +class TestCollectionEvent: + def test_collect_result(self, testdir): + col = testdir.getmodulecol(""" def test_func1(): pass class TestClass: @@ -292,8 +293,8 @@ assert res[0].name == "test_func1" assert res[1].name == "TestClass" - def test_skip_at_module_scope(self): - col = self.getmodulecol(""" + def test_skip_at_module_scope(self, testdir): + col = testdir.getmodulecol(""" import py py.test.skip("hello") def test_func(): @@ -305,9 +306,9 @@ assert ev.skipped -class TestRunnerRepr(InlineCollection): - def test_runner_repr(self): - item = self.getitem("def test_func(): pass") +class TestRunnerRepr: + def test_runner_repr(self, testdir): + item = testdir.getitem("def test_func(): pass") robustrun = RobustRun(item) r = repr(robustrun) assert r Modified: py/branch/pytestplugin/py/test/testing/test_session.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_session.py (original) +++ py/branch/pytestplugin/py/test/testing/test_session.py Wed Feb 18 17:26:19 2009 @@ -1,47 +1,43 @@ import py from py.__.test import event -from py.__.test.testing import suptest -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(mod.__name__) - -class TestKeywordSelection(suptest.InlineSession): - def test_select_simple(self): - file_test = self.makepyfile(file_test=""" +class TestKeywordSelection: + def test_select_simple(self, testdir): + file_test = testdir.makepyfile(""" def test_one(): assert 0 class TestClass(object): def test_method_one(self): assert 42 == 43 """) def check(keyword, name): - sorter = self.inline_run("-s", "-k", keyword, file_test) + sorter = testdir.inline_run("-s", "-k", keyword, file_test) passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 1 assert failed[0].colitem.name == name assert len(sorter.get(event.Deselected)) == 1 for keyword in ['test_one', 'est_on']: - yield check, keyword, 'test_one' - yield check, 'TestClass.test', 'test_method_one' + #yield check, keyword, 'test_one' + check(keyword, 'test_one') + check('TestClass.test', 'test_method_one') - def test_select_extra_keywords(self): - o = self.tmpdir - tfile = o.join('test_select.py').write(py.code.Source(""" + def test_select_extra_keywords(self, testdir): + p = testdir.makepyfile(test_select=""" def test_1(): pass class TestClass: def test_2(self): pass - """)) - conftest = o.join('conftest.py').write(py.code.Source(""" + """) + testdir.makepyfile(conftest=""" import py class Class(py.test.collect.Class): def _keywords(self): return ['xxx', self.name] - """)) + """) for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', 'TestClass test_2', 'xxx TestClass test_2',): - sorter = suptest.events_from_cmdline([o, '-s', '-k', keyword]) + sorter = testdir.inline_run(p.dirpath(), '-s', '-k', keyword) print "keyword", repr(keyword) passed, skipped, failed = sorter.listoutcomes() assert len(passed) == 1 @@ -50,13 +46,13 @@ assert len(dlist) == 1 assert dlist[0].items[0].name == 'test_1' - def test_select_starton(self): - threepass = self.makepyfile(test_threepass=""" + def test_select_starton(self, testdir): + threepass = testdir.makepyfile(test_threepass=""" def test_one(): assert 1 def test_two(): assert 1 def test_three(): assert 1 """) - sorter = self.inline_run("-k", "test_two:", threepass) + sorter = testdir.inline_run("-k", "test_two:", threepass) passed, skipped, failed = sorter.listoutcomes() assert len(passed) == 2 assert not failed @@ -65,27 +61,9 @@ item = dlist[0].items[0] assert item.name == "test_one" -class SessionTests(suptest.InlineCollection): - def events_from_cmdline(self, *args): - paths = [p for p in args if isinstance(p, py.path.local)] - if not paths: - args = (self.tmpdir,) + args - config = self.parseconfig(*args) - self.session = config.initsession() - self.sorter = suptest.EventSorter(config, self.session) - self.session.main() - return self.sorter - - def events_from_runsource(self, source, *args): - p = self.makepyfile(test_source=source) - return self.events_from_cmdline(p, *args) - - def makepyfile(self, *args, **kw): - self.tmpdir.ensure('__init__.py') - return super(SessionTests, self).makepyfile(*args, **kw) - - def test_basic_testitem_events(self): - tfile = self.makepyfile(test_one=""" +class SessionTests: + def test_basic_testitem_events(self, testdir): + tfile = testdir.makepyfile(""" def test_one(): pass def test_one_one(): @@ -95,7 +73,7 @@ def test_two(someargs): pass """) - sorter = self.events_from_cmdline(tfile) + sorter = testdir.inline_run(tfile) passed, skipped, failed = sorter.listoutcomes() assert len(skipped) == 0 assert len(passed) == 1 @@ -110,8 +88,8 @@ col = colstarted[0].collector assert isinstance(col, py.test.collect.Module) - def test_nested_import_error(self): - tfile = self.makepyfile(test_one=""" + def test_nested_import_error(self, testdir): + tfile = testdir.makepyfile(""" import import_fails def test_this(): assert import_fails.a == 1 @@ -119,19 +97,18 @@ import does_not_work a = 1 """) - sorter = self.events_from_cmdline() + sorter = testdir.inline_run(tfile) l = sorter.getfailedcollections() assert len(l) == 1 out = l[0].longrepr.reprcrash.message assert out.find('does_not_work') != -1 - def test_raises_output(self): - self.makepyfile(test_one=""" + def test_raises_output(self, testdir): + sorter = testdir.inline_runsource(""" import py def test_raises_doesnt(): py.test.raises(ValueError, int, "3") """) - sorter = self.events_from_cmdline() passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 1 out = failed[0].longrepr.reprcrash.message @@ -139,8 +116,8 @@ print out py.test.fail("incorrect raises() output") - def test_generator_yields_None(self): - sorter = self.events_from_runsource(""" + def test_generator_yields_None(self, testdir): + sorter = testdir.inline_runsource(""" def test_1(): yield None """) @@ -149,15 +126,15 @@ i = out.find('TypeError') assert i != -1 - def test_syntax_error_module(self): - sorter = self.events_from_runsource("this is really not python") + def test_syntax_error_module(self, testdir): + sorter = testdir.inline_runsource("this is really not python") l = sorter.getfailedcollections() assert len(l) == 1 out = l[0].longrepr.reprcrash.message assert out.find(str('not python')) != -1 - def test_exit_first_problem(self): - sorter = self.events_from_runsource(""" + def test_exit_first_problem(self, testdir): + sorter = testdir.inline_runsource(""" def test_one(): assert 0 def test_two(): assert 0 """, '--exitfirst') @@ -165,8 +142,8 @@ assert failed == 1 assert passed == skipped == 0 - def test_broken_repr(self): - self.makepyfile(test_broken=""" + def test_broken_repr(self, testdir): + p = testdir.makepyfile(""" import py class BrokenRepr1: foo=0 @@ -190,7 +167,7 @@ t = BrokenRepr2() assert t.foo == 1 """) - sorter = self.events_from_cmdline() + sorter = testdir.inline_run(p) passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 2 out = failed[0].longrepr.reprcrash.message @@ -199,9 +176,8 @@ assert (out.find("[unknown exception raised in repr()]") != -1 or out.find("TypeError") != -1) - def test_skip_by_conftest_directory(self): - from py.__.test import outcome - self.makepyfile(conftest=""" + def test_skip_by_conftest_directory(self, testdir): + testdir.makepyfile(conftest=""" import py class Directory(py.test.collect.Directory): def collect(self): @@ -209,14 +185,14 @@ """, test_file=""" def test_one(): pass """) - sorter = self.events_from_cmdline() + sorter = testdir.inline_run(testdir.tmpdir) skips = sorter.get(event.CollectionReport) assert len(skips) == 1 assert skips[0].skipped class TestNewSession(SessionTests): - def test_pdb_run(self): - tfile = self.makepyfile(test_one=""" + def test_pdb_run(self, testdir): + tfile = testdir.makepyfile(""" def test_usepdb(): assert 0 """) @@ -225,7 +201,7 @@ l.append(args) py.magic.patch(py.__.test.custompdb, 'post_mortem', mypdb) try: - sorter = self.events_from_cmdline('--pdb') + sorter = testdir.inline_run('--pdb', tfile) finally: py.magic.revert(py.__.test.custompdb, 'post_mortem') rep = sorter.getreport("test_usepdb") @@ -234,8 +210,8 @@ tb = py.code.Traceback(l[0][0]) assert tb[-1].name == "test_usepdb" - def test_order_of_execution(self): - sorter = self.events_from_runsource(""" + def test_order_of_execution(self, testdir): + sorter = testdir.inline_runsource(""" l = [] def test_1(): l.append(1) @@ -259,8 +235,8 @@ assert passed == 7 # also test listnames() here ... - def test_collect_only_with_various_situations(self): - p = self.makepyfile( + def test_collect_only_with_various_situations(self, testdir): + p = testdir.makepyfile( test_one=""" def test_one(): raise ValueError() @@ -276,9 +252,10 @@ import py py.test.skip('xxx') """, - test_three="xxxdsadsadsadsa" + test_three="xxxdsadsadsadsa", + __init__="" ) - sorter = self.events_from_cmdline('--collectonly') + sorter = testdir.inline_run('--collectonly', p.dirpath()) itemstarted = sorter.get(event.ItemStart) assert len(itemstarted) == 3 Modified: py/branch/pytestplugin/py/test/testing/test_setup_nested.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_setup_nested.py (original) +++ py/branch/pytestplugin/py/test/testing/test_setup_nested.py Wed Feb 18 17:26:19 2009 @@ -2,11 +2,8 @@ # test correct setup/teardowns at # module, class, and instance level -import py -import suptest - -def test_module_and_function_setup(): - sorter = suptest.events_from_runsource(""" +def test_module_and_function_setup(testdir): + sorter = testdir.inline_runsource(""" modlevel = [] def setup_module(module): assert not modlevel @@ -35,8 +32,8 @@ rep = sorter.getreport("test_module") assert rep.passed -def test_class_setup(): - sorter = suptest.events_from_runsource(""" +def test_class_setup(testdir): + sorter = testdir.inline_runsource(""" class TestSimpleClassSetup: clslevel = [] def setup_class(cls): @@ -58,8 +55,8 @@ """) sorter.assertoutcome(passed=1+2+1) -def test_method_setup(): - sorter = suptest.events_from_runsource(""" +def test_method_setup(testdir): + sorter = testdir.inline_runsource(""" class TestSetupMethod: def setup_method(self, meth): self.methsetup = meth @@ -74,8 +71,8 @@ """) sorter.assertoutcome(passed=2) -def test_method_generator_setup(): - sorter = suptest.events_from_runsource(""" +def test_method_generator_setup(testdir): + sorter = testdir.inline_runsource(""" class TestSetupTeardownOnInstance: def setup_class(cls): cls.classsetup = True @@ -96,8 +93,8 @@ """) sorter.assertoutcome(passed=1, failed=1) -def test_func_generator_setup(): - sorter = suptest.events_from_runsource(""" +def test_func_generator_setup(testdir): + sorter = testdir.inline_runsource(""" import sys def setup_module(mod): @@ -124,8 +121,8 @@ rep = sorter.getreport("test_one") assert rep.passed -def test_method_setup_uses_fresh_instances(): - sorter = suptest.events_from_runsource(""" +def test_method_setup_uses_fresh_instances(testdir): + sorter = testdir.inline_runsource(""" class TestSelfState1: def __init__(self): self.hello = 42 From hpk at codespeak.net Wed Feb 18 18:02:17 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 18:02:17 +0100 (CET) Subject: [py-svn] r62013 - in py/branch/pytestplugin/py/test: . dsession/testing testing Message-ID: <20090218170217.065E4168528@codespeak.net> Author: hpk Date: Wed Feb 18 18:02:15 2009 New Revision: 62013 Modified: py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/testing/test_runner_functional.py Log: * introducing pytest_pyfunc_arg on classes of test methods * porting another file away from suptest Modified: py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py Wed Feb 18 18:02:15 2009 @@ -3,23 +3,15 @@ """ import py -from py.__.test.testing import suptest from py.__.test.dsession.hostmanage import HostRSync, Host, HostManager, gethosts from py.__.test.dsession.hostmanage import sethomedir, gethomedir, getpath_relto_home from py.__.test import event -class TmpWithSourceDest(suptest.FileCreation): - def setup_method(self, method): - super(TmpWithSourceDest, self).setup_method(method) - self.source = self.tmpdir.mkdir("source") - self.dest = self.tmpdir.mkdir("dest") - -class TestHost(suptest.FileCreation): - def _gethostinfo(self, relpath=""): - exampledir = self.tmpdir.join("gethostinfo") +class TestHost: + def _gethostinfo(self, testdir, relpath=""): + exampledir = testdir.mkdir("gethostinfo") if relpath: exampledir = exampledir.join(relpath) - assert not exampledir.check() hostinfo = Host("localhost:%s" % exampledir) return hostinfo @@ -62,8 +54,8 @@ py.test.raises((py.process.cmdexec.Error, IOError, EOFError), host.initgateway) - def test_remote_has_homedir_as_currentdir(self): - host = self._gethostinfo() + def test_remote_has_homedir_as_currentdir(self, testdir): + host = self._gethostinfo(testdir) old = py.path.local.get_temproot().chdir() try: host.initgateway() @@ -125,18 +117,22 @@ res = channel.receive() assert res == host.gw_remotepath -class TestSyncing(TmpWithSourceDest): - def _gethostinfo(self): - hostinfo = Host("localhost:%s" % self.dest) +class TestSyncing: + def _gethostinfo(self, dest): + hostinfo = Host("localhost:%s" % dest) return hostinfo + + def pytest_pyfunc_arg(self, pyfuncitem, argname): + if argname in ("source", "dest"): + return py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir(argname), None - def test_hrsync_filter(self): - self.source.ensure("dir", "file.txt") - self.source.ensure(".svn", "entries") - self.source.ensure(".somedotfile", "moreentries") - self.source.ensure("somedir", "editfile~") - syncer = HostRSync(self.source) - l = list(self.source.visit(rec=syncer.filter, + def test_hrsync_filter(self, source, dest): + source.ensure("dir", "file.txt") + source.ensure(".svn", "entries") + source.ensure(".somedotfile", "moreentries") + source.ensure("somedir", "editfile~") + syncer = HostRSync(source) + l = list(source.visit(rec=syncer.filter, fil=syncer.filter)) assert len(l) == 3 basenames = [x.basename for x in l] @@ -144,10 +140,10 @@ assert 'file.txt' in basenames assert 'somedir' in basenames - def test_hrsync_localhost_inplace(self): + def test_hrsync_localhost_inplace(self, source, dest): h1 = Host("localhost") events = [] - rsync = HostRSync(self.source) + rsync = HostRSync(source) h1.initgateway() rsync.add_target_host(h1, notify=events.append) assert events @@ -161,24 +157,24 @@ if isinstance(x, event.HostRSyncRootReady)] assert len(l) == 1 ev = l[0] - assert ev.root == self.source + assert ev.root == source assert ev.host == h1 - def test_hrsync_one_host(self): - h1 = self._gethostinfo() + def test_hrsync_one_host(self, source, dest): + h1 = self._gethostinfo(dest) finished = [] - rsync = HostRSync(self.source) + rsync = HostRSync(source) h1.initgateway() rsync.add_target_host(h1) - self.source.join("hello.py").write("world") + source.join("hello.py").write("world") rsync.send() - assert self.dest.join("hello.py").check() + assert dest.join("hello.py").check() - def test_hrsync_same_host_twice(self): - h1 = self._gethostinfo() - h2 = self._gethostinfo() + def test_hrsync_same_host_twice(self, source, dest): + h1 = self._gethostinfo(dest) + h2 = self._gethostinfo(dest) finished = [] - rsync = HostRSync(self.source) + rsync = HostRSync(source) l = [] h1.initgateway() h2.initgateway() @@ -187,87 +183,92 @@ res2 = rsync.add_target_host(h2) assert not res2 -class TestHostManager(TmpWithSourceDest): - def gethostmanager(self, dist_hosts, dist_rsync_roots=None): +class TestHostManager: + def pytest_pyfunc_arg(self, pyfuncitem, argname): + if argname in ("source", "dest"): + p = py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir(argname) + return p, None + + def gethostmanager(self, source, dist_hosts, dist_rsync_roots=None): l = ["dist_hosts = %r" % dist_hosts] if dist_rsync_roots: l.append("dist_rsync_roots = %r" % dist_rsync_roots) - self.source.join("conftest.py").write("\n".join(l)) - config = py.test.config._reparse([self.source]) - assert config.topdir == self.source + source.join("conftest.py").write("\n".join(l)) + config = py.test.config._reparse([source]) + assert config.topdir == source session = config.initsession() hm = HostManager(session) assert hm.hosts return hm - def test_hostmanager_custom_hosts(self): - session = py.test.config._reparse([self.source]).initsession() + def test_hostmanager_custom_hosts(self, source, dest): + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, hosts=[1,2,3]) assert hm.hosts == [1,2,3] - def test_hostmanager_init_rsync_topdir(self): - dir2 = self.source.ensure("dir1", "dir2", dir=1) + def test_hostmanager_init_rsync_topdir(self, source, dest): + dir2 = source.ensure("dir1", "dir2", dir=1) dir2.ensure("hello") - hm = self.gethostmanager( - dist_hosts = ["localhost:%s" % self.dest] + hm = self.gethostmanager(source, + dist_hosts = ["localhost:%s" % dest] ) - assert hm.session.config.topdir == self.source + assert hm.session.config.topdir == source hm.init_rsync() - dest = self.dest.join(self.source.basename) + dest = dest.join(source.basename) assert dest.join("dir1").check() assert dest.join("dir1", "dir2").check() assert dest.join("dir1", "dir2", 'hello').check() - def test_hostmanager_init_rsync_topdir_explicit(self): - dir2 = self.source.ensure("dir1", "dir2", dir=1) + def test_hostmanager_init_rsync_topdir_explicit(self, source, dest): + dir2 = source.ensure("dir1", "dir2", dir=1) dir2.ensure("hello") - hm = self.gethostmanager( - dist_hosts = ["localhost:%s" % self.dest], - dist_rsync_roots = [str(self.source)] + hm = self.gethostmanager(source, + dist_hosts = ["localhost:%s" % dest], + dist_rsync_roots = [str(source)] ) - assert hm.session.config.topdir == self.source + assert hm.session.config.topdir == source hm.init_rsync() - dest = self.dest.join(self.source.basename) + dest = dest.join(source.basename) assert dest.join("dir1").check() assert dest.join("dir1", "dir2").check() assert dest.join("dir1", "dir2", 'hello').check() - def test_hostmanager_init_rsync_roots(self): - dir2 = self.source.ensure("dir1", "dir2", dir=1) - self.source.ensure("dir1", "somefile", dir=1) + def test_hostmanager_init_rsync_roots(self, source, dest): + dir2 = source.ensure("dir1", "dir2", dir=1) + source.ensure("dir1", "somefile", dir=1) dir2.ensure("hello") - self.source.ensure("bogusdir", "file") - self.source.join("conftest.py").write(py.code.Source(""" + source.ensure("bogusdir", "file") + source.join("conftest.py").write(py.code.Source(""" dist_rsync_roots = ['dir1/dir2'] """)) - session = py.test.config._reparse([self.source]).initsession() + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, - hosts=[Host("localhost:" + str(self.dest))]) + hosts=[Host("localhost:" + str(dest))]) hm.init_rsync() - assert self.dest.join("dir2").check() - assert not self.dest.join("dir1").check() - assert not self.dest.join("bogus").check() - - def test_hostmanager_rsync_ignore(self): - dir2 = self.source.ensure("dir1", "dir2", dir=1) - dir5 = self.source.ensure("dir5", "dir6", "bogus") - dirf = self.source.ensure("dir5", "file") + assert dest.join("dir2").check() + assert not dest.join("dir1").check() + assert not dest.join("bogus").check() + + def test_hostmanager_rsync_ignore(self, source, dest): + dir2 = source.ensure("dir1", "dir2", dir=1) + dir5 = source.ensure("dir5", "dir6", "bogus") + dirf = source.ensure("dir5", "file") dir2.ensure("hello") - self.source.join("conftest.py").write(py.code.Source(""" + source.join("conftest.py").write(py.code.Source(""" dist_rsync_ignore = ['dir1/dir2', 'dir5/dir6'] """)) - session = py.test.config._reparse([self.source]).initsession() + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, - hosts=[Host("localhost:" + str(self.dest))]) + hosts=[Host("localhost:" + str(dest))]) hm.init_rsync() - assert self.dest.join("dir1").check() - assert not self.dest.join("dir1", "dir2").check() - assert self.dest.join("dir5","file").check() - assert not self.dest.join("dir6").check() + assert dest.join("dir1").check() + assert not dest.join("dir1", "dir2").check() + assert dest.join("dir5","file").check() + assert not dest.join("dir6").check() - def test_hostmanage_optimise_localhost(self): + def test_hostmanage_optimise_localhost(self, source, dest): hosts = [Host("localhost") for i in range(3)] - session = py.test.config._reparse([self.source]).initsession() + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, hosts=hosts) hm.init_rsync() for host in hosts: @@ -275,9 +276,9 @@ assert host.gw_remotepath is None assert not host.relpath - def test_hostmanage_setup_hosts(self): + def test_hostmanage_setup_hosts(self, source): hosts = [Host("localhost") for i in range(3)] - session = py.test.config._reparse([self.source]).initsession() + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, hosts=hosts) queue = py.std.Queue.Queue() def queueput(**kw): Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Wed Feb 18 18:02:15 2009 @@ -354,18 +354,24 @@ return kwargs def fillarg(self, argname, kwargs): - reslist = self._config.pluginmanager.callfirst( - "pytest_pyfunc_arg", pyfuncitem=self, argname=argname) - if reslist: - value, argfinalizer = reslist[0] - kwargs[argname] = value - if argfinalizer is not None: - self._argfinalizers.append(argfinalizer) - else: - print "pluginmanager is", self._config.pluginmanager - print "could not find argument %r, plugins=%r" %( - argname,self._config.pluginmanager._plugins) - raise TypeError("could not provide funcargument %r" %(argname,)) + value = argfinalizer = None + if hasattr(self.parent.obj, 'pytest_pyfunc_arg'): + result = self.parent.obj.pytest_pyfunc_arg(pyfuncitem=self, argname=argname) + if result: + value, argfinalizer = result + if value is None: + reslist = self._config.pluginmanager.callfirst( + "pytest_pyfunc_arg", pyfuncitem=self, argname=argname) + if reslist: + value, argfinalizer = reslist[0] + else: + print "pluginmanager is", self._config.pluginmanager + print "could not find argument %r, plugins=%r" %( + argname,self._config.pluginmanager._plugins) + raise TypeError("could not provide funcargument %r" %(argname,)) + kwargs[argname] = value + if argfinalizer is not None: + self._argfinalizers.append(argfinalizer) def __eq__(self, other): try: Modified: py/branch/pytestplugin/py/test/testing/test_runner_functional.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_runner_functional.py (original) +++ py/branch/pytestplugin/py/test/testing/test_runner_functional.py Wed Feb 18 18:02:15 2009 @@ -47,6 +47,20 @@ """) ev, = evrec.getnamed("itemtestreport") assert ev.passed, ev.longrepr + + def test_getwkargs_boundmethod_send_within_class(self, testdir): + evrec = testdir.inline_runsource(""" + class TestClass: + def pytest_pyfunc_arg(self, pyfuncitem, argname): + assert argname != 'self' + if argname == "hello": + return "world", None + + def test_func(self, hello): + assert hello == "world" + """) + ev, = evrec.getnamed("itemtestreport") + assert ev.passed, ev.longrepr def test_funcattr(self, testdir): ev = testdir.runitem(""" From hpk at codespeak.net Wed Feb 18 18:41:39 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 18:41:39 +0100 (CET) Subject: [py-svn] r62014 - in py/branch/pytestplugin/py/test: dsession/testing plugin testing Message-ID: <20090218174139.99639169DFE@codespeak.net> Author: hpk Date: Wed Feb 18 18:41:37 2009 New Revision: 62014 Removed: py/branch/pytestplugin/py/test/testing/suptest.py Modified: py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Log: puh! with a last bit of hacking finally the old way of setting up things is gone within the py/test hierarchy. Modified: py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py Wed Feb 18 18:41:37 2009 @@ -3,9 +3,11 @@ from py.__.test.dsession.masterslave import MasterNode from py.__.test.dsession.hostmanage import Host from py.__.test import event -from py.__.test.testing import suptest -class TestMasterSlaveConnection(suptest.InlineCollection): +class MySetup: + def __init__(self, pyfuncitem): + self.pyfuncitem = pyfuncitem + def getevent(self, eventtype=event.ItemTestReport, timeout=2.0): events = [] while 1: @@ -23,82 +25,82 @@ return ev events.append(ev) - def setup_method(self, method): - super(TestMasterSlaveConnection, self).setup_method(method) - self.makepyfile(__init__="") - self.config = self.parseconfig(self.tmpdir) + def queueput(self, **kw): + self.queue.put(kw) + + def makenode(self, config=None): + if config is None: + config = py.test.config._reparse([]) + self.config = config self.queue = py.std.Queue.Queue() self.host = Host("localhost") self.host.initgateway() - def queueput(**kw): - self.queue.put(kw) - self.node = MasterNode(self.host, self.config, queueput) + self.node = MasterNode(self.host, self.config, self.queueput) assert not self.node.channel.isclosed() + return self.node - def getitem(self, source): - kw = {"test_" + self.tmpdir.basename: py.code.Source(source).strip()} - path = self.makepyfile(**kw) - fscol = self.config.getfsnode(path) - return fscol.collect_by_name("test_func") - - def getitems(self, source): - kw = {"test_" + self.tmpdir.basename: py.code.Source(source).strip()} - path = self.makepyfile(**kw) - fscol = self.config.getfsnode(path) - return fscol.collect() - - def teardown_method(self, method): - print "at teardown:", self.node.channel - #if not self.node.channel.isclosed(): - # self.node.shutdown() - self.host.gw.exit() - - def test_crash_invalid_item(self): - self.node.send(123) # invalid item - ev = self.getevent(event.HostDown) - assert ev.host == self.host + def finalize(self): + if hasattr(self, 'host'): + print "exiting:", self.host.gw + self.host.gw.exit() + +class TestMasterSlaveConnection: + def pytest_pyfunc_arg(self, pyfuncitem, argname): + if argname == "mysetup": + x = MySetup(pyfuncitem) + return x, x.finalize + + def test_crash_invalid_item(self, mysetup): + node = mysetup.makenode() + node.send(123) # invalid item + ev = mysetup.getevent(event.HostDown) + assert ev.host == mysetup.host assert str(ev.error).find("AttributeError") != -1 - def test_crash_killed(self): + def test_crash_killed(self, testdir, mysetup): if not hasattr(py.std.os, 'kill'): py.test.skip("no os.kill") - item = self.getitem(""" + item = testdir.getitem(""" def test_func(): import os os.kill(os.getpid(), 15) """) - self.node.send(item) - ev = self.getevent(event.HostDown) - assert ev.host == self.host + node = mysetup.makenode(item._config) + node.send(item) + ev = mysetup.getevent(event.HostDown) + assert ev.host == mysetup.host assert str(ev.error).find("TERMINATED") != -1 - def test_node_down(self): - self.node.shutdown() - ev = self.getevent(event.HostDown) - assert ev.host == self.host + def test_node_down(self, mysetup): + node = mysetup.makenode() + node.shutdown() + ev = mysetup.getevent(event.HostDown) + assert ev.host == mysetup.host assert not ev.error - self.node.callback(self.node.ENDMARK) + node.callback(node.ENDMARK) excinfo = py.test.raises(IOError, - "self.getevent(event.HostDown, timeout=0.01)") + "mysetup.getevent(event.HostDown, timeout=0.01)") - def test_send_on_closed_channel(self): - item = self.getitem("def test_func(): pass") - self.node.channel.close() - py.test.raises(IOError, "self.node.send(item)") + def test_send_on_closed_channel(self, testdir, mysetup): + item = testdir.getitem("def test_func(): pass") + node = mysetup.makenode(item._config) + node.channel.close() + py.test.raises(IOError, "node.send(item)") #ev = self.getevent(event.InternalException) #assert ev.excinfo.errisinstance(IOError) - def test_send_one(self): - item = self.getitem("def test_func(): pass") - self.node.send(item) - ev = self.getevent() + def test_send_one(self, testdir, mysetup): + item = testdir.getitem("def test_func(): pass") + node = mysetup.makenode(item._config) + node.send(item) + ev = mysetup.getevent() assert ev.passed assert ev.colitem == item #assert event.item == item #assert event.item is not item - def test_send_some(self): - items = self.getitems(""" + def test_send_some(self, testdir, mysetup): + items = testdir.getitems(""" def test_pass(): pass def test_fail(): @@ -107,13 +109,14 @@ import py py.test.skip("x") """) + node = mysetup.makenode(items[0]._config) for item in items: - self.node.send(item) + node.send(item) for outcome in "passed failed skipped".split(): - ev = self.getevent() + ev = mysetup.getevent() assert getattr(ev, outcome) - self.node.sendlist(items) + node.sendlist(items) for outcome in "passed failed skipped".split(): - ev = self.getevent() + ev = mysetup.getevent() assert getattr(ev, outcome) Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 18 18:41:37 2009 @@ -109,6 +109,12 @@ assert item is not None, "%r item not found in module:\n%s" %(funcname, source) return item + def getitems(self, source): + modcol = self.getmodulecol(source) + return list(modcol._config.initsession().genitems([modcol])) + #assert item is not None, "%r item not found in module:\n%s" %(funcname, source) + #return item + def getfscol(self, path, configargs=()): self.config = self.parseconfig(path, *configargs) self.session = self.config.initsession() Deleted: /py/branch/pytestplugin/py/test/testing/suptest.py ============================================================================== --- /py/branch/pytestplugin/py/test/testing/suptest.py Wed Feb 18 18:41:37 2009 +++ (empty file) @@ -1,185 +0,0 @@ -""" - test support code - - makeuniquepyfile(source) generates a per-test-run-unique directory and test_*.py file - - for analyzing events an EventSorter instance is returned for both of: - * events_from_cmdline(args): inprocess-run of cmdline invocation - * events_from_session(session): inprocess-run of given session - - eventappender(config): for getting all events in a list: -""" -import py -from py.__.test import event -from fnmatch import fnmatch - -def eventappender(bus): - l = [] - def app((name, ev)): - print ev - l.append(ev) - bus.subscribe(app) - return l - -def initsorter_from_cmdline(args=None): - if args is None: - args = [] - config = py.test.config._reparse(args) - session = config.initsession() - sorter = EventSorter(config, session) - return sorter - -def getcolitems(config): - return [config.getfsnode(arg) for arg in config.args] - -def events_from_cmdline(args=None): - sorter = initsorter_from_cmdline(args) - sorter.session.main(getcolitems(sorter.session.config)) - return sorter - -def events_from_runsource(source): - source = py.code.Source(source) - tfile = makeuniquepyfile(source) - return events_from_cmdline([tfile]) - -def events_from_session(session): - sorter = EventSorter(session.config, session) - session.main(getcolitems(session.config)) - return sorter - -class EventSorter(object): - def __init__(self, config, session=None): - self.config = config - self.session = session - self.cls2events = d = {} - def app((name, event)): - print "[event]", event - for cls in py.std.inspect.getmro(event.__class__): - if cls is not object: - d.setdefault(cls, []).append(event) - session.bus.subscribe(app) - - def get(self, cls): - return self.cls2events.get(cls, []) - - def listoutcomes(self): - passed = [] - skipped = [] - failed = [] - for ev in self.get(event.ItemTestReport): - if ev.passed: - passed.append(ev) - elif ev.skipped: - skipped.append(ev) - elif ev.failed: - failed.append(ev) - return passed, skipped, failed - - def countoutcomes(self): - return map(len, self.listoutcomes()) - - def assertoutcome(self, passed=0, skipped=0, failed=0): - realpassed, realskipped, realfailed = self.listoutcomes() - assert passed == len(realpassed) - assert skipped == len(realskipped) - assert failed == len(realfailed) - - def getfailedcollections(self): - l = [] - for ev in self.get(event.CollectionReport): - if ev.failed: - l.append(ev) - return l - - def getreport(self, inamepart): - """ return a testreport whose dotted import path matches """ - l = [] - for rep in self.get(event.ItemTestReport): - if inamepart in rep.colitem.listnames(): - l.append(rep) - if len(l) != 1: - raise ValueError("did not find exactly one testreport" - "found" + str(l)) - return l[0] - - -counter = py.std.itertools.count().next -def makeuniquepyfile(source): - dirname = "test_%d" %(counter(),) - tmpdir = py.test.ensuretemp(dirname) - p = tmpdir.join(dirname + ".py") - assert not p.check() - p.write(py.code.Source(source)) - print "created test file", p - p.dirpath("__init__.py").ensure() - return p - -def getItemTestReport(source, tb="long"): - tfile = makeuniquepyfile(source) - sorter = events_from_cmdline([tfile, "--tb=%s" %tb]) - # get failure base info - failevents = sorter.get(event.ItemTestReport) - assert len(failevents) == 1 - return failevents[0],tfile - -# XXX below some code to help with inlining examples -# as source code. -# -class FileCreation(object): - def setup_method(self, method): - self.tmpdir = py.test.ensuretemp("%s_%s" % - (self.__class__.__name__, method.__name__)) - def makepyfile(self, **kwargs): - return self._makefile('.py', **kwargs) - def maketxtfile(self, **kwargs): - return self._makefile('.txt', **kwargs) - def _makefile(self, ext, **kwargs): - ret = None - for name, value in kwargs.iteritems(): - p = self.tmpdir.join(name).new(ext=ext) - source = py.code.Source(value) - p.write(str(py.code.Source(value)).lstrip()) - if ret is None: - ret = p - return ret - - def parseconfig(self, *args): - return py.test.config._reparse(list(args)) - -class InlineCollection(FileCreation): - """ helps to collect and run test functions inlining other test functions. """ - - def getmodulecol(self, source, configargs=(), withsession=False): - self.tmpdir.ensure("__init__.py") - kw = {"test_" + self.tmpdir.basename: py.code.Source(source).strip()} - path = self.makepyfile(**kw) - self.config = self.parseconfig(path, *configargs) - if withsession: - self.session = self.config.initsession() - return self.config.getfsnode(path) - - def getitems(self, source): - modulecol = self.getmodulecol(source) - return modulecol.collect() - - def getitem(self, source, funcname="test_func"): - modulecol = self.getmodulecol(source) - item = modulecol.join(funcname) - assert item is not None, "%r item not found in module:\n%s" %(funcname, source) - return item - - def runitem(self, func, funcname="test_func", **runnerargs): - item = self.getitem(func, funcname=funcname) - runner = self.getrunner() - return runner(item, **runnerargs) - -class InlineSession(InlineCollection): - def inline_run(self, *args): - config = self.parseconfig(*args) - config.pluginmanager.configure(config) - session = config.initsession() - sorter = EventSorter(config, session) - session.main() - config.pluginmanager.unconfigure(config) - return sorter - From hpk at codespeak.net Wed Feb 18 19:00:17 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 18 Feb 2009 19:00:17 +0100 (CET) Subject: [py-svn] r62015 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090218180017.DFE9F169E06@codespeak.net> Author: hpk Date: Wed Feb 18 19:00:16 2009 New Revision: 62015 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/plugin/pytest_xfail.py py/branch/pytestplugin/py/test/testing/test_config.py Log: noting down one issue, modifying xfail reporting a bit Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Wed Feb 18 19:00:16 2009 @@ -73,7 +73,11 @@ self._repr = repr def _initafterpickle(self, topdir): - self.__init__() + self.__init__( + #issue1 + #bus=py.test._EventBus(bus=py.event), + #pluginmanager=py.test._PytestPluginManager(py.plugin) + ) self._initialized = True self.topdir = py.path.local(topdir) self._mergerepr(self._repr) Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Wed Feb 18 19:00:16 2009 @@ -27,7 +27,8 @@ key = entry.path, entry.lineno, entry.message reason = event.longrepr.reprcrash.message modpath = event.colitem.getmodpath(stopatmodule=False) - tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message)) + #tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message)) + tr._tw.line("%s %s:%d: " %(modpath, entry.path, entry.lineno)) xpassed = terminalreporter.stats.get("xpassed") if xpassed: @@ -57,6 +58,6 @@ result = testdir.runpytest(p) extra = result.stdout.fnmatch_lines([ "*XFAILURES*", - "*test_one.test_this*test_one.py:5: assert 0*", + "*test_one.test_this*test_one.py:5*", ]) assert result.ret == 1 Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Wed Feb 18 19:00:16 2009 @@ -101,6 +101,22 @@ assert gettopdir([c]) == a assert gettopdir([c, Z]) == tmp + at py.test.keywords(xfail="issue1 config's pluginmanager/bus initialization") +def test_config_initafterpickle_plugin(testdir): + testdir.makepyfile(__init__="", conftest="x=1; y=2") + hello = testdir.makepyfile(hello="") + tmp = testdir.tmpdir + config = py.test.config._reparse([hello]) + config2 = py.test.config._reparse([tmp.dirpath()]) + config2._initialized = False # we have to do that from tests + config2._repr = config._makerepr() + config2._initafterpickle(topdir=tmp.dirpath()) + # we check that config "remote" config objects + # have correct plugin initialization + #XXX assert config2.pluginmanager.pm._plugins + #XXX assert config2.bus.issubscribed(config2.pluginmanager.forward_event) + assert config2.bus._bus == py.event + assert config2.pluginmanager.pm == py.plugin def test_config_initafterpickle_some(): tmp = py.test.ensuretemp("test_config_initafterpickle_some") @@ -112,10 +128,7 @@ config2._initialized = False # we have to do that from tests config2._repr = config._makerepr() config2._initafterpickle(topdir=tmp.dirpath()) - # we check that config "remote" config objects - # have correct plugin initialization - #XXX assert config2.pluginmanager.pm._plugins - #XXX assert config2.bus.issubscribed(config2.pluginmanager.forward_event) + for col1, col2 in zip(getcolitems(config), getcolitems(config2)): assert col1.fspath == col2.fspath cols = getcolitems(config2) From hpk at codespeak.net Thu Feb 19 15:49:06 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 19 Feb 2009 15:49:06 +0100 (CET) Subject: [py-svn] r62019 - in py/branch/pytestplugin/py/test: plugin testing Message-ID: <20090219144906.B379E169E47@codespeak.net> Author: hpk Date: Thu Feb 19 15:49:04 2009 New Revision: 62019 Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/testing/acceptance_test.py Log: test and fix for --collectonly failures. remove dead code. Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Thu Feb 19 15:49:04 2009 @@ -23,17 +23,6 @@ setattr(self.reporter._tw, name, getattr(config, attr)) config.bus.subscribe_methods(self.reporter) -def folded_skips(skipped): - d = {} - for event in skipped: - entry = event.longrepr.reprcrash - key = entry.path, entry.lineno, entry.message - d.setdefault(key, []).append(event) - l = [] - for key, events in d.iteritems(): - l.append((len(events),) + key) - return l - class TerminalReporter: def __init__(self, config, file=None): self.config = config @@ -156,8 +145,6 @@ def pyevent_collectionreport(self, ev): if not ev.passed: - #XXX allow plugins to override collection failures? - # cat, letter, word = self.getcategoryletterword(ev) if ev.failed: self.stats.setdefault("failed", []).append(ev) msg = ev.longrepr.reprcrash.message @@ -203,18 +190,6 @@ for rootdir in ev.rootdirs: self.write_line("### Watching: %s" %(rootdir,), bold=True) - if 0: - print "#" * 60 - print "# looponfailing: mode: %d failures args" % len(failures) - for ev in failurereports: - name = "/".join(ev.colitem.listnames()) # XXX - print "Failure at: %r" % (name,) - print "# watching py files below %s" % rootdir - print "# ", "^" * len(str(rootdir)) - failures = [ev.colitem for ev in failurereports] - if not failures: - failures = colitems - # # summaries for TestrunFinish # @@ -290,12 +265,26 @@ def pyevent_collectionreport(self, ev): if not ev.passed: self.outindent("!!! %s !!!" % ev.longrepr.reprcrash.message) + self._failed.append(ev) self.indent = self.indent[:-len(self.INDENT)] def pyevent_testrunfinish(self, session): + if self._failed: + self.out.sep("!", "collection failures") for ev in self._failed: ev.toterminal(self.out) +def folded_skips(skipped): + d = {} + for event in skipped: + entry = event.longrepr.reprcrash + key = entry.path, entry.lineno, entry.message + d.setdefault(key, []).append(event) + l = [] + for key, events in d.iteritems(): + l.append((len(events),) + key) + return l + def repr_pythonversion(v=None): if v is None: v = sys.version_info @@ -570,7 +559,6 @@ def test_func(): pass """) - #stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) registerdispatcher(rep) indent = rep.indent Modified: py/branch/pytestplugin/py/test/testing/acceptance_test.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/acceptance_test.py (original) +++ py/branch/pytestplugin/py/test/testing/acceptance_test.py Thu Feb 19 15:49:04 2009 @@ -38,6 +38,20 @@ """).strip()) + def test_collectonly_error(self, testdir): + p = testdir.makepyfile("import Errlkjqweqwe") + result = testdir.runpytest("--collectonly", p) + stderr = result.stderr.str().strip() + assert stderr.startswith("inserting into sys.path") + assert result.ret == 1 + extra = result.stdout.fnmatch_lines(py.code.Source(""" + + *ImportError* + !!!*failures*!!! + *test_collectonly_error.py:1* + """).strip()) + + def test_nested_import_error(self, testdir): p = testdir.makepyfile(""" import import_fails From hpk at codespeak.net Fri Feb 20 01:50:23 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 20 Feb 2009 01:50:23 +0100 (CET) Subject: [py-svn] r62041 - in py/branch/pytestplugin/py: . misc/testing Message-ID: <20090220005023.BCAB7169E9F@codespeak.net> Author: hpk Date: Fri Feb 20 01:50:21 2009 New Revision: 62041 Added: py/branch/pytestplugin/py/_com.py (contents, props changed) py/branch/pytestplugin/py/misc/testing/test_com.py (contents, props changed) Modified: py/branch/pytestplugin/py/__init__.py Log: * introduce py._com for plugin and event management * flexibilize the Plugin call protocol Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Fri Feb 20 01:50:21 2009 @@ -59,6 +59,12 @@ 'event' : ('./misc/event.py', 'eventbus'), 'plugin' : ('./misc/plugin.py', 'plugin'), + '_com.PluginManager' : ('./_com.py', 'PluginManager'), + '_com.EventBus' : ('./_com.py', 'EventBus'), + '_com.Call' : ('./_com.py', 'Call'), + '_com.pluginmanager' : ('./_com.py', 'pluginmanager'), + '_com.bus' : ('./_com.py', 'bus'), + # py lib cmdline tools 'cmdline.pytest' : ('./cmdline/pytest.py', 'main',), 'cmdline.pyrest' : ('./cmdline/pyrest.py', 'main',), Added: py/branch/pytestplugin/py/_com.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/_com.py Fri Feb 20 01:50:21 2009 @@ -0,0 +1,135 @@ + +import py + +class Call: + NORESULT = object() + + def __init__(self, callees, methname, *args, **kwargs): + self.callees = list(callees) + self.methname = methname + self.args = args + self.kwargs = kwargs + self.results = [] + + def listmethods(self): + l = [] + for callee in self.callees: + try: + l.append(getattr(callee, self.methname)) + except AttributeError: + continue + return l + + def execute(self, firstresult=False): + self.methods = self.listmethods() + while self.methods: + self.currentmethod = self.methods.pop(0) + res = self.currentmethod(self, *self.args, **self.kwargs) + if res is not self.NORESULT: + self.results.append(res) + if firstresult: + break + if not firstresult: + return self.results + if self.results: + return self.results[0] + +class PluginManager: + def __init__(self, bus=None): + if bus is None: + bus = EventBus() + self.bus = bus + self.plugins = [] + + def import_module(self, modspec): + # XXX allow modspec to specify version / lookup + modpath = modspec + self.bus.notify(importingmodule=modpath) + __import__(modpath) + + def consider_env(self, varname="PYLIB"): + """ consider ENV variable for loading modules. """ + val = py.std.os.environ.get(varname, None) + self._consider(val) + + def consider_module(self, mod, varname="pylib"): + speclist = getattr(mod, varname, None) + self._consider(speclist) + + def _consider(self, speclist): + if speclist is not None: + if not isinstance(speclist, (list, tuple)): + speclist = (speclist,) + for spec in speclist: + self.import_module(spec) + + def register(self, plugin): + assert not isinstance(plugin, str) + self.plugins.append(plugin) + #print "registering", self, plugin + self.bus.notify(pluginregistered=plugin) + + def iterattr(self, attrname): + for plugin in self.plugins: + try: + yield getattr(plugin, attrname) + except AttributeError: + continue + + #def Call(self, methname, *args, **kwargs): + # """ return call object for executing a plugin call. """ + # return Call(self.plugins, methname, args, kwargs) + + def call_firstresult(self, methname, *args, **kwargs): + """ return first non-None result of a plugin method. """ + return Call(self.plugins, methname, *args, **kwargs).execute(firstresult=True) + + def call_each(self, methname, *args, **kwargs): + """ return call object for executing a plugin call. """ + return Call(self.plugins, methname, *args, **kwargs).execute() + + def callplugin(self, plugin, methname, *args, **kwargs): + return Call([plugin], methname, *args, **kwargs).execute(firstresult=True) + +class EventBus(object): + """ Event Bus for distributing named and unnamed events. """ + def __init__(self): + self._subscribers = {} + + def _getsubscribers(self, key): + try: + return self._subscribers[key] + except KeyError: + x = self._subscribers[key] = [] + return x + + def subscribe(self, callback=None, **kwcallback): + """ subscribe callback to bus events. """ + if callback: + self._getsubscribers('').append(callback) + assert not kwcallback + else: + assert len(kwcallback) == 1 + name, call = kwcallback.popitem() + self._getsubscribers(name).append(call) + + def unsubscribe(self, callback=None, **kwcallback): + """ unsubscribe given callback from bus events. """ + try: + if callback is not None: + self._getsubscribers('').remove(callback) + else: + for name, call in kwcallback.items(): + self._getsubscribers(name).remove(call) + except ValueError, e: + raise KeyError(*e.args) + + def notify(self, **kw): + for name in kw: + for callback in self._getsubscribers(name): + callback(kw[name]) + for callback in self._getsubscribers(''): + callback((name, kw[name])) + +pluginmanager = PluginManager() +bus = pluginmanager.bus Added: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Fri Feb 20 01:50:21 2009 @@ -0,0 +1,235 @@ + +import py +import os +from py._com import PluginManager, EventBus, Call + +pytest_plugins = "xfail" + +class TestCall: + def test_basic(self): + class P1: + def m(self, call, x): + assert call.currentmethod == self.m + assert call.results == [] + assert call.methods + return 17 + + class P2: + def m(self, call, x): + assert call.currentmethod == self.m + assert call.args + assert len(call.results) == 1 + assert not call.methods + return 23 + + p1 = P1() + p2 = P2() + call = Call([p1, p2], 'm', 23) + reslist = call.execute() + assert len(reslist) == 2 + assert reslist == [17, 23] + +class TestPluginManager: + def test_register(self): + pm = PluginManager() + class MyPlugin: + pass + my = MyPlugin() + pm.register(my) + assert my in pm.plugins + + #@py.test.keywords(xfail=True) + def test_onregister(self): + py.test.skip("implement exitfirst plugin and " + "modify xfail plugin to override exitfirst behaviour?") + pm = PluginManager() + l = [] + class MyApi: + def pyevent_pluginregistered(self, plugin): + l.append(plugin) + myapi = MyApi() + pm.register(myapi) + assert len(l) == 1 + assert l[0] is myapi + + def test_call_methods(self): + pm = PluginManager() + class api1: + def m(self, call, x): + return x + class api2: + def m(self, call, x, y=33): + return y + pm.register(api1()) + pm.register(api2()) + res = pm.call_firstresult("m", x=5) + assert pm.call_firstresult("notexist") is None + + assert res == 5 + reslist = pm.call_each("m", x=5) + assert len(reslist) == 2 + assert 5 in reslist + assert 33 in reslist + assert pm.call_each("notexist") == [] + + assert pm.callplugin(api1(), 'm', x=12) == 12 + assert pm.callplugin(api2(), 't') is None + + def test_callfirst_ignores_None(self): + pm = PluginManager() + class api1: + def m(self, call): + return None + class api2: + def m(self, call): + return 41 + pm.register(api1()) + pm.register(api1()) + pm.register(api1()) + pm.register(api2()) + pm.register(api1()) + pm.register(api1()) + assert pm.call_firstresult('m') == None + + def test_callsetnoresult(self): + pm = PluginManager() + class api1: + def m(self, call): + return call.NORESULT + class api2: + def m(self, call): + return 41 + pm.register(api1()) + pm.register(api1()) + pm.register(api2()) + assert pm.call_firstresult('m') == 41 + assert pm.call_each('m') == [41] + + def test_iterattr(self): + pm = PluginManager() + class api1: + x = 42 + class api2: + x = 41 + pm.register(api1()) + pm.register(api2()) + l = list(pm.iterattr('x')) + l.sort() + assert l == [41, 42] + + def test_consider_env(self): + # XXX write a helper for preserving os.environ + pm = PluginManager() + KEY = "XXPYLIB" + old = os.environ.get(KEY, None) + try: + os.environ[KEY] = "unknownconsider_env" + py.test.raises(ImportError, "pm.consider_env(KEY)") + finally: + if old is None: + del os.environ[KEY] + else: + os.environ[KEY] = old + + def test_consider_module(self): + pm = PluginManager() + mod = py.std.new.module("temp") + mod.pylib = ["xxx nomod"] + excinfo = py.test.raises(ImportError, "pm.consider_module(mod)") + mod.pylib = "os" + l = [] + pm.bus.subscribe(importingmodule=l.append) + pm.consider_module(mod) + assert len(l) == 1 + assert l[0] == mod.pylib + +def test_api_and_defaults(): + assert isinstance(py._com.pluginmanager, PluginManager) + assert isinstance(py._com.bus, EventBus) + +def test_subprocess_env(): + # XXX write a helper for preserving os.environ + pm = PluginManager() + KEY = "PYLIB" + old = os.environ.get(KEY, None) + olddir = py.path.local(py.__file__).dirpath().dirpath().chdir() + try: + os.environ[KEY] = "unknownconsider_env" + excinfo = py.test.raises(py.process.cmdexec.Error, """ + py.process.cmdexec("python -c 'import py'") + """) + assert str(excinfo.value).find("ImportError") != -1 + assert str(excinfo.value).find("unknownconsider") != -1 + finally: + olddir.chdir() + if old is None: + del os.environ[KEY] + else: + os.environ[KEY] = old + +class TestEventBus: + def test_kw_register_notify_unregister(self): + bus = EventBus() + l = [] + bus.subscribe(hello=l.append) + bus.notify(some=1) + assert not l + bus.notify(hello=5) + assert len(l) == 1 + l.remove(5) + bus.unsubscribe(hello=l.append) + bus.notify(hello=7) + assert not l + + def test_kw_unregister_non_existing(self): + bus = EventBus() + py.test.raises(KeyError, "bus.unsubscribe(hello=42)") + bus.subscribe(hello=10) + bus.unsubscribe(hello=10) + py.test.raises(KeyError, "bus.unsubscribe(hello=42)") + + def test_kw_multiregister(self): + bus = EventBus() + l1 = [] + l2 = [] + bus.subscribe(hello=l1.append) + bus.subscribe(hello=l2.append) + bus.subscribe(world=l2.append) + bus.notify(some=1) + assert not l1 and not l2 + bus.notify(hello=5) + assert len(l1) == 1 + assert len(l2) == 1 + bus.notify(world=42) + assert len(l2) == 2 + bus.unsubscribe(hello=l2.append) + bus.unsubscribe(world=l2.append) + bus.unsubscribe(hello=l1.append) + + def test_simple_anonymous(self): + bus = EventBus() + l = [] + bus.subscribe(l.append) + bus.notify(hello=1) + assert l == [("hello", 1)] + bus.unsubscribe(l.append) + bus.notify(hello=1) + assert l == [("hello", 1)] + + def test_multi_anonymous(self): + bus = EventBus() + l1 = [] + l2 = [] + bus.subscribe(l1.append) + bus.subscribe(l2.append) + bus.notify(event=1) + bus.notify(event2=2) + bus.notify(event3=3) + assert l1 == [("event", 1), ("event2", 2), ("event3", 3)] + assert l2 == [("event", 1), ("event2", 2), ("event3", 3)] + bus.unsubscribe(l1.append) + bus.unsubscribe(l2.append) + py.test.raises(KeyError, "bus.unsubscribe(l1.append)") + py.test.raises(KeyError, "bus.unsubscribe(l2.append)") + + From hpk at codespeak.net Fri Feb 20 02:44:17 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 20 Feb 2009 02:44:17 +0100 (CET) Subject: [py-svn] r62042 - in py/branch/pytestplugin/py: . misc misc/testing test test/plugin test/testing Message-ID: <20090220014417.3321B168078@codespeak.net> Author: hpk Date: Fri Feb 20 02:44:14 2009 New Revision: 62042 Removed: py/branch/pytestplugin/py/misc/event.py py/branch/pytestplugin/py/misc/plugin.py py/branch/pytestplugin/py/misc/testing/test_event.py py/branch/pytestplugin/py/misc/testing/test_plugin.py Modified: py/branch/pytestplugin/py/__init__.py py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/initpkg.py py/branch/pytestplugin/py/misc/testing/test_com.py py/branch/pytestplugin/py/misc/testing/test_warn.py py/branch/pytestplugin/py/misc/warn.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/event.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py Log: * get rid of old global py.event/py.plugin in favour of the new py._com arrangement * experimental: allow to optionally receive a "call" argument Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Fri Feb 20 02:44:14 2009 @@ -56,9 +56,6 @@ exportdefs = { # py lib events and plugins - 'event' : ('./misc/event.py', 'eventbus'), - 'plugin' : ('./misc/plugin.py', 'plugin'), - '_com.PluginManager' : ('./_com.py', 'PluginManager'), '_com.EventBus' : ('./_com.py', 'EventBus'), '_com.Call' : ('./_com.py', 'Call'), @@ -203,6 +200,6 @@ }) import py -py.plugin.consider_env() +py._com.pluginmanager.consider_env() Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Fri Feb 20 02:44:14 2009 @@ -2,7 +2,7 @@ import py class Call: - NORESULT = object() + NONEASRESULT = object() def __init__(self, callees, methname, *args, **kwargs): self.callees = list(callees) @@ -23,9 +23,15 @@ def execute(self, firstresult=False): self.methods = self.listmethods() while self.methods: - self.currentmethod = self.methods.pop(0) - res = self.currentmethod(self, *self.args, **self.kwargs) - if res is not self.NORESULT: + self.currentmethod = self.methods.pop() + varnames = self.currentmethod.im_func.func_code.co_varnames + if varnames >1 and varnames[1] == 'call': + res = self.currentmethod(self, *self.args, **self.kwargs) + else: + res = self.currentmethod(*self.args, **self.kwargs) + if res is not None: + if res is self.NONEASRESULT: + res = None self.results.append(res) if firstresult: break Modified: py/branch/pytestplugin/py/initpkg.py ============================================================================== --- py/branch/pytestplugin/py/initpkg.py (original) +++ py/branch/pytestplugin/py/initpkg.py Fri Feb 20 02:44:14 2009 @@ -273,5 +273,5 @@ ENVKEY = pkgname.upper() + "_AUTOIMPORT" if ENVKEY in os.environ: for impname in os.environ[ENVKEY].split(","): - py.event.notify(autoimport=impname) + py._com.bus.notify(autoimport=impname) __import__(impname) Deleted: /py/branch/pytestplugin/py/misc/event.py ============================================================================== --- /py/branch/pytestplugin/py/misc/event.py Fri Feb 20 02:44:14 2009 +++ (empty file) @@ -1,42 +0,0 @@ - -class EventBus(object): - """ Event Bus for distributing named and unnamed events. """ - def __init__(self): - self._subscribers = {} - - def _getsubscribers(self, key): - try: - return self._subscribers[key] - except KeyError: - x = self._subscribers[key] = [] - return x - - def subscribe(self, callback=None, **kwcallback): - """ subscribe callback to bus events. """ - if callback: - self._getsubscribers('').append(callback) - assert not kwcallback - else: - assert len(kwcallback) == 1 - name, call = kwcallback.popitem() - self._getsubscribers(name).append(call) - - def unsubscribe(self, callback=None, **kwcallback): - """ unsubscribe given callback from bus events. """ - try: - if callback is not None: - self._getsubscribers('').remove(callback) - else: - for name, call in kwcallback.items(): - self._getsubscribers(name).remove(call) - except ValueError, e: - raise KeyError(*e.args) - - def notify(self, **kw): - for name in kw: - for callback in self._getsubscribers(name): - callback(kw[name]) - for callback in self._getsubscribers(''): - callback((name, kw[name])) - -eventbus = EventBus() Deleted: /py/branch/pytestplugin/py/misc/plugin.py ============================================================================== --- /py/branch/pytestplugin/py/misc/plugin.py Fri Feb 20 02:44:14 2009 +++ (empty file) @@ -1,69 +0,0 @@ -import py - -class PluginManager: - _pyspec = "pylib" - - def __init__(self, bus=None): - if bus is None: - from py.__.misc.event import EventBus - bus = EventBus() - self._bus = bus - self.list = [] - - def import_module(self, modspec): - # XXX allow modspec to specify version / lookup - modpath = modspec - self._bus.notify(importingmodule=modpath) - __import__(modpath) - - def consider_env(self): - """ consider ENV variables for loading modules. """ - val = py.std.os.environ.get(self._pyspec.upper(), None) - self._consider(val) - - def consider_module(self, mod): - speclist = getattr(mod, self._pyspec, None) - self._consider(speclist) - - def _consider(self, speclist): - if speclist is not None: - if not isinstance(speclist, (list, tuple)): - speclist = (speclist,) - for spec in speclist: - self.import_module(spec) - - def register(self, plugin): - assert not isinstance(plugin, str) - self.list.append(plugin) - #print "registering", self, plugin - self._bus.notify(pluginregistered=plugin) - - def iterattr(self, attrname): - for plugin in self.list: - try: - yield getattr(plugin, attrname) - except AttributeError: - continue - - def calleach(self, methname, *args, **kwargs): - ret = [] - for call in self.iterattr(methname): - result = call(*args, **kwargs) - ret.append(result) - return ret - - def callfirst(self, methname, *args, **kwargs): - ret = [] - for call in self.iterattr(methname): - result = call(*args, **kwargs) - if result is not None: # XXX odd to interpret the result here - ret.append(result) - break - return ret - - def callone(self, plugin, methname, *args, **kwargs): - call = getattr(plugin, methname, None) - if call is not None: - return call(*args, **kwargs) - -plugin = PluginManager(bus=py.event) Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Fri Feb 20 02:44:14 2009 @@ -10,16 +10,16 @@ class P1: def m(self, call, x): assert call.currentmethod == self.m - assert call.results == [] - assert call.methods + assert len(call.results) == 1 + assert not call.methods return 17 class P2: def m(self, call, x): assert call.currentmethod == self.m assert call.args - assert len(call.results) == 1 - assert not call.methods + assert call.results == [] + assert call.methods return 23 p1 = P1() @@ -27,7 +27,16 @@ call = Call([p1, p2], 'm', 23) reslist = call.execute() assert len(reslist) == 2 - assert reslist == [17, 23] + # ensure reversed order + assert reslist == [23, 17] + + def test_optionalcallarg(self): + class P1: + def m(self, x): + return x + call = Call([P1()], 'm', 23) + assert call.execute() == [23] + assert call.execute(firstresult=True) == 23 class TestPluginManager: def test_register(self): @@ -65,7 +74,7 @@ res = pm.call_firstresult("m", x=5) assert pm.call_firstresult("notexist") is None - assert res == 5 + assert res == 33 reslist = pm.call_each("m", x=5) assert len(reslist) == 2 assert 5 in reslist @@ -75,7 +84,7 @@ assert pm.callplugin(api1(), 'm', x=12) == 12 assert pm.callplugin(api2(), 't') is None - def test_callfirst_ignores_None(self): + def test_call_none_is_no_result(self): pm = PluginManager() class api1: def m(self, call): @@ -85,25 +94,19 @@ return 41 pm.register(api1()) pm.register(api1()) - pm.register(api1()) pm.register(api2()) - pm.register(api1()) - pm.register(api1()) - assert pm.call_firstresult('m') == None + assert pm.call_firstresult('m') == 41 + assert pm.call_each('m') == [41] - def test_callsetnoresult(self): + def test_call_noneasresult(self): pm = PluginManager() class api1: def m(self, call): - return call.NORESULT - class api2: - def m(self, call): - return 41 + return call.NONEASRESULT pm.register(api1()) pm.register(api1()) - pm.register(api2()) - assert pm.call_firstresult('m') == 41 - assert pm.call_each('m') == [41] + assert pm.call_firstresult('m') is None + assert pm.call_each('m') == [None, None] def test_iterattr(self): pm = PluginManager() Deleted: /py/branch/pytestplugin/py/misc/testing/test_event.py ============================================================================== --- /py/branch/pytestplugin/py/misc/testing/test_event.py Fri Feb 20 02:44:14 2009 +++ (empty file) @@ -1,72 +0,0 @@ - -import py -from py.__.misc.event import EventBus - -class TestEventBus: - def test_kw_register_notify_unregister(self): - bus = EventBus() - l = [] - bus.subscribe(hello=l.append) - bus.notify(some=1) - assert not l - bus.notify(hello=5) - assert len(l) == 1 - l.remove(5) - bus.unsubscribe(hello=l.append) - bus.notify(hello=7) - assert not l - - def test_kw_unregister_non_existing(self): - bus = EventBus() - py.test.raises(KeyError, "bus.unsubscribe(hello=42)") - bus.subscribe(hello=10) - bus.unsubscribe(hello=10) - py.test.raises(KeyError, "bus.unsubscribe(hello=42)") - - def test_kw_multiregister(self): - bus = EventBus() - l1 = [] - l2 = [] - bus.subscribe(hello=l1.append) - bus.subscribe(hello=l2.append) - bus.subscribe(world=l2.append) - bus.notify(some=1) - assert not l1 and not l2 - bus.notify(hello=5) - assert len(l1) == 1 - assert len(l2) == 1 - bus.notify(world=42) - assert len(l2) == 2 - bus.unsubscribe(hello=l2.append) - bus.unsubscribe(world=l2.append) - bus.unsubscribe(hello=l1.append) - - def test_simple_anonymous(self): - bus = EventBus() - l = [] - bus.subscribe(l.append) - bus.notify(hello=1) - assert l == [("hello", 1)] - bus.unsubscribe(l.append) - bus.notify(hello=1) - assert l == [("hello", 1)] - - def test_multi_anonymous(self): - bus = EventBus() - l1 = [] - l2 = [] - bus.subscribe(l1.append) - bus.subscribe(l2.append) - bus.notify(event=1) - bus.notify(event2=2) - bus.notify(event3=3) - assert l1 == [("event", 1), ("event2", 2), ("event3", 3)] - assert l2 == [("event", 1), ("event2", 2), ("event3", 3)] - bus.unsubscribe(l1.append) - bus.unsubscribe(l2.append) - py.test.raises(KeyError, "bus.unsubscribe(l1.append)") - py.test.raises(KeyError, "bus.unsubscribe(l2.append)") - - -def test_api(): - assert isinstance(py.event, EventBus) Deleted: /py/branch/pytestplugin/py/misc/testing/test_plugin.py ============================================================================== --- /py/branch/pytestplugin/py/misc/testing/test_plugin.py Fri Feb 20 02:44:14 2009 +++ (empty file) @@ -1,134 +0,0 @@ - -import py -import os -from py.__.misc.plugin import PluginManager -pytest_plugins = "xfail" - -class TestPluginManager: - def test_register(self): - pm = PluginManager() - class MyPlugin: - pass - my = MyPlugin() - pm.register(my) - assert my in pm.list - - @py.test.keywords(xfail=True) - def test_onregister(self): - pm = PluginManager() - l = [] - class MyApi: - def pyevent_pluginregistered(self, plugin): - l.append(plugin) - myapi = MyApi() - pm.register(myapi) - assert len(l) == 1 - assert l[0] is myapi - - def test_calleach(self): - pm = PluginManager() - class api1: - def m(self): - return 42 - class api2: - def m(self): - return 41 - pm.register(api1()) - pm.register(api2()) - l = pm.calleach('m') - l.sort() - assert l == [41, 42] - - def test_callfirst(self): - pm = PluginManager() - class api1: - def m(self): - return 42 - class api2: - def m(self): - return 41 - pm.register(api1()) - pm.register(api2()) - l = pm.callfirst('m') - assert l == [42] - - def test_callfirst_ignores_None(self): - pm = PluginManager() - class api1: - def m(self): - return None - class api2: - def m(self): - return 41 - pm.register(api1()) - pm.register(api1()) - pm.register(api1()) - pm.register(api2()) - pm.register(api1()) - pm.register(api1()) - l = pm.callfirst('m') - assert l == [41] - - - def test_iterattr(self): - pm = PluginManager() - class api1: - x = 42 - class api2: - x = 41 - pm.register(api1()) - pm.register(api2()) - l = list(pm.iterattr('x')) - l.sort() - assert l == [41, 42] - - def test_consider_env(self): - # XXX write a helper for preserving os.environ - pm = PluginManager() - KEY = "PYLIB" - old = os.environ.get(KEY, None) - try: - os.environ[KEY] = "unknownconsider_env" - py.test.raises(ImportError, "pm.consider_env()") - finally: - if old is None: - del os.environ[KEY] - else: - os.environ[KEY] = old - - def test_consider_module(self): - pm = PluginManager() - mod = py.std.new.module("temp") - mod.pylib = ["xxx nomod"] - excinfo = py.test.raises(ImportError, "pm.consider_module(mod)") - mod.pylib = "os" - l = [] - pm._bus.subscribe(importingmodule=l.append) - pm.consider_module(mod) - assert len(l) == 1 - assert l[0] == mod.pylib - -def test_api_and_defaults(): - assert isinstance(py.plugin, PluginManager) - assert py.plugin._bus is py.event - -def test_subprocess_env(): - # XXX write a helper for preserving os.environ - pm = PluginManager() - KEY = "PYLIB" - old = os.environ.get(KEY, None) - olddir = py.path.local(py.__file__).dirpath().dirpath().chdir() - try: - os.environ[KEY] = "unknownconsider_env" - excinfo = py.test.raises(py.process.cmdexec.Error, """ - py.process.cmdexec("python -c 'import py'") - """) - assert str(excinfo.value).find("ImportError") != -1 - assert str(excinfo.value).find("unknownconsider") != -1 - finally: - olddir.chdir() - if old is None: - del os.environ[KEY] - else: - os.environ[KEY] = old - Modified: py/branch/pytestplugin/py/misc/testing/test_warn.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_warn.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_warn.py Fri Feb 20 02:44:14 2009 @@ -49,4 +49,4 @@ def test_default(): from py.__.misc.warn import APIWARN - assert APIWARN.im_self._eventbus is py.event + assert APIWARN.im_self._eventbus is py._com.bus Modified: py/branch/pytestplugin/py/misc/warn.py ============================================================================== --- py/branch/pytestplugin/py/misc/warn.py (original) +++ py/branch/pytestplugin/py/misc/warn.py Fri Feb 20 02:44:14 2009 @@ -11,11 +11,11 @@ def __str__(self): return self.msg -# XXX probably only apiwarn() + py.event forwarding +# XXX probably only apiwarn() + py._com.bus forwarding # warn_explicit is actually needed class WarningBus(object): - def __init__(self, eventbus=py.event): + def __init__(self, eventbus=py._com.bus): self._eventbus = eventbus def subscribe(self, callable): Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Fri Feb 20 02:44:14 2009 @@ -75,8 +75,8 @@ def _initafterpickle(self, topdir): self.__init__( #issue1 - #bus=py.test._EventBus(bus=py.event), - #pluginmanager=py.test._PytestPluginManager(py.plugin) + #bus=py.test._EventBus(bus=py._com.bus), + #pluginmanager=py.test._PytestPluginManager(py._com.pluginmanager) ) self._initialized = True self.topdir = py.path.local(topdir) @@ -264,8 +264,9 @@ # this is the one per-process instance of py.test configuration config_per_process = Config( - bus=py.test._EventBus(bus=py.event), - pluginmanager=py.test._PytestPluginManager(py.plugin)) + bus=py.test._EventBus(bus=py._com.bus), + pluginmanager=py.test._PytestPluginManager(py._com.pluginmanager) +) # default import paths for sessions Modified: py/branch/pytestplugin/py/test/event.py ============================================================================== --- py/branch/pytestplugin/py/test/event.py (original) +++ py/branch/pytestplugin/py/test/event.py Fri Feb 20 02:44:14 2009 @@ -5,13 +5,12 @@ import py import time from py.__.test.outcome import Skipped -import py.__.misc.event class EventBus(object): """ Bus for distributing events. """ def __init__(self, bus=None): if bus is None: - bus = py.__.misc.event.EventBus() + bus = py._com.EventBus() self._bus = bus self._memo = [] Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Fri Feb 20 02:44:14 2009 @@ -5,9 +5,8 @@ class PytestPluginManager(object): def __init__(self, pm=None, _bus=None): - from py.__.misc.plugin import PluginManager if pm is None: - pm = PluginManager(bus=_bus) + pm = py._com.PluginManager(bus=_bus) #assert isinstance(pm, PluginManager) self.pm = pm self._plugins = {} @@ -52,11 +51,11 @@ for x in self.pm.iterattr(methname): return x - def callfirst(self, *args, **kwargs): - return self.pm.callfirst(*args, **kwargs) + def call_firstresult(self, *args, **kwargs): + return self.pm.call_firstresult(*args, **kwargs) - def calleach(self, *args, **kwargs): - return self.pm.calleach(*args, **kwargs) + def call_each(self, *args, **kwargs): + return self.pm.call_each(*args, **kwargs) def add_cmdlineoptions(self, config): @@ -69,18 +68,18 @@ def configure(self, config): def configureplugin(plugin): - self.pm.callone(plugin, "pytest_configure", config=config) + self.pm.callplugin(plugin, "pytest_configure", config=config) if hasattr(plugin, 'pytest_event'): config.bus._bus.subscribe(plugin.pytest_event) config.bus.subscribe_methods(plugin) self._configureplugin = configureplugin config.bus.subscribe(pluginregistered=self._configureplugin) - for plugin in self.pm.list: + for plugin in self.pm.plugins: configureplugin(plugin) def unconfigure(self, config): - for plugin in self.pm.list: - self.pm.callone(plugin, "pytest_unconfigure", config=config) + for plugin in self.pm.plugins: + self.pm.callplugin(plugin, "pytest_unconfigure", config=config) config.bus.close() del self._configureplugin Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Fri Feb 20 02:44:14 2009 @@ -65,9 +65,9 @@ self._tw.sep(sep, title, **markup) def getcategoryletterword(self, event): - reslist = self.config.pluginmanager.callfirst("pytest_report_teststatus", event=event) - if reslist: - return reslist[0] + res = self.config.pluginmanager.call_firstresult("pytest_report_teststatus", event=event) + if res: + return res for cat in 'skipped failed passed ???'.split(): if getattr(event, cat, None): break @@ -169,7 +169,7 @@ if ev.exitstatus in (0, 1, 2): self.summary_failures() self.summary_skips() - self.config.pluginmanager.calleach("pytest_terminal_summary", terminalreporter=self) + self.config.pluginmanager.call_each("pytest_terminal_summary", terminalreporter=self) if ev.excrepr is not None: self.summary_final_exc(ev.excrepr) if ev.exitstatus == 2: Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Fri Feb 20 02:44:14 2009 @@ -137,10 +137,10 @@ return self.join(name) def makeitem(self, name, obj): - reslist = self._config.pluginmanager.callfirst( + res = self._config.pluginmanager.call_firstresult( "pytest_pymodule_makeitem", modcol=self, name=name, obj=obj) - if reslist: - return reslist[0] + if res: + return res if (self.classnamefilter(name)) and \ py.std.inspect.isclass(obj): res = self._deprecated_join(name) @@ -355,23 +355,23 @@ def fillarg(self, argname, kwargs): value = argfinalizer = None - if hasattr(self.parent.obj, 'pytest_pyfunc_arg'): - result = self.parent.obj.pytest_pyfunc_arg(pyfuncitem=self, argname=argname) - if result: - value, argfinalizer = result - if value is None: - reslist = self._config.pluginmanager.callfirst( - "pytest_pyfunc_arg", pyfuncitem=self, argname=argname) - if reslist: - value, argfinalizer = reslist[0] - else: - print "pluginmanager is", self._config.pluginmanager - print "could not find argument %r, plugins=%r" %( - argname,self._config.pluginmanager._plugins) - raise TypeError("could not provide funcargument %r" %(argname,)) - kwargs[argname] = value - if argfinalizer is not None: - self._argfinalizers.append(argfinalizer) + call = py._com.Call( + self._config.pluginmanager.pm.plugins + [self.parent.obj], + "pytest_pyfunc_arg", pyfuncitem=self, argname=argname + ) + res = call.execute(firstresult=True) + if res: + value, argfinalizer = res + kwargs[argname] = value + if argfinalizer is not None: + self._argfinalizers.append(argfinalizer) + else: + print "pluginmanager is", self._config.pluginmanager + print "could not find argument %r, plugins=%r" %( + argname,self._config.pluginmanager._plugins) + for x in call.methods: + print x + raise TypeError("could not provide funcargument %r" %(argname,)) def __eq__(self, other): try: Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Fri Feb 20 02:44:14 2009 @@ -115,8 +115,8 @@ # have correct plugin initialization #XXX assert config2.pluginmanager.pm._plugins #XXX assert config2.bus.issubscribed(config2.pluginmanager.forward_event) - assert config2.bus._bus == py.event - assert config2.pluginmanager.pm == py.plugin + assert config2.bus._bus == py._com.bus + assert config2.pluginmanager.pm == py._com.pluginmanager def test_config_initafterpickle_some(): tmp = py.test.ensuretemp("test_config_initafterpickle_some") @@ -465,5 +465,5 @@ assert newcol2.fspath.relto(topdir) def test_default_bus(): - assert py.test.config.bus._bus is py.event + assert py.test.config.bus._bus is py._com.bus Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Fri Feb 20 02:44:14 2009 @@ -84,7 +84,7 @@ aplugin = testdir.makepyfile(pytest_a="""class A: pass""") pm = PytestPluginManager() l = [] - pm.pm._bus.subscribe(pluginregistered=l.append) + pm.pm.bus.subscribe(pluginregistered=l.append) #syspath.prepend(aplugin.dirpath()) py.std.sys.path.insert(0, str(aplugin.dirpath())) pm.consider_module(mod) @@ -92,7 +92,6 @@ assert len(l) == 1 pm.consider_module(mod) assert len(l) == 1 - import os, sys import py @@ -224,7 +223,7 @@ pm.callplugins("method", arg=42) py.test.raises(TypeError, 'pm.callplugins("method", arg=42, s=13)') - def test_callfirst(self): + def test_call_firstresult(self): pm = PytestPluginManager() class My1: def method(self): @@ -235,14 +234,14 @@ class My3: def method(self): return None - assert pm.callfirst("method") is None - assert pm.callfirst("methodnotexists") is None + assert pm.call_firstresult("method") is None + assert pm.call_firstresult("methodnotexists") is None pm.addpluginclass(My1) - assert pm.callfirst("method") is None + assert pm.call_firstresult("method") is None pm.addpluginclass(My2) - assert pm.callfirst("method") == True + assert pm.call_firstresult("method") == True pm.addpluginclass(My3) - assert pm.callfirst("method") == True + assert pm.call_firstresult("method") == True def test_listattr(self): pm = PytestPluginManager() From hpk at codespeak.net Fri Feb 20 03:47:45 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 20 Feb 2009 03:47:45 +0100 (CET) Subject: [py-svn] r62043 - in py/branch/pytestplugin/py: . misc/testing test test/testing Message-ID: <20090220024745.8D34E169EB3@codespeak.net> Author: hpk Date: Fri Feb 20 03:47:43 2009 New Revision: 62043 Removed: py/branch/pytestplugin/py/test/testing/plugintester.py Modified: py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/misc/testing/test_com.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py Log: * have Call object be based on methods * add callevent/subscribe/unsubscribe methods to PluginManager Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Fri Feb 20 03:47:43 2009 @@ -4,28 +4,20 @@ class Call: NONEASRESULT = object() - def __init__(self, callees, methname, *args, **kwargs): - self.callees = list(callees) - self.methname = methname + def __init__(self, methods, *args, **kwargs): + self.methods = methods self.args = args self.kwargs = kwargs self.results = [] - def listmethods(self): - l = [] - for callee in self.callees: - try: - l.append(getattr(callee, self.methname)) - except AttributeError: - continue - return l - def execute(self, firstresult=False): - self.methods = self.listmethods() while self.methods: self.currentmethod = self.methods.pop() - varnames = self.currentmethod.im_func.func_code.co_varnames - if varnames >1 and varnames[1] == 'call': + try: + varnames = self.currentmethod.im_func.func_code.co_varnames + except AttributeError: + varnames = () + if len(varnames) > 1 and varnames[1] == 'call': res = self.currentmethod(self, *self.args, **self.kwargs) else: res = self.currentmethod(*self.args, **self.kwargs) @@ -46,6 +38,7 @@ bus = EventBus() self.bus = bus self.plugins = [] + self._callbacks = [] def import_module(self, modspec): # XXX allow modspec to specify version / lookup @@ -75,27 +68,41 @@ #print "registering", self, plugin self.bus.notify(pluginregistered=plugin) - def iterattr(self, attrname): - for plugin in self.plugins: + def listattr(self, attrname, plugins=None, extra=()): + l = [] + if plugins is None: + plugins = self.plugins + if extra: + plugins += list(extra) + for plugin in plugins: try: - yield getattr(plugin, attrname) + l.append(getattr(plugin, attrname)) except AttributeError: continue + return l - #def Call(self, methname, *args, **kwargs): - # """ return call object for executing a plugin call. """ - # return Call(self.plugins, methname, args, kwargs) + def call_each(self, methname, *args, **kwargs): + """ return call object for executing a plugin call. """ + return Call(self.listattr(methname), *args, **kwargs).execute() def call_firstresult(self, methname, *args, **kwargs): """ return first non-None result of a plugin method. """ - return Call(self.plugins, methname, *args, **kwargs).execute(firstresult=True) - - def call_each(self, methname, *args, **kwargs): - """ return call object for executing a plugin call. """ - return Call(self.plugins, methname, *args, **kwargs).execute() + return Call(self.listattr(methname), *args, **kwargs).execute(firstresult=True) def callplugin(self, plugin, methname, *args, **kwargs): - return Call([plugin], methname, *args, **kwargs).execute(firstresult=True) + return Call(self.listattr(methname, plugins=[plugin]), + *args, **kwargs).execute(firstresult=True) + + def callevent(self, eventname, *args, **kwargs): + return Call( + self.listattr("pyevent_" + eventname) + self.listattr('pyevent') + + self._callbacks, *args, **kwargs).execute() + + def subscribe(self, callback): + self._callbacks.append(callback) + + def unsubscribe(self, callback): + self._callbacks.remove(callback) class EventBus(object): """ Event Bus for distributing named and unnamed events. """ Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Fri Feb 20 03:47:43 2009 @@ -24,7 +24,7 @@ p1 = P1() p2 = P2() - call = Call([p1, p2], 'm', 23) + call = Call([p1.m, p2.m], 23) reslist = call.execute() assert len(reslist) == 2 # ensure reversed order @@ -34,7 +34,7 @@ class P1: def m(self, x): return x - call = Call([P1()], 'm', 23) + call = Call([P1().m], 23) assert call.execute() == [23] assert call.execute(firstresult=True) == 23 @@ -108,7 +108,7 @@ assert pm.call_firstresult('m') is None assert pm.call_each('m') == [None, None] - def test_iterattr(self): + def test_listattr(self): pm = PluginManager() class api1: x = 42 @@ -116,10 +116,24 @@ x = 41 pm.register(api1()) pm.register(api2()) - l = list(pm.iterattr('x')) + l = list(pm.listattr('x')) l.sort() assert l == [41, 42] + def test_callevent(self): + pm = PluginManager() + class api1: + def pyevent_some(self, x): return x + 1 + class api2: + def pyevent(self, x): return x + 2 + l = [] + pm.register(api1()) + pm.register(api2()) + pm.subscribe(l.append) + res = pm.callevent('some', 0) + assert res == [2, 1] + assert l == [0] + def test_consider_env(self): # XXX write a helper for preserving os.environ pm = PluginManager() Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Fri Feb 20 03:47:43 2009 @@ -48,7 +48,7 @@ # # def getfirst(self, methname): - for x in self.pm.iterattr(methname): + for x in self.pm.listattr(methname): return x def call_firstresult(self, *args, **kwargs): @@ -62,7 +62,7 @@ # XXX think about sorting/grouping of options from user-perspective #assert self.pm.list opts = [] - for options in self.pm.iterattr("pytest_cmdlineoptions"): + for options in self.pm.listattr("pytest_cmdlineoptions"): opts.extend(options) config.addoptions("ungrouped options added by plugins", *opts) Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Fri Feb 20 03:47:43 2009 @@ -355,9 +355,10 @@ def fillarg(self, argname, kwargs): value = argfinalizer = None + pm = self._config.pluginmanager.pm call = py._com.Call( - self._config.pluginmanager.pm.plugins + [self.parent.obj], - "pytest_pyfunc_arg", pyfuncitem=self, argname=argname + pm.listattr("pytest_pyfunc_arg", extra=[self.parent.obj]), + pyfuncitem=self, argname=argname ) res = call.execute(firstresult=True) if res: Deleted: /py/branch/pytestplugin/py/test/testing/plugintester.py ============================================================================== --- /py/branch/pytestplugin/py/test/testing/plugintester.py Fri Feb 20 03:47:43 2009 +++ (empty file) @@ -1,71 +0,0 @@ -import py -from py.__.test.pmanage import PluginManager - -# generic test methods - -def nocalls(impname): - pname = impname.split("_")[-1] - pm = PluginManager() - pm.import_plugin(impname) - plugin = pm.getplugin(pname) - assert plugin - callhooklist = [ - ("pytest_configure", "config"), - ("pytest_unconfigure", "config"), - ("pytest_report_teststatus", "event"), - ("pytest_terminal_summary", "terminalreporter"), - ("pytest_event", "event"), - ] - plugin_pytest_methods = dict([item for item in vars(plugin.__class__).items() - if item[0].startswith("pytest_")]) - print plugin_pytest_methods - for name, argnames in callhooklist: - if name in plugin_pytest_methods: - func = plugin_pytest_methods.pop(name) - for argname in argnames.split(","): - assert argname in func.func_code.co_varnames - try: - value = plugin_pytest_methods.pop("pytest_cmdlineoptions") - except KeyError: - pass - else: - assert isinstance(value, list) - assert not plugin_pytest_methods - -def setupfs(tmpdir): - tmpdir.join("test_x.py").write(py.code.Source(""" - import py - def test_pass(): - pass - def test_fail(): - assert 0 - def test_skip(): - py.test.skip('hello') - """)) - -def functional(impname, *cmdlineopts): - from py.__.test.config import Config - tmpdir = py.test.ensuretemp("autotest_" + impname) - pname = impname.split("_")[-1] - olddir = tmpdir.chdir() - try: - config = Config() - setupfs(tmpdir) - config.parse([tmpdir] + list(cmdlineopts)) - try: - config.pluginmanager.import_plugin(impname) - except ValueError: - pass # already registered - plugin = config.pluginmanager.getplugin(pname) - assert plugin - config.pluginmanager.configure(config) - session = config.initsession() - exitstatus = session.main() - config.pluginmanager.unconfigure(config) - return tmpdir - finally: - olddir.chdir() - -def plugintester(impname): - plugintester_nocalls(impname) - plugintester_standard_events(impname) Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Fri Feb 20 03:47:43 2009 @@ -44,7 +44,7 @@ def test_addcmdlineoptions(): class PseudoPM: - def iterattr(self, name): + def listattr(self, name): assert name == "pytest_cmdlineoptions" return [("xxx", [py.std.optparse.Option("--pseudopm", action="store", dest="x")])] From hpk at codespeak.net Fri Feb 20 12:15:41 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 20 Feb 2009 12:15:41 +0100 (CET) Subject: [py-svn] r62046 - in py/branch/pytestplugin/py: . misc/testing test test/testing Message-ID: <20090220111541.58861169E6C@codespeak.net> Author: hpk Date: Fri Feb 20 12:15:39 2009 New Revision: 62046 Modified: py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/misc/testing/test_com.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py Log: more regular naming of call methods Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Fri Feb 20 12:15:39 2009 @@ -89,11 +89,11 @@ """ return first non-None result of a plugin method. """ return Call(self.listattr(methname), *args, **kwargs).execute(firstresult=True) - def callplugin(self, plugin, methname, *args, **kwargs): + def call_plugin(self, plugin, methname, *args, **kwargs): return Call(self.listattr(methname, plugins=[plugin]), *args, **kwargs).execute(firstresult=True) - def callevent(self, eventname, *args, **kwargs): + def notify(self, eventname, *args, **kwargs): return Call( self.listattr("pyevent_" + eventname) + self.listattr('pyevent') + self._callbacks, *args, **kwargs).execute() Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Fri Feb 20 12:15:39 2009 @@ -81,8 +81,8 @@ assert 33 in reslist assert pm.call_each("notexist") == [] - assert pm.callplugin(api1(), 'm', x=12) == 12 - assert pm.callplugin(api2(), 't') is None + assert pm.call_plugin(api1(), 'm', x=12) == 12 + assert pm.call_plugin(api2(), 't') is None def test_call_none_is_no_result(self): pm = PluginManager() @@ -120,7 +120,7 @@ l.sort() assert l == [41, 42] - def test_callevent(self): + def test_notify(self): pm = PluginManager() class api1: def pyevent_some(self, x): return x + 1 @@ -130,7 +130,7 @@ pm.register(api1()) pm.register(api2()) pm.subscribe(l.append) - res = pm.callevent('some', 0) + res = pm.notify('some', 0) assert res == [2, 1] assert l == [0] Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Fri Feb 20 12:15:39 2009 @@ -68,7 +68,7 @@ def configure(self, config): def configureplugin(plugin): - self.pm.callplugin(plugin, "pytest_configure", config=config) + self.pm.call_plugin(plugin, "pytest_configure", config=config) if hasattr(plugin, 'pytest_event'): config.bus._bus.subscribe(plugin.pytest_event) config.bus.subscribe_methods(plugin) @@ -79,7 +79,7 @@ def unconfigure(self, config): for plugin in self.pm.plugins: - self.pm.callplugin(plugin, "pytest_unconfigure", config=config) + self.pm.call_plugin(plugin, "pytest_unconfigure", config=config) config.bus.close() del self._configureplugin Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Fri Feb 20 12:15:39 2009 @@ -212,16 +212,16 @@ pm.addpluginclass(My1) assert pm.getfirst("x") == 1 - def test_callplugins(self): + def test_call_plugins(self): pm = PytestPluginManager() class My: def method(self, arg): pass pm.addpluginclass(My) - py.test.raises(TypeError, 'pm.callplugins("method")') - py.test.raises(TypeError, 'pm.callplugins("method", 42)') - pm.callplugins("method", arg=42) - py.test.raises(TypeError, 'pm.callplugins("method", arg=42, s=13)') + py.test.raises(TypeError, 'pm.call_plugins("method")') + py.test.raises(TypeError, 'pm.call_plugins("method", 42)') + pm.call_plugins("method", arg=42) + py.test.raises(TypeError, 'pm.call_plugins("method", arg=42, s=13)') def test_call_firstresult(self): pm = PytestPluginManager() From hpk at codespeak.net Fri Feb 20 12:41:56 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 20 Feb 2009 12:41:56 +0100 (CET) Subject: [py-svn] r62047 - in py/branch/pytestplugin/py: . misc/testing Message-ID: <20090220114156.DF4C9169E92@codespeak.net> Author: hpk Date: Fri Feb 20 12:41:51 2009 New Revision: 62047 Modified: py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/misc/testing/test_com.py Log: refine and test new pluginmanager.notify mechanism Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Fri Feb 20 12:41:51 2009 @@ -94,13 +94,18 @@ *args, **kwargs).execute(firstresult=True) def notify(self, eventname, *args, **kwargs): - return Call( - self.listattr("pyevent_" + eventname) + self.listattr('pyevent') + - self._callbacks, *args, **kwargs).execute() + Call(self.listattr("pyevent_" + eventname), + *args, **kwargs).execute() + print "calling anonymous hooks", args, kwargs + Call(self._callbacks + self.listattr("pyevent"), + eventname, *args, **kwargs).execute() def subscribe(self, callback): self._callbacks.append(callback) + def issubscribed(self, callback): + return callback in self._callbacks + def unsubscribe(self, callback): self._callbacks.remove(callback) Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Fri Feb 20 12:41:51 2009 @@ -120,19 +120,20 @@ l.sort() assert l == [41, 42] - def test_notify(self): + def test_notify_anonymous_ordered(self): pm = PluginManager() + l = [] class api1: - def pyevent_some(self, x): return x + 1 + def pyevent_hello(self): + l.append("hellospecific") class api2: - def pyevent(self, x): return x + 2 - l = [] + def pyevent(self, name): + l.append("helloanonymous") pm.register(api1()) pm.register(api2()) pm.subscribe(l.append) - res = pm.notify('some', 0) - assert res == [2, 1] - assert l == [0] + pm.notify('hello') + assert l == ["hellospecific", "helloanonymous", "hello"] def test_consider_env(self): # XXX write a helper for preserving os.environ @@ -184,69 +185,34 @@ else: os.environ[KEY] = old -class TestEventBus: - def test_kw_register_notify_unregister(self): - bus = EventBus() +class TestPluginManagerEvents: + def test_pyevent_named_dispatch(self): + pm = PluginManager() l = [] - bus.subscribe(hello=l.append) - bus.notify(some=1) - assert not l - bus.notify(hello=5) - assert len(l) == 1 - l.remove(5) - bus.unsubscribe(hello=l.append) - bus.notify(hello=7) - assert not l - - def test_kw_unregister_non_existing(self): - bus = EventBus() - py.test.raises(KeyError, "bus.unsubscribe(hello=42)") - bus.subscribe(hello=10) - bus.unsubscribe(hello=10) - py.test.raises(KeyError, "bus.unsubscribe(hello=42)") - - def test_kw_multiregister(self): - bus = EventBus() - l1 = [] - l2 = [] - bus.subscribe(hello=l1.append) - bus.subscribe(hello=l2.append) - bus.subscribe(world=l2.append) - bus.notify(some=1) - assert not l1 and not l2 - bus.notify(hello=5) - assert len(l1) == 1 - assert len(l2) == 1 - bus.notify(world=42) - assert len(l2) == 2 - bus.unsubscribe(hello=l2.append) - bus.unsubscribe(world=l2.append) - bus.unsubscribe(hello=l1.append) + class A: + def pyevent_name(self, x): + l.append(x) + pm.register(A()) + pm.notify("name", 13) + assert l == [13] - def test_simple_anonymous(self): - bus = EventBus() + def test_pyevent_anonymous_dispatch(self): + pm = PluginManager() l = [] - bus.subscribe(l.append) - bus.notify(hello=1) - assert l == [("hello", 1)] - bus.unsubscribe(l.append) - bus.notify(hello=1) - assert l == [("hello", 1)] - - def test_multi_anonymous(self): - bus = EventBus() - l1 = [] - l2 = [] - bus.subscribe(l1.append) - bus.subscribe(l2.append) - bus.notify(event=1) - bus.notify(event2=2) - bus.notify(event3=3) - assert l1 == [("event", 1), ("event2", 2), ("event3", 3)] - assert l2 == [("event", 1), ("event2", 2), ("event3", 3)] - bus.unsubscribe(l1.append) - bus.unsubscribe(l2.append) - py.test.raises(KeyError, "bus.unsubscribe(l1.append)") - py.test.raises(KeyError, "bus.unsubscribe(l2.append)") - + class A: + def pyevent(self, *args, **kwargs): + l.append((args, kwargs)) + + pm.register(A()) + pm.notify("name", 13, x=15) + assert l == [(("name", 13), {'x':15})] + def test_pyevent_subscription(self): + pm = PluginManager() + l = [] + pm.subscribe(l.append) + assert pm.issubscribed(l.append) + pm.notify("hello") + assert l == ['hello'] + pm.unsubscribe(l.append) + assert not pm.issubscribed(l.append) From hpk at codespeak.net Fri Feb 20 19:31:17 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 20 Feb 2009 19:31:17 +0100 (CET) Subject: [py-svn] r62059 - in py/branch/pytestplugin/py: . misc misc/testing test test/dsession test/dsession/testing test/looponfail test/plugin test/testing Message-ID: <20090220183117.0F865169E45@codespeak.net> Author: hpk Date: Fri Feb 20 19:31:15 2009 New Revision: 62059 Removed: py/branch/pytestplugin/py/test/testing/test_event.py Modified: py/branch/pytestplugin/py/__init__.py py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/initpkg.py py/branch/pytestplugin/py/misc/testing/test_com.py py/branch/pytestplugin/py/misc/testing/test_warn.py py/branch/pytestplugin/py/misc/warn.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/dsession/dsession.py py/branch/pytestplugin/py/test/dsession/hostmanage.py py/branch/pytestplugin/py/test/dsession/masterslave.py py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py py/branch/pytestplugin/py/test/event.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/looponfail/remote.py py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/session.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py Log: * getting rid of EventManager in favour of one class, currently named PluginManager * porting all tests and code to the new generalized event/plugin-call style * basing more event matching on names instead of types Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Fri Feb 20 19:31:15 2009 @@ -57,7 +57,6 @@ # py lib events and plugins '_com.PluginManager' : ('./_com.py', 'PluginManager'), - '_com.EventBus' : ('./_com.py', 'EventBus'), '_com.Call' : ('./_com.py', 'Call'), '_com.pluginmanager' : ('./_com.py', 'pluginmanager'), '_com.bus' : ('./_com.py', 'bus'), @@ -73,7 +72,6 @@ # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), - 'test._EventBus' : ('./test/event.py', 'EventBus'), 'test._PytestPluginManager' : ('./test/handleplugin.py', 'PytestPluginManager'), 'test.raises' : ('./test/outcome.py', 'raises'), 'test.keywords' : ('./test/outcome.py', 'keywords',), Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Fri Feb 20 19:31:15 2009 @@ -33,17 +33,14 @@ return self.results[0] class PluginManager: - def __init__(self, bus=None): - if bus is None: - bus = EventBus() - self.bus = bus + def __init__(self): self.plugins = [] self._callbacks = [] def import_module(self, modspec): # XXX allow modspec to specify version / lookup modpath = modspec - self.bus.notify(importingmodule=modpath) + self.notify("importingmodule", modpath) __import__(modpath) def consider_env(self, varname="PYLIB"): @@ -66,7 +63,7 @@ assert not isinstance(plugin, str) self.plugins.append(plugin) #print "registering", self, plugin - self.bus.notify(pluginregistered=plugin) + self.notify("pluginregistered", plugin) def listattr(self, attrname, plugins=None, extra=()): l = [] @@ -94,9 +91,10 @@ *args, **kwargs).execute(firstresult=True) def notify(self, eventname, *args, **kwargs): + #print "notifying", eventname, args, kwargs Call(self.listattr("pyevent_" + eventname), *args, **kwargs).execute() - print "calling anonymous hooks", args, kwargs + #print "calling anonymous hooks", args, kwargs Call(self._callbacks + self.listattr("pyevent"), eventname, *args, **kwargs).execute() @@ -109,45 +107,5 @@ def unsubscribe(self, callback): self._callbacks.remove(callback) -class EventBus(object): - """ Event Bus for distributing named and unnamed events. """ - def __init__(self): - self._subscribers = {} - - def _getsubscribers(self, key): - try: - return self._subscribers[key] - except KeyError: - x = self._subscribers[key] = [] - return x - - def subscribe(self, callback=None, **kwcallback): - """ subscribe callback to bus events. """ - if callback: - self._getsubscribers('').append(callback) - assert not kwcallback - else: - assert len(kwcallback) == 1 - name, call = kwcallback.popitem() - self._getsubscribers(name).append(call) - - def unsubscribe(self, callback=None, **kwcallback): - """ unsubscribe given callback from bus events. """ - try: - if callback is not None: - self._getsubscribers('').remove(callback) - else: - for name, call in kwcallback.items(): - self._getsubscribers(name).remove(call) - except ValueError, e: - raise KeyError(*e.args) - - def notify(self, **kw): - for name in kw: - for callback in self._getsubscribers(name): - callback(kw[name]) - for callback in self._getsubscribers(''): - callback((name, kw[name])) - pluginmanager = PluginManager() -bus = pluginmanager.bus +bus = pluginmanager Modified: py/branch/pytestplugin/py/initpkg.py ============================================================================== --- py/branch/pytestplugin/py/initpkg.py (original) +++ py/branch/pytestplugin/py/initpkg.py Fri Feb 20 19:31:15 2009 @@ -273,5 +273,5 @@ ENVKEY = pkgname.upper() + "_AUTOIMPORT" if ENVKEY in os.environ: for impname in os.environ[ENVKEY].split(","): - py._com.bus.notify(autoimport=impname) + py._com.bus.notify("autoimport", impname) __import__(impname) Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Fri Feb 20 19:31:15 2009 @@ -1,7 +1,7 @@ import py import os -from py._com import PluginManager, EventBus, Call +from py._com import PluginManager, Call pytest_plugins = "xfail" @@ -127,8 +127,9 @@ def pyevent_hello(self): l.append("hellospecific") class api2: - def pyevent(self, name): - l.append("helloanonymous") + def pyevent(self, name, *args): + if name == "hello": + l.append(name + "anonymous") pm.register(api1()) pm.register(api2()) pm.subscribe(l.append) @@ -155,15 +156,17 @@ mod.pylib = ["xxx nomod"] excinfo = py.test.raises(ImportError, "pm.consider_module(mod)") mod.pylib = "os" - l = [] - pm.bus.subscribe(importingmodule=l.append) + class Events(list): + def pyevent_importingmodule(self, mod): + self.append(mod) + l = Events() + pm.register(l) pm.consider_module(mod) assert len(l) == 1 - assert l[0] == mod.pylib + assert l[0] == (mod.pylib) def test_api_and_defaults(): assert isinstance(py._com.pluginmanager, PluginManager) - assert isinstance(py._com.bus, EventBus) def test_subprocess_env(): # XXX write a helper for preserving os.environ @@ -200,12 +203,13 @@ pm = PluginManager() l = [] class A: - def pyevent(self, *args, **kwargs): - l.append((args, kwargs)) + def pyevent(self, name, *args, **kwargs): + if name == "name": + l.extend([args, kwargs]) pm.register(A()) pm.notify("name", 13, x=15) - assert l == [(("name", 13), {'x':15})] + assert l == [(13, ), {'x':15}] def test_pyevent_subscription(self): pm = PluginManager() Modified: py/branch/pytestplugin/py/misc/testing/test_warn.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_warn.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_warn.py Fri Feb 20 19:31:15 2009 @@ -1,20 +1,20 @@ import py -from py.__.misc.warn import WarningBus +from py.__.misc.warn import WarningPlugin mypath = py.magic.autopath() -class TestWarningBus: +class TestWarningPlugin: def setup_method(self, method): - self.wb = WarningBus() + self.bus = py._com.PluginManager() + self.wb = WarningPlugin(self.bus) + self.bus.register(self) self.warnings = [] - self.wb.subscribe(self.warnings.append) - def test_basic(self): + def pyevent_WARNING(self, warning): + self.warnings.append(warning) + + def test_event_generation(self): self.wb.warn("hello") assert len(self.warnings) == 1 - self.wb.unsubscribe(self.warnings.append) - self.wb.warn("this") - assert len(self.warnings) == 1 - w = self.warnings[0] def test_location(self): self.wb.warn("again") @@ -27,19 +27,16 @@ assert str(warning) == warning.msg def test_stacklevel(self): - l = [] - self.wb.subscribe(l.append) def f(): self.wb.warn("x", stacklevel=2) - # 5 - # 6 + # 3 + # 4 f() - lno = self.test_stacklevel.im_func.func_code.co_firstlineno + 7 - warning = l[0] + lno = self.test_stacklevel.im_func.func_code.co_firstlineno + 5 + warning = self.warnings[0] assert warning.lineno == lno def test_forwarding_to_warnings_module(self): - self.wb._setforwarding() py.test.deprecated_call(self.wb.warn, "x") def test_apiwarn(self): @@ -49,4 +46,4 @@ def test_default(): from py.__.misc.warn import APIWARN - assert APIWARN.im_self._eventbus is py._com.bus + assert APIWARN.im_self in py._com.pluginmanager.plugins Modified: py/branch/pytestplugin/py/misc/warn.py ============================================================================== --- py/branch/pytestplugin/py/misc/warn.py (original) +++ py/branch/pytestplugin/py/misc/warn.py Fri Feb 20 19:31:15 2009 @@ -1,5 +1,4 @@ import py, sys -from py.__.test.event import EventBus class Warning(py.std.exceptions.DeprecationWarning): def __init__(self, msg, path, lineno): @@ -14,20 +13,13 @@ # XXX probably only apiwarn() + py._com.bus forwarding # warn_explicit is actually needed -class WarningBus(object): - def __init__(self, eventbus=py._com.bus): - self._eventbus = eventbus - - def subscribe(self, callable): - self._eventbus.subscribe(warning=callable) - - def unsubscribe(self, callable): - self._eventbus.unsubscribe(warning=callable) - - def _setforwarding(self): - self._eventbus.subscribe(warning=self._forward) - - def _forward(self, warning): +class WarningPlugin(object): + def __init__(self, bus): + self.bus = bus + bus.register(self) + + def pyevent_WARNING(self, warning): + # forward to python warning system py.std.warnings.warn_explicit(warning, category=Warning, filename=str(warning.path), lineno=warning.lineno, @@ -70,9 +62,8 @@ filename = module path = py.path.local(filename) warning = Warning(msg, path, lineno) - self._eventbus.notify(warning=warning) + self.bus.notify("WARNING", warning) # singleton api warner for py lib -apiwarner = WarningBus() -apiwarner._setforwarding() +apiwarner = WarningPlugin(py._com.pluginmanager) APIWARN = apiwarner.apiwarn Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Fri Feb 20 19:31:15 2009 @@ -29,16 +29,14 @@ Option = optparse.Option _initialized = False - def __init__(self, bus=None, pluginmanager=None): + def __init__(self, pluginmanager=None): self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") - if bus is None: - bus = py.test._EventBus() - self.bus = bus if pluginmanager is None: - pluginmanager = py.test._PytestPluginManager(_bus=bus._bus) + pluginmanager = py.test._PytestPluginManager() assert isinstance(pluginmanager, py.test._PytestPluginManager) + self.bus = pluginmanager.pm self.pluginmanager = pluginmanager self._conftest = Conftest(onimport=self.pluginmanager.consider_module) @@ -75,7 +73,6 @@ def _initafterpickle(self, topdir): self.__init__( #issue1 - #bus=py.test._EventBus(bus=py._com.bus), #pluginmanager=py.test._PytestPluginManager(py._com.pluginmanager) ) self._initialized = True @@ -264,7 +261,6 @@ # this is the one per-process instance of py.test configuration config_per_process = Config( - bus=py.test._EventBus(bus=py._com.bus), pluginmanager=py.test._PytestPluginManager(py._com.pluginmanager) ) Modified: py/branch/pytestplugin/py/test/dsession/dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/dsession.py Fri Feb 20 19:31:15 2009 @@ -8,8 +8,8 @@ from py.__.test import event import py.__.test.custompdb from py.__.test.dsession.hostmanage import HostManager -Item = (py.test.collect.Item, py.test.collect.Item) -Collector = (py.test.collect.Collector, py.test.collect.Collector) +Item = py.test.collect.Item +Collector = py.test.collect.Collector from py.__.test.runner import basic_run_report, basic_collect_report from py.__.test.session import Session from py.__.test import outcome @@ -43,10 +43,6 @@ self._testsfailed = False self.trace = config.maketrace("dsession.log") - def queueput(self, **kw): - assert len(kw) == 1 - self.queue.put(kw) - def fixoptions(self): """ check, fix and determine conflicting options. """ option = self.config.option @@ -101,30 +97,32 @@ # we use a timeout here so that control-C gets through while 1: try: - kw = self.queue.get(timeout=2.0) + eventcall = self.queue.get(timeout=2.0) break except Queue.Empty: continue loopstate.dowork = True - self.bus.notify(**kw) - assert len(kw) == 1 - name, ev = kw.items()[0] - if isinstance(ev, event.ItemTestReport): + + print "eventcall", eventcall + eventname, args, kwargs = eventcall + self.bus.notify(eventname, *args, **kwargs) + ev, = args + if eventname == "itemtestreport": self.removeitem(ev.colitem) if ev.failed: loopstate.testsfailed = True - elif isinstance(ev, event.CollectionReport): + elif eventname == "collectionreport": if ev.passed: colitems.extend(ev.result) - elif isinstance(ev, event.HostUp): + elif eventname == "hostup": self.addhost(ev.host) - elif isinstance(ev, event.HostDown): + elif eventname == "hostdown": pending = self.removehost(ev.host) if pending: crashitem = pending.pop(0) self.handle_crashitem(crashitem, ev.host) colitems.extend(pending) - elif isinstance(ev, event.RescheduleItems): + elif eventname == "rescheduleitems": colitems.extend(ev.items) loopstate.dowork = False # avoid busywait @@ -139,10 +137,10 @@ def loop_once_shutdown(self, loopstate): # once we are in shutdown mode we dont send # events other than HostDown upstream - kw = self.queue.get() - name, ev = kw.items()[0] - if name == "hostdown": - self.bus.notify(hostdown=ev) + eventname, args, kwargs = self.queue.get() + if eventname == "hostdown": + ev, = args + self.bus.notify("hostdown", ev) self.removehost(ev.host) if not self.host2pending: # finished @@ -163,7 +161,7 @@ except KeyboardInterrupt: exitstatus = outcome.EXIT_INTERRUPTED except: - self.bus.notify(internalerror=event.InternalException()) + self.bus.notify("internalerror", event.InternalException()) exitstatus = outcome.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: exitstatus = outcome.EXIT_TESTSFAILED @@ -191,11 +189,13 @@ if isinstance(next, Item): senditems.append(next) else: - self.bus.notify(collectionstart=event.CollectionStart(next)) - ev = basic_collect_report(next) - self.queueput(collectionreport=ev) + self.bus.notify("collectionstart", event.CollectionStart(next)) + self.queueevent("collectionreport", basic_collect_report(next)) self.senditems(senditems) + def queueevent(self, eventname, *args, **kwargs): + self.queue.put((eventname, args, kwargs)) + def senditems(self, tosend): if not tosend: return @@ -210,14 +210,14 @@ # "sending same item %r to multiple hosts " # "not implemented" %(item,)) self.item2host[item] = host - self.bus.notify(itemstart=event.ItemStart(item, host)) + self.bus.notify("itemstart", event.ItemStart(item, host)) pending.extend(sending) tosend[:] = tosend[room:] # update inplace if not tosend: break if tosend: # we have some left, give it to the main loop - self.queueput(rescheduleitems=event.RescheduleItems(tosend)) + self.queueevent("rescheduleitems", event.RescheduleItems(tosend)) def removeitem(self, item): if item not in self.item2host: @@ -229,15 +229,12 @@ def handle_crashitem(self, item, host): longrepr = "!!! Host %r crashed during running of test %r" %(host, item) rep = event.ItemTestReport(item, when="???", excinfo=longrepr) - self.bus.notify(itemtestreport=rep) + self.bus.notify("itemtestreport", rep) def setup_hosts(self): """ setup any neccessary resources ahead of the test run. """ self.hm = HostManager(self) - def kwnotify(**kw): - assert len(kw) == 1 - self.queue.put(kw) - self.hm.setup_hosts(notify=kwnotify) + self.hm.setup_hosts(putevent=self.queue.put) def teardown_hosts(self): """ teardown any resources after a test run. """ Modified: py/branch/pytestplugin/py/test/dsession/hostmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/hostmanage.py (original) +++ py/branch/pytestplugin/py/test/dsession/hostmanage.py Fri Feb 20 19:31:15 2009 @@ -148,7 +148,7 @@ def prepare_gateways(self): for host in self.hosts: host.initgateway() - self.session.bus.notify(hostgatewayready=event.HostGatewayReady(host, self.roots)) + self.session.bus.notify("hostgatewayready", event.HostGatewayReady(host, self.roots)) def init_rsync(self): self.prepare_gateways() @@ -164,16 +164,14 @@ for host in self.hosts: rsync.add_target_host(host, destrelpath) rsync.send(raises=False) - self.session.bus.notify(rsyncfinished=event.RsyncFinished()) + self.session.bus.notify("rsyncfinished", event.RsyncFinished()) - def setup_hosts(self, notify=None): - if notify is None: - notify = self.session.bus.notify + def setup_hosts(self, putevent): self.init_rsync() for host in self.hosts: host.node = MasterNode(host, self.session.config, - notify) + putevent) # # helpers Modified: py/branch/pytestplugin/py/test/dsession/masterslave.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/masterslave.py (original) +++ py/branch/pytestplugin/py/test/dsession/masterslave.py Fri Feb 20 19:31:15 2009 @@ -8,16 +8,19 @@ class MasterNode(object): ENDMARK = -1 - def __init__(self, host, config, notify): + def __init__(self, host, config, putevent): self.host = host self.config = config - self.notify = notify + self.putevent = putevent self.channel = install_slave(host, config) self.channel.setcallback(self.callback, endmarker=self.ENDMARK) self._down = False + + def notify(self, eventname, *args, **kwargs): + self.putevent((eventname, args, kwargs)) - def callback(self, ev): - """ this gets called for each item we receive from + def callback(self, eventcall): + """ this gets called for each object we receive from the other side and if the channel closes. Note that the callback runs in the receiver @@ -25,31 +28,27 @@ avoid raising exceptions or doing heavy work. """ try: - if ev == self.ENDMARK: + if eventcall == self.ENDMARK: err = self.channel._getremoteerror() if not self._down: if not err: err = "TERMINATED" - self.notify(hostdown=event.HostDown(self.host, err)) + self.notify("hostdown", event.HostDown(self.host, err)) return - if ev is None: + elif eventcall is None: self._down = True - self.notify(hostdown=event.HostDown(self.host, None)) + self.notify("hostdown", event.HostDown(self.host, None)) return except KeyboardInterrupt: raise except: excinfo = py.code.ExceptionInfo() print "!" * 20, excinfo - self.notify(internalerror=event.InternalException(excinfo)) + self.notify("internalerror", event.InternalException(excinfo)) else: # XXX we need to have the proper event name - if isinstance(ev, event.HostUp): - self.notify(hostup=ev) - elif isinstance(ev, event.ItemTestReport): - self.notify(itemtestreport=ev) - elif isinstance(ev, event.HostDown): - self.notify(hostdown=ev) + eventname, args, kwargs = eventcall + self.notify(eventname, *args, **kwargs) def send(self, item): assert item is not None @@ -108,23 +107,24 @@ def __repr__(self): host = getattr(self, 'host', '') - return "<%s host=%s>" %(self.__class__.__name__, host.hostid) + return "<%s host=%s>" %(self.__class__.__name__, host) + + def sendevent(self, eventname, *args, **kwargs): + self.channel.send((eventname, args, kwargs)) def run(self): from py.__.test.dsession.hostmanage import makehostup channel = self.channel self.host = host = channel.receive() - channel.send(makehostup(host)) + self.sendevent("hostup", makehostup(host)) self.trace = self.config.maketrace(host.hostid) self.trace("initialized") - try: while 1: task = channel.receive() self.trace("received", task) - if task is None: # shutdown - channel.send(None) + self.channel.send(None) self.trace("shutting down, send None to", channel) break if isinstance(task, list): @@ -135,9 +135,8 @@ except KeyboardInterrupt: raise except: - rep = event.InternalException() + self.sendevent("internalerror", event.InternalException()) self.trace("sending back internal exception report, breaking loop") - channel.send(rep) raise else: self.trace("normal shutdown") @@ -145,5 +144,5 @@ def runtest(self, item): runner = item._getrunner() testrep = runner(item) - self.channel.send(testrep) - self.trace("sent back testreport", testrep) + self.sendevent("itemtestreport", testrep) + self.trace("notified testreport", testrep) Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py Fri Feb 20 19:31:15 2009 @@ -68,9 +68,9 @@ """) session = DSession(modcol._config) session.triggertesting([modcol]) - kw = session.queue.get(block=False) - name, rep = kw.items()[0] + name, args, kwargs = session.queue.get(block=False) assert name == 'collectionreport' + rep, = args assert isinstance(rep, event.CollectionReport) assert len(rep.result) == 1 @@ -90,9 +90,9 @@ assert host2_sent == [item] * session.MAXITEMSPERHOST assert session.host2pending[host1] == host1_sent assert session.host2pending[host2] == host2_sent - kw = session.queue.get(block=False) - name, ev = kw.items()[0] - assert isinstance(ev, event.RescheduleItems) + name, args, kwargs = session.queue.get(block=False) + assert name == "rescheduleitems" + ev, = args assert ev.items == [item] def test_keyboardinterrupt(self, testdir): @@ -119,17 +119,17 @@ session.addhost(host1) ev = event.RescheduleItems([item]) loopstate = LoopState([]) - session.queueput(rescheduleitems=ev) + session.queueevent("rescheduleitems", ev) session.loop_once(loopstate) # check that RescheduleEvents are not immediately # rescheduled if there are no hosts assert loopstate.dowork == False - session.queueput(anonymous=event.NOP()) + session.queueevent("anonymous", event.NOP()) session.loop_once(loopstate) - session.queueput(anonymous=event.NOP()) + session.queueevent("anonymous", event.NOP()) session.loop_once(loopstate) assert host1.node.sent == [[item]] - session.queueput(itemtestreport=run(item)) + session.queueevent("itemtestreport", run(item)) session.loop_once(loopstate) assert loopstate.shuttingdown assert not loopstate.testsfailed @@ -144,7 +144,7 @@ # setup a HostDown event ev = event.HostDown(host1, None) - session.queueput(hostdown=ev) + session.queueevent("hostdown", ev) loopstate = LoopState([item]) loopstate.dowork = False @@ -174,7 +174,7 @@ session.senditems([item1, item2]) host = session.item2host[item1] ev = event.HostDown(host, None) - session.queueput(hostdown=ev) + session.queueevent("hostdown", ev) evrec = EventRecorder(session.bus) loopstate = LoopState([]) session.loop_once(loopstate) @@ -192,7 +192,7 @@ session = DSession(item._config) host1 = Host("localhost") hostup = makehostup(host1) - session.queueput(hostup=hostup) + session.queueevent("hostup", hostup) loopstate = LoopState([item]) loopstate.dowork = False assert len(session.host2pending) == 0 @@ -205,7 +205,7 @@ ev = event.NOP() evrec = EventRecorder(session.bus) - session.queueput(anonymous=ev) + session.queueevent("anonymous", ev) session.loop_once(LoopState([])) assert evrec.getfirstnamed('anonymous') == ev @@ -216,15 +216,15 @@ session.addhost(host1) loopstate = LoopState([item]) - session.queueput(anonymous=event.NOP()) + session.queueevent("anonymous", event.NOP()) session.loop_once(loopstate) assert host1.node.sent == [[item]] ev = run(item) - session.queueput(itemtestreport=ev) + session.queueevent("itemtestreport", ev) session.loop_once(loopstate) assert loopstate.shuttingdown - session.queueput(hostdown=event.HostDown(host1, None)) + session.queueevent("hostdown", event.HostDown(host1, None)) session.loop_once(loopstate) dumpqueue(session.queue) return session, loopstate.exitstatus @@ -259,8 +259,8 @@ # run tests ourselves and produce reports ev1 = run(items[0]) ev2 = run(items[1]) - session.queueput(itemtestreport=ev1) # a failing one - session.queueput(itemtestreport=ev2) + session.queueevent("itemtestreport", ev1) # a failing one + session.queueevent("itemtestreport", ev2) # now call the loop loopstate = LoopState(items) session.loop_once(loopstate) @@ -275,11 +275,11 @@ loopstate = LoopState([]) loopstate.shuttingdown = True evrec = EventRecorder(session.bus) - session.queueput(itemtestreport=run(item)) + session.queueevent("itemtestreport", run(item)) session.loop_once(loopstate) assert not evrec.events ev = event.HostDown(host) - session.queueput(hostdown=ev) + session.queueevent("hostdown", ev) session.loop_once(loopstate) assert evrec.getfirstnamed('hostdown') == ev @@ -320,13 +320,13 @@ host.node = MockNode() session.addhost(host) session.senditems([item]) - session.queueput(itemtestreport=run(item)) + session.queueevent("itemtestreport", run(item)) loopstate = LoopState([]) session.loop_once(loopstate) assert host.node._shutdown is True assert loopstate.exitstatus is None, "loop did not wait for hostdown" assert loopstate.shuttingdown - session.queueput(hostdown=event.HostDown(host, None)) + session.queueevent("hostdown", event.HostDown(host, None)) session.loop_once(loopstate) assert loopstate.exitstatus == 0 @@ -347,10 +347,10 @@ session.senditems([item1]) # host2pending will become empty when the loop sees # the report - session.queueput(itemtestreport=run(item1)) + session.queueevent("itemtestreport", run(item1)) # but we have a collection pending - session.queueput(collectionreport=colreport) + session.queueevent("collectionreport", colreport) loopstate = LoopState([]) session.loop_once(loopstate) Modified: py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py Fri Feb 20 19:31:15 2009 @@ -6,25 +6,10 @@ from py.__.test import event from py.__.test.dsession.dsession import DSession from py.__.test.dsession.hostmanage import HostManager, Host +from test_masterslave import EventQueue + import os -def eventreader(session): - queue = py.std.Queue.Queue() - session.bus.subscribe(queue.put) - def readevent(eventtype=event.ItemTestReport, timeout=2.0): - events = [] - while 1: - try: - evname, ev = queue.get(timeout=timeout) - except py.std.Queue.Empty: - print "seen events", events - raise IOError("did not see %r events" % (eventtype)) - else: - if isinstance(ev, eventtype): - #print "other events seen", events - return ev - events.append(ev) - return readevent class TestAsyncFunctional: def test_dist_no_disthost(self, testdir): @@ -51,9 +36,9 @@ print p1 config = testdir.parseconfig('-n1', p1, '--forcegen') dsession = DSession(config) - readevent = eventreader(dsession) + eq = EventQueue(config.bus) dsession.main() - ev = readevent(event.ItemTestReport) + ev, = eq.geteventargs("itemtestreport") if not ev.passed: print ev assert 0 @@ -71,18 +56,18 @@ """) config = testdir.parseconfig('-d', p1) dsession = DSession(config) - readevent = eventreader(dsession) + eq = EventQueue(config.bus) dsession.main([config.getfsnode(p1)]) - ev = readevent(event.ItemTestReport) + ev, = eq.geteventargs("itemtestreport") assert ev.passed - ev = readevent(event.ItemTestReport) + ev, = eq.geteventargs("itemtestreport") assert ev.skipped - ev = readevent(event.ItemTestReport) + ev, = eq.geteventargs("itemtestreport") assert ev.failed # see that the host is really down - ev = readevent(event.HostDown) + ev, = eq.geteventargs("hostdown") assert ev.host.hostname == "localhost" - ev = readevent(event.TestrunFinish) + ev, = eq.geteventargs("testrunfinish") def test_distribution_rsync_roots_example(self, testdir): py.test.skip("testing for root rsync needs rework") Modified: py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py Fri Feb 20 19:31:15 2009 @@ -281,19 +281,17 @@ session = py.test.config._reparse([source]).initsession() hm = HostManager(session, hosts=hosts) queue = py.std.Queue.Queue() - def queueput(**kw): - queue.put(kw) - hm.setup_hosts(notify=queueput) + hm.setup_hosts(putevent=queue.put) for host in hm.hosts: - kw = queue.get(timeout=2.0) - name, ev = kw.items()[0] - assert isinstance(ev, event.HostUp) + eventcall = queue.get(timeout=2.0) + name, args, kwargs = eventcall + assert name == "hostup" for host in hm.hosts: host.node.shutdown() for host in hm.hosts: - kw = queue.get(timeout=2.0) - name, ev = kw.items()[0] - assert isinstance(ev, event.HostDown) + eventcall = queue.get(timeout=2.0) + name, args, kwargs = eventcall + assert name == "hostdown" def XXXtest_ssh_rsync_samehost_twice(self): #XXX we have no easy way to have a temp directory remotely! Modified: py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py Fri Feb 20 19:31:15 2009 @@ -4,29 +4,40 @@ from py.__.test.dsession.hostmanage import Host from py.__.test import event -class MySetup: - def __init__(self, pyfuncitem): - self.pyfuncitem = pyfuncitem +class EventQueue: + def __init__(self, bus, queue=None): + if queue is None: + queue = py.std.Queue.Queue() + self.queue = queue + bus.register(self) + + def pyevent(self, eventname, *args, **kwargs): + self.queue.put((eventname, args, kwargs)) - def getevent(self, eventtype=event.ItemTestReport, timeout=2.0): + def geteventargs(self, eventname, timeout=2.0): events = [] while 1: try: - kw = self.queue.get(timeout=timeout) + eventcall = self.queue.get(timeout=timeout) except py.std.Queue.Empty: - print "node channel", self.node.channel - print "remoteerror", self.node.channel._getremoteerror() + #print "node channel", self.node.channel + #print "remoteerror", self.node.channel._getremoteerror() print "seen events", events - raise IOError("did not see %r events" % (eventtype)) + raise IOError("did not see %r events" % (eventname)) else: - assert len(kw) == 1 - name, ev = kw.items()[0] - if isinstance(ev, eventtype): - return ev - events.append(ev) + name, args, kwargs = eventcall + assert isinstance(name, str) + if name == eventname: + return args + events.append(name) + +class MySetup: + def __init__(self, pyfuncitem): + self.pyfuncitem = pyfuncitem - def queueput(self, **kw): - self.queue.put(kw) + def geteventargs(self, eventname, timeout=2.0): + eq = EventQueue(self.config.bus, self.queue) + return eq.geteventargs(eventname, timeout=timeout) def makenode(self, config=None): if config is None: @@ -35,7 +46,7 @@ self.queue = py.std.Queue.Queue() self.host = Host("localhost") self.host.initgateway() - self.node = MasterNode(self.host, self.config, self.queueput) + self.node = MasterNode(self.host, self.config, putevent=self.queue.put) assert not self.node.channel.isclosed() return self.node @@ -53,7 +64,7 @@ def test_crash_invalid_item(self, mysetup): node = mysetup.makenode() node.send(123) # invalid item - ev = mysetup.getevent(event.HostDown) + ev, = mysetup.geteventargs("hostdown") assert ev.host == mysetup.host assert str(ev.error).find("AttributeError") != -1 @@ -67,33 +78,33 @@ """) node = mysetup.makenode(item._config) node.send(item) - ev = mysetup.getevent(event.HostDown) + ev, = mysetup.geteventargs("hostdown") assert ev.host == mysetup.host assert str(ev.error).find("TERMINATED") != -1 def test_node_down(self, mysetup): node = mysetup.makenode() node.shutdown() - ev = mysetup.getevent(event.HostDown) + ev, = mysetup.geteventargs("hostdown") assert ev.host == mysetup.host assert not ev.error node.callback(node.ENDMARK) excinfo = py.test.raises(IOError, - "mysetup.getevent(event.HostDown, timeout=0.01)") + "mysetup.geteventargs('hostdown', timeout=0.01)") def test_send_on_closed_channel(self, testdir, mysetup): item = testdir.getitem("def test_func(): pass") node = mysetup.makenode(item._config) node.channel.close() py.test.raises(IOError, "node.send(item)") - #ev = self.getevent(event.InternalException) + #ev = self.geteventargs(event.InternalException) #assert ev.excinfo.errisinstance(IOError) def test_send_one(self, testdir, mysetup): item = testdir.getitem("def test_func(): pass") node = mysetup.makenode(item._config) node.send(item) - ev = mysetup.getevent() + ev, = mysetup.geteventargs("itemtestreport") assert ev.passed assert ev.colitem == item #assert event.item == item @@ -113,10 +124,10 @@ for item in items: node.send(item) for outcome in "passed failed skipped".split(): - ev = mysetup.getevent() + ev, = mysetup.geteventargs("itemtestreport") assert getattr(ev, outcome) node.sendlist(items) for outcome in "passed failed skipped".split(): - ev = mysetup.getevent() + ev, = mysetup.geteventargs("itemtestreport") assert getattr(ev, outcome) Modified: py/branch/pytestplugin/py/test/event.py ============================================================================== --- py/branch/pytestplugin/py/test/event.py (original) +++ py/branch/pytestplugin/py/test/event.py Fri Feb 20 19:31:15 2009 @@ -6,56 +6,6 @@ import time from py.__.test.outcome import Skipped -class EventBus(object): - """ Bus for distributing events. """ - def __init__(self, bus=None): - if bus is None: - bus = py._com.EventBus() - self._bus = bus - self._memo = [] - - def issubscribed(self, callback=None, **kw): - if callback is not None: - kw[''] = callback - for name, value in kw.items(): - if value in self._bus._getsubscribers(name): - return True - - def subscribe(self, callback=None, **kw): - """ subscribe given callback to bus events. """ - if not hasattr(self, '_memo'): - raise EnvironmentError("event bus already closed") - d = kw.copy() - if callback is not None: - d[''] = callback - self._memo.append(d) - #print "appending", d - self._bus.subscribe(callback, **kw) - - def close(self): - for kw in self._memo: - #print "closing", kw - if '' in kw: - callback = kw.pop('') - else: - callback = None - self.unsubscribe(callback, **kw) - del self._memo - - def subscribe_methods(self, instance, prefix = "pyevent_"): - for cls in py.std.inspect.getmro(instance.__class__): - for attrname in cls.__dict__: - if attrname.startswith(prefix): - name = attrname[len(prefix):] - self.subscribe(**{name: getattr(instance, attrname)}) - - def unsubscribe(self, callback=None, **kw): - """ unsubscribe given callback from bus events. """ - return self._bus.unsubscribe(callback, **kw) - - def notify(self, **kw): - self._bus.notify(**kw) - class BaseEvent(object): def __repr__(self): l = ["%s=%s" %(key, value) Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Fri Feb 20 19:31:15 2009 @@ -3,11 +3,12 @@ """ import py +# XXX rename PytestPluginManager to PytestCom(...) +# class PytestPluginManager(object): - def __init__(self, pm=None, _bus=None): + def __init__(self, pm=None): if pm is None: - pm = py._com.PluginManager(bus=_bus) - #assert isinstance(pm, PluginManager) + pm = py._com.PluginManager() self.pm = pm self._plugins = {} @@ -57,6 +58,14 @@ def call_each(self, *args, **kwargs): return self.pm.call_each(*args, **kwargs) + def notify(self, eventname, *args, **kwargs): + return self.pm.notify(eventname, *args, **kwargs) + + def subscribe(self, callback): + bus = self.pm.subscribe(callback) + + def unsubscribe(self, callback): + bus = self.pm.unsubscribe(callback) def add_cmdlineoptions(self, config): # XXX think about sorting/grouping of options from user-perspective @@ -66,22 +75,20 @@ opts.extend(options) config.addoptions("ungrouped options added by plugins", *opts) + def pyevent_pluginregistered(self, plugin): + if hasattr(self, '_config'): + self.pm.call_plugin(plugin, "pytest_configure", config=self._config) + def configure(self, config): - def configureplugin(plugin): - self.pm.call_plugin(plugin, "pytest_configure", config=config) - if hasattr(plugin, 'pytest_event'): - config.bus._bus.subscribe(plugin.pytest_event) - config.bus.subscribe_methods(plugin) - self._configureplugin = configureplugin - config.bus.subscribe(pluginregistered=self._configureplugin) - for plugin in self.pm.plugins: - configureplugin(plugin) + assert not hasattr(self, '_config') + config.bus.register(self) + self._config = config + self.pm.call_each("pytest_configure", config=self._config) def unconfigure(self, config): - for plugin in self.pm.plugins: - self.pm.call_plugin(plugin, "pytest_unconfigure", config=config) - config.bus.close() - del self._configureplugin + config = self._config + del self._config + self.pm.call_each("pytest_unconfigure", config=config) # # XXX old code to automatically load classes Modified: py/branch/pytestplugin/py/test/looponfail/remote.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/remote.py (original) +++ py/branch/pytestplugin/py/test/looponfail/remote.py Fri Feb 20 19:31:15 2009 @@ -149,20 +149,18 @@ else: colitems = None session.shouldclose = channel.isclosed - #def sendevent(ev): - # channel.send(ev) - #session.bus.subscribe(sendevent) - - failreports = [] - def recordfailures((evname, ev)): - if isinstance(ev, event.BaseReport): + + class Failures(list): + def pyevent_itemtestreport(self, ev): if ev.failed: - failreports.append(ev) - session.bus.subscribe(recordfailures) + self.append(ev) + pyevent_collectionreport = pyevent_itemtestreport + + failreports = Failures() + session.bus.register(failreports) DEBUG("SLAVE: starting session.main()") session.main(colitems) - session.bus.unsubscribe(recordfailures) - ev = event.LooponfailingInfo(failreports, [config.topdir]) - session.bus.notify(looponfailinginfo=ev) + ev = event.LooponfailingInfo(list(failreports), [config.topdir]) + session.bus.notify("looponfailinginfo", ev) channel.send([x.colitem._totrail() for x in failreports if x.failed]) Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Fri Feb 20 19:31:15 2009 @@ -16,10 +16,10 @@ self.eventlogfile.close() del self.eventlogfile - def pytest_event(self, (name, event)): + def pyevent(self, eventname, *args, **kwargs): if hasattr(self, 'eventlogfile'): f = self.eventlogfile - print >>f, event + print >>f, eventname, args, kwargs f.flush() # =============================================================================== Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Fri Feb 20 19:31:15 2009 @@ -187,7 +187,7 @@ bus.subscribe(self.record) self.debug = debug - def record(self, (name, event)): + def record(self, name, event): if self.debug: print "[event] %s: %s" %(name, event) self.events.append((name, event)) @@ -265,15 +265,15 @@ self.bus.unsubscribe(self.record) def test_eventrecorder(): - bus = event.EventBus() + bus = py._com.PluginManager() recorder = EventRecorder(bus) - bus.notify(anonymous=event.NOP()) + bus.notify("anonymous", event.NOP()) assert recorder.events assert not recorder.getfailures() rep = event.ItemTestReport(None, None) rep.passed = False rep.failed = True - bus.notify(itemtestreport=rep) + bus.notify("itemtestreport", rep) failures = recorder.getfailures() assert failures == [rep] failures = recorder.get(event.ItemTestReport) @@ -284,12 +284,12 @@ rep = event.ItemTestReport(None, None) rep.passed = False rep.skipped = True - bus.notify(itemtestreport=rep) + bus.notify("itemtestreport", rep) rep = event.CollectionReport(None, None) rep.passed = False rep.failed = True - bus.notify(itemtestreport=rep) + bus.notify("itemtestreport", rep) passed, skipped, failed = recorder.listoutcomes() assert not passed and skipped and failed @@ -303,7 +303,7 @@ assert not recorder.events assert not recorder.getfailures() recorder.unsubscribe() - bus.notify(itemtestreport=rep) + bus.notify("itemtestreport", rep) assert not recorder.events assert not recorder.getfailures() Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Fri Feb 20 19:31:15 2009 @@ -12,15 +12,13 @@ if resultlog: logfile = open(resultlog, 'w', 1) # line buffered self.resultlog = ResultLog(logfile) + config.bus.register(self.resultlog) def pytest_unconfigure(self, config): if hasattr(self, 'resultlog'): self.resultlog.logfile.close() del self.resultlog - - def pytest_event(self, (name, event)): - if hasattr(self, 'resultlog'): - self.resultlog.log_event_to_file(event) + #config.bus.unregister(self.resultlog) def generic_path(item): chain = item.listchain() @@ -76,21 +74,21 @@ longrepr = str(ev.longrepr.reprcrash.message) return code, longrepr - def log_outcome(self, ev): - if (not ev.passed or isinstance(ev, ev.ItemTestReport)): - gpath = generic_path(ev.colitem) - shortrepr, longrepr = self.getoutcomecodes(ev) + def log_outcome(self, event): + if (not event.passed or isinstance(event, event.ItemTestReport)): + gpath = generic_path(event.colitem) + shortrepr, longrepr = self.getoutcomecodes(event) self.write_log_entry(shortrepr, gpath, longrepr) - def log_event_to_file(self, ev): - if isinstance(ev, ev.ItemTestReport): - self.log_outcome(ev) - elif isinstance(ev, ev.CollectionReport): - if not ev.passed: - self.log_outcome(ev) - elif isinstance(ev, ev.InternalException): - path = ev.repr.reprcrash.path # fishing :( - self.write_log_entry('!', path, str(ev.repr)) + def pyevent(self, eventname, event, *args, **kwargs): + if eventname == "itemtestreport": + self.log_outcome(event) + elif eventname == "collectionreport": + if not event.passed: + self.log_outcome(event) + elif eventname == "internalerror": + path = event.repr.reprcrash.path # fishing :( + self.write_log_entry('!', path, str(event.repr)) # =============================================================================== # @@ -227,7 +225,7 @@ from py.__.test import event internal = event.InternalException(excinfo) reslog = ResultLog(StringIO.StringIO()) - reslog.log_event_to_file(internal) + reslog.pyevent("internalerror", event.InternalException(excinfo)) entry = reslog.logfile.getvalue() entry_lines = entry.splitlines() Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Fri Feb 20 19:31:15 2009 @@ -21,7 +21,7 @@ name = attr.split("_")[-1] assert hasattr(self.reporter._tw, name), name setattr(self.reporter._tw, name, getattr(config, attr)) - config.bus.subscribe_methods(self.reporter) + config.bus.register(self.reporter) class TerminalReporter: def __init__(self, config, file=None): @@ -325,16 +325,16 @@ assert 0 """) rep = TerminalReporter(modcol._config, file=linecomp.stringio) - registerdispatcher(rep) - rep.config.bus.notify(testrunstart=event.TestrunStart()) + rep.config.bus.register(rep) + rep.config.bus.notify("testrunstart", event.TestrunStart()) for item in testdir.genitems([modcol]): ev = basic_run_report(item) - rep.config.bus.notify(itemtestreport=ev) + rep.config.bus.notify("itemtestreport", ev) linecomp.assert_contains_lines([ "*test_pass_skip_fail.py .sF" ]) - rep.config.bus.notify(testrunfinish=event.TestrunFinish()) + rep.config.bus.notify("testrunfinish", event.TestrunFinish()) linecomp.assert_contains_lines([ " def test_func():", "> assert 0", @@ -352,21 +352,21 @@ assert 0 """, configargs=("-v",)) rep = TerminalReporter(modcol._config, file=linecomp.stringio) - registerdispatcher(rep) - rep.config.bus.notify(testrunstart=event.TestrunStart()) + rep.config.bus.register(rep) + rep.config.bus.notify("testrunstart", event.TestrunStart()) items = modcol.collect() for item in items: - rep.config.bus.notify(itemstart=event.ItemStart(item)) + rep.config.bus.notify("itemstart", event.ItemStart(item)) s = linecomp.stringio.getvalue().strip() assert s.endswith(item.name) - rep.config.bus.notify(itemtestreport=basic_run_report(item)) + rep.config.bus.notify("itemtestreport", basic_run_report(item)) linecomp.assert_contains_lines([ "*test_pass_skip_fail_verbose.py:2: *test_ok*PASS", "*test_pass_skip_fail_verbose.py:4: *test_skip*SKIP", "*test_pass_skip_fail_verbose.py:6: *test_func*FAIL", ]) - rep.config.bus.notify(testrunfinish=event.TestrunFinish()) + rep.config.bus.notify("testrunfinish", event.TestrunFinish()) linecomp.assert_contains_lines([ " def test_func():", "> assert 0", @@ -376,14 +376,14 @@ def test_collect_fail(self, testdir, linecomp): modcol = testdir.getmodulecol("import xyz") rep = TerminalReporter(modcol._config, file=linecomp.stringio) - registerdispatcher(rep) - rep.config.bus.notify(testrunstart=event.TestrunStart()) + rep.config.bus.register(rep) + rep.config.bus.notify("testrunstart", event.TestrunStart()) l = list(testdir.genitems([modcol])) assert len(l) == 0 linecomp.assert_contains_lines([ "*test_collect_fail.py F*" ]) - rep.config.bus.notify(testrunfinish=event.TestrunFinish()) + rep.config.bus.notify("testrunfinish", event.TestrunFinish()) linecomp.assert_contains_lines([ "> import xyz", "E ImportError: No module named xyz" @@ -455,11 +455,12 @@ g() # --calling-- """, configargs=("--tb=%s" % tbopt,)) rep = TerminalReporter(modcol._config, file=linecomp.stringio) - registerdispatcher(rep) - rep.config.bus.notify(testrunstart=event.TestrunStart()) + rep.config.bus.register(rep) + rep.config.bus.notify("testrunstart", event.TestrunStart()) + rep.config.bus.notify("testrunstart", event.TestrunStart()) for item in testdir.genitems([modcol]): - rep.config.bus.notify(itemtestreport=basic_run_report(item)) - rep.config.bus.notify(testrunfinish=event.TestrunFinish()) + rep.config.bus.notify("itemtestreport", basic_run_report(item)) + rep.config.bus.notify("testrunfinish", event.TestrunFinish()) s = linecomp.stringio.getvalue() if tbopt == "long": print s @@ -481,10 +482,10 @@ pass """) rep = TerminalReporter(modcol._config, file=linecomp.stringio) - registerdispatcher(rep) + modcol._config.bus.register(rep) l = list(testdir.genitems([modcol])) assert len(l) == 1 - rep.config.bus.notify(itemstart=event.ItemStart(l[0])) + rep.config.bus.notify("itemstart", event.ItemStart(l[0])) linecomp.assert_contains_lines([ "*test_show_path_before_running_test.py*" ]) @@ -499,12 +500,12 @@ raise KeyboardInterrupt # simulating the user """, configargs=("--showskipsummary",) + ("-v",)*verbose) rep = TerminalReporter(modcol._config, file=linecomp.stringio) - registerdispatcher(rep) + modcol._config.bus.register(rep) bus = modcol._config.bus - bus.notify(testrunstart=event.TestrunStart()) + bus.notify("testrunstart", event.TestrunStart()) try: for item in testdir.genitems([modcol]): - bus.notify(itemtestreport=basic_run_report(item)) + bus.notify("itemtestreport", basic_run_report(item)) except KeyboardInterrupt: excinfo = py.code.ExceptionInfo() else: @@ -512,7 +513,7 @@ s = linecomp.stringio.getvalue() if not verbose: assert s.find("_keyboard_interrupt.py Fs") != -1 - bus.notify(testrunfinish=event.TestrunFinish(exitstatus=2, excinfo=excinfo)) + bus.notify("testrunfinish", event.TestrunFinish(exitstatus=2, excinfo=excinfo)) text = linecomp.stringio.getvalue() linecomp.assert_contains_lines([ " def test_foobar():", @@ -560,19 +561,19 @@ pass """) rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) - registerdispatcher(rep) + modcol._config.bus.register(rep) indent = rep.indent - rep.config.bus.notify(collectionstart=event.CollectionStart(modcol)) + rep.config.bus.notify("collectionstart", event.CollectionStart(modcol)) linecomp.assert_contains_lines([ "" ]) item = modcol.join("test_func") - rep.config.bus.notify(itemstart=event.ItemStart(item)) + rep.config.bus.notify("itemstart", event.ItemStart(item)) linecomp.assert_contains_lines([ " ", ]) - rep.config.bus.notify( - collectionreport=event.CollectionReport(modcol, [], excinfo=None)) + rep.config.bus.notify( "collectionreport", + event.CollectionReport(modcol, [], excinfo=None)) assert rep.indent == indent def test_collectonly_skipped_module(self, testdir, linecomp): @@ -581,7 +582,7 @@ py.test.skip("nomod") """) rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) - registerdispatcher(rep) + modcol._config.bus.register(rep) cols = list(testdir.genitems([modcol])) assert len(cols) == 0 linecomp.assert_contains_lines(""" @@ -594,7 +595,7 @@ raise ValueError(0) """) rep = CollectonlyReporter(modcol._config, out=linecomp.stringio) - registerdispatcher(rep) + modcol._config.bus.register(rep) cols = list(testdir.genitems([modcol])) assert len(cols) == 0 linecomp.assert_contains_lines(""" @@ -610,7 +611,3 @@ assert repr_pythonversion() == str(x) finally: py.magic.revert(sys, 'version_info') - -def registerdispatcher(rep): - rep.config.bus.subscribe_methods(rep) - Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Fri Feb 20 19:31:15 2009 @@ -24,7 +24,7 @@ def __init__(self, config): self.config = config self.bus = config.bus # shortcut - self.bus.subscribe_methods(self) + self.bus.register(self) self._testsfailed = False self._nomatch = False @@ -49,16 +49,16 @@ if isinstance(next, Item): remaining = self.filteritems([next]) if remaining: - notify(itemstart=event.ItemStart(next)) + notify("itemstart", event.ItemStart(next)) yield next else: assert isinstance(next, Collector) - notify(collectionstart=event.CollectionStart(next)) + notify("collectionstart", event.CollectionStart(next)) ev = basic_collect_report(next) if ev.passed: for x in self.genitems(ev.result, keywordexpr): yield x - notify(collectionreport=ev) + notify("collectionreport", ev) def filteritems(self, colitems): """ return items to process (some may be deselected)""" @@ -76,7 +76,7 @@ continue remaining.append(colitem) if deselected: - self.bus.notify(deselected=event.Deselected(deselected, )) + self.bus.notify("deselected", event.Deselected(deselected, )) if self.config.option.keyword.endswith(":"): self._nomatch = True return remaining @@ -88,7 +88,7 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.bus.notify(testrunstart=event.TestrunStart()) + self.bus.notify("testrunstart", event.TestrunStart()) def pyevent_itemtestreport(self, rep): if rep.failed: @@ -99,8 +99,8 @@ def sessionfinishes(self, exitstatus=0, excinfo=None): """ teardown any resources after a test run. """ - self.bus.notify(testrunfinish=event.TestrunFinish(exitstatus=exitstatus, - excinfo=excinfo)) + self.bus.notify("testrunfinish", + event.TestrunFinish(exitstatus=exitstatus, excinfo=excinfo)) def getinitialitems(self, colitems): if colitems is None: @@ -113,7 +113,7 @@ colitems = self.getinitialitems(colitems) self.shouldstop = False self.sessionstarts() - self.bus.notify(hostup=makehostup()) + self.bus.notify("hostup", makehostup()) exitstatus = outcome.EXIT_OK captured_excinfo = None try: @@ -127,7 +127,7 @@ captured_excinfo = py.code.ExceptionInfo() exitstatus = outcome.EXIT_INTERRUPTED except: - self.bus.notify(internalerror=event.InternalException()) + self.bus.notify("internalerror", event.InternalException()) exitstatus = outcome.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: exitstatus = outcome.EXIT_TESTSFAILED @@ -141,4 +141,4 @@ runner = item._getrunner() pdb = self.config.option.usepdb and self.runpdb or None testrep = runner(item, pdb=pdb) - self.bus.notify(itemtestreport=testrep) + self.bus.notify("itemtestreport", testrep) Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Fri Feb 20 19:31:15 2009 @@ -115,7 +115,7 @@ # have correct plugin initialization #XXX assert config2.pluginmanager.pm._plugins #XXX assert config2.bus.issubscribed(config2.pluginmanager.forward_event) - assert config2.bus._bus == py._com.bus + assert config2.bus == py._com.bus assert config2.pluginmanager.pm == py._com.pluginmanager def test_config_initafterpickle_some(): @@ -465,5 +465,5 @@ assert newcol2.fspath.relto(topdir) def test_default_bus(): - assert py.test.config.bus._bus is py._com.bus + assert py.test.config.bus is py._com.bus Deleted: /py/branch/pytestplugin/py/test/testing/test_event.py ============================================================================== --- /py/branch/pytestplugin/py/test/testing/test_event.py Fri Feb 20 19:31:15 2009 +++ (empty file) @@ -1,108 +0,0 @@ -import py -from py.__.test.event import EventBus -from py.__.test import event -from py.__.code.testing.test_excinfo import TWMock - -class TestEventBus: - - def test_anonymous_subscription(self): - bus = EventBus() - l = [] - bus.subscribe(l.append) - bus.notify(x=1) - assert len(l) == 1 - assert l[0] == ('x', 1) - - def test_multi_sub(self): - bus = EventBus() - l1, l2 = [], [] - bus.subscribe(l1.append) - bus.subscribe(l2.append) - bus.notify(x=1) - bus.notify(x=2) - assert l1 == l2 - assert l1 == [('x', 1), ('x', 2)] - - def test_remove(self): - bus = EventBus() - l = [] - bus.subscribe(l.append) - bus.notify(x=1) - bus.unsubscribe(l.append) - bus.notify(x=22) - assert l == [('x', 1)] - - def test_subscribe_methods(self): - bus = EventBus() - class A: - def pyevent_hello(self, ev): pass - class B(A): pass - b = B() - bus.subscribe_methods(b) - assert bus.issubscribed(hello=b.pyevent_hello) - - def test_close_simple(self): - membus = EventBus() - bus = membus._bus - def f(x): pass - membus.subscribe(f) - membus.subscribe(hello=f) - assert len(bus._getsubscribers('hello')) == 1 - assert len(bus._getsubscribers('')) == 1 - membus.close() - assert len(bus._getsubscribers('hello')) == 0 - assert len(bus._getsubscribers('')) == 0 - py.test.raises(EnvironmentError, "membus.subscribe(hello=f)") - - @py.test.keywords(xfail=True) - def test_close_with_unsubscribe(self): - membus = EventBus() - bus = membus._bus - def f(x): pass - def g(x): pass - membus.subscribe(f) - membus.subscribe(hello=f) - membus.unsubscribe(f) - membus.unsubscribe(hello=f) - membus.subscribe(hello=g) - membus.close() - assert len(bus._getsubscribers('hello')) == 0 - assert len(bus._getsubscribers('')) == 0 - - -def test_event_attributes(): - for name, value in vars(event).items(): - if py.std.inspect.isclass(value) and issubclass(value, event.BaseEvent): - assert hasattr(event.BaseEvent, value.__name__) - -class TestItemTestReport: - def test_excinfo_is_longrepr(self, testdir): - modcol = testdir.getmodulecol("def test_ok(): pass") - ev = event.ItemTestReport(modcol, excinfo="hello") - twmock = TWMock() - ev.toterminal(twmock) - assert twmock.lines - assert twmock.lines[0] == "hello" - - def test_toterminal(self, testdir): - sorter = testdir.inline_runsource(""" - def test_one(): - assert 42 == 43 - """) - reports = sorter.get(event.ItemTestReport) - ev = reports[0] - assert ev.failed - twmock = TWMock() - ev.toterminal(twmock) - assert twmock.lines - twmock = TWMock() - ev.longrepr = "hello" - ev.toterminal(twmock) - assert twmock.lines[0] == "hello" - assert not twmock.lines[1:] - - ##assert ev.repr_run.find("AssertionError") != -1 - filepath = ev.colitem.fspath - #filepath , modpath = ev.itemrepr_path - assert str(filepath).endswith(".py") - #assert modpath.endswith("file_test.test_one") Modified: py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_handleplugin.py Fri Feb 20 19:31:15 2009 @@ -17,7 +17,6 @@ def test_configure(): config = pytestConfig() - pp = config.pluginmanager l = [] events = [] class A: @@ -26,37 +25,35 @@ def pyevent_hello(self, obj): events.append(obj) - pp.pm.register(A()) + config.bus.register(A()) assert len(l) == 0 - pp.configure(config=config) + config.pluginmanager.configure(config=config) assert len(l) == 1 - pp.pm.register(A()) + config.bus.register(A()) # this should lead to a configured() plugin assert len(l) == 2 assert l[0] != l[1] - config.bus.notify(hello=42) + config.bus.notify("hello", 42) assert len(events) == 2 assert events == [42,42] - pp.unconfigure(config=config) - pp.pm.register(A()) + config.pluginmanager.unconfigure(config=config) + config.bus.register(A()) assert len(l) == 2 def test_addcmdlineoptions(): - class PseudoPM: - def listattr(self, name): - assert name == "pytest_cmdlineoptions" - return [("xxx", - [py.std.optparse.Option("--pseudopm", action="store", dest="x")])] class PseudoConfig: opts = [] def addoptions(self, *opts): self.opts.append(opts) - pseudopm = PseudoPM() - pseudoconfig = PseudoConfig() - pc = PytestPluginManager(pseudopm) - pc.add_cmdlineoptions(pseudoconfig) - assert len(pseudoconfig.opts) == 1 + config = PseudoConfig() + pc = PytestPluginManager() + class A: + pytest_cmdlineoptions = [42] + pc.pm.register(A) + pc.add_cmdlineoptions(config) + assert len(config.opts) == 1 + assert config.opts[0][1] == 42 def test_canonical_names(): for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz': @@ -84,7 +81,7 @@ aplugin = testdir.makepyfile(pytest_a="""class A: pass""") pm = PytestPluginManager() l = [] - pm.pm.bus.subscribe(pluginregistered=l.append) + pm.pm.subscribe(lambda *args: l.append(args)) #syspath.prepend(aplugin.dirpath()) py.std.sys.path.insert(0, str(aplugin.dirpath())) pm.consider_module(mod) From hpk at codespeak.net Fri Feb 20 19:34:12 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 20 Feb 2009 19:34:12 +0100 (CET) Subject: [py-svn] r62060 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090220183412.1A5EC169E45@codespeak.net> Author: hpk Date: Fri Feb 20 19:34:11 2009 New Revision: 62060 Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Log: streamline test Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Fri Feb 20 19:34:11 2009 @@ -216,16 +216,13 @@ def test_internal_exception(self): # they are produced for example by a teardown failing # at the end of the run - + from py.__.test import event try: raise ValueError except ValueError: - excinfo = py.code.ExceptionInfo() - - from py.__.test import event - internal = event.InternalException(excinfo) + excinfo = event.InternalException() reslog = ResultLog(StringIO.StringIO()) - reslog.pyevent("internalerror", event.InternalException(excinfo)) + reslog.pyevent("internalerror", excinfo) entry = reslog.logfile.getvalue() entry_lines = entry.splitlines() From py-svn at codespeak.net Sat Feb 21 05:14:13 2009 From: py-svn at codespeak.net (Pakvyrsz) Date: Sat, 21 Feb 2009 05:14:13 +0100 (CET) Subject: [py-svn] Lindsay and Miley dirty twosome Message-ID: <20090221041413.EC8701684C7@codespeak.net> An HTML attachment was scrubbed... URL: From hpk at codespeak.net Sat Feb 21 13:08:43 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 21 Feb 2009 13:08:43 +0100 (CET) Subject: [py-svn] r62073 - py/branch/pytestplugin Message-ID: <20090221120843.8D7C11684CC@codespeak.net> Author: hpk Date: Sat Feb 21 13:08:41 2009 New Revision: 62073 Modified: py/branch/pytestplugin/TODO.txt Log: move out py.test TODOs to "freemap" (not committed at this point) Modified: py/branch/pytestplugin/TODO.txt ============================================================================== --- py/branch/pytestplugin/TODO.txt (original) +++ py/branch/pytestplugin/TODO.txt Sat Feb 21 13:08:41 2009 @@ -12,115 +12,6 @@ - small things: --nomagic -> --no-reassert - deprecate py.magic -py.test --------------- - -- compatilibity: honour/warn item.run() method of test items (so far - probably only execute() is warned about or the other way round) - -- introduce plugin arch, port existing things to plugins: - - OK filelog - - OK tmpdir / chtmpdir per method - - OK xfail - - OK unittest plugin - - OK in "pytest_plugins" specs allow for missing pytest prefix - - OK allow specifying pytest plugins from the environment - - OK/MOSTLY streamline assert_lines_contains_lines / result handling - - construct Argument object for sending/receiving of args to - test methods - - py.plugin: make subscriptions weakref'ed - - Pytest functiona arguments: provide the test function - source / head when reporting a "could not find test function argument" - - plugins: additionally initialize from $HOME/.py/pylib file? - extend or add another method to PluginManager.consider_env()? - - have plugins/hook cause log events / and have a debug plugin - - implement doctest plugin - - apigen - - plugin for more information about test environment - - plugin ordering? - - --traceconfig presenting info about conftests, plugins, ... - - consider porting py/doc/confrest to a plugin (mostly, also - consider pypy dependencies) - - ask guido to look at apigen & port javascript tests - - plugin for ReST reporting? - - put assert reinterpretation into plugin - - port py/test/web to py/test/plugin/pytest_webcheck.py - - cross-platform testing (testing from osx/linux to windows and vice versa?) - - deprecated_call and importorskip(), allow plugin-provided "py.test.*" hooks? - - acceptance/assertlines - - dist-testing? - - - normalize/use linecomp/assert_lines_... - - statt "fstester, pytester" etc. ein "testdir" plugin: - def test_something(testdir): - path = testdir.file.py = makefile(...) # will use test method name a a filename - #testdir.makefile(...) would raise an Exception becaue a file was created already - result = testdir.subprocess_runpytest(*args) - result.stdout.contains(...) - result.stdout.assert_contains_lines(...) - result.stderr.assert_contains_lines(...) - -- Testdir - -- DONE remove need for usefilters by directly checking if the - "to-be-filtered" file is an command line arg - (seems liek the original purpose for usefilters is exactly that) - -- consider py lib plugins: - - py_path_svn - py_path_execnet - - and generalize pytest plugin calling, for example: - - py.plugins.callfirst(...) - py.plugins.calleach(...) - - and also consider a command line tool: - - py.plugin list|ls - etc. - - -- introduce setuptools-style version checking, at least - for py lib itself, maybe also for other packages: - - py.checkversion("py>=1.0") - -- review/simplify py/doc/conftest.py - -- generative tests: it is somewhat misleading for - the classical htmlviews that generated tests are identified - by numbers rather than by its parameters. unclear how - to fix this because we may not always be able to assume - that we can identify a generated tests by using its parameters - (it might not be hashable, doesn't have a sensical repr ...?) - -- turn deprecation / apiwarnings into events, report them at the end? - -- get APIGEN back to work - -- get web reporter back to work - -- introduce decorator "shouldfail" or "xfail" - as to mark a test as "expected to fail", - report specially if it surprisingly passes - -- nightly test runs on multiple platforms - -- review and refactor architecture of py.test with particular - respect to: - - writing (stacked) extensions / plugins (compared to Nose) - - porting existing extensions (htmlconftest / buildbot / PyPy's conftest's ...) - - fast and stable distributed testing - - reliable cross-platform testing - -- improve py.test documentation to reflect new - event architecture - -- review and optimize skip-handling (it can be quite slow in - certain situations because e.g. setup/teardown is fully performed - although we have "skip by keyword" and could detect this early) py.execnet -------------- From hpk at codespeak.net Sat Feb 21 13:49:55 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 21 Feb 2009 13:49:55 +0100 (CET) Subject: [py-svn] r62074 - in py/branch/pytestplugin/py: . misc misc/testing test test/plugin test/testing Message-ID: <20090221124955.98EDA1684C5@codespeak.net> Author: hpk Date: Sat Feb 21 13:49:52 2009 New Revision: 62074 Modified: py/branch/pytestplugin/py/__init__.py py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/initpkg.py py/branch/pytestplugin/py/misc/testing/test_com.py py/branch/pytestplugin/py/misc/testing/test_warn.py py/branch/pytestplugin/py/misc/warn.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/testing/test_config.py Log: renaming and refinements: see e.g. py._com.MultiCall and py._com.PyPlugins docstrings. Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Sat Feb 21 13:49:52 2009 @@ -56,10 +56,9 @@ exportdefs = { # py lib events and plugins - '_com.PluginManager' : ('./_com.py', 'PluginManager'), - '_com.Call' : ('./_com.py', 'Call'), - '_com.pluginmanager' : ('./_com.py', 'pluginmanager'), - '_com.bus' : ('./_com.py', 'bus'), + '_com.PyPlugins' : ('./_com.py', 'PyPlugins'), + '_com.MultiCall' : ('./_com.py', 'MultiCall'), + '_com.pyplugins' : ('./_com.py', 'pyplugins'), # py lib cmdline tools 'cmdline.pytest' : ('./cmdline/pytest.py', 'main',), @@ -198,6 +197,6 @@ }) import py -py._com.pluginmanager.consider_env() +py._com.pyplugins.consider_env() Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Sat Feb 21 13:49:52 2009 @@ -1,7 +1,12 @@ - import py -class Call: +class MultiCall: + """ Manage a specific call into many python functions/methods. + + Simple example: + MultiCall([list1.append, list2.append], 42).execute() + """ + NONEASRESULT = object() def __init__(self, methods, *args, **kwargs): @@ -13,11 +18,12 @@ def execute(self, firstresult=False): while self.methods: self.currentmethod = self.methods.pop() + # provide call introspection if "__call__" is the first positional argument try: varnames = self.currentmethod.im_func.func_code.co_varnames except AttributeError: varnames = () - if len(varnames) > 1 and varnames[1] == 'call': + if varnames[:2] == ('self', '__call__'): res = self.currentmethod(self, *self.args, **self.kwargs) else: res = self.currentmethod(*self.args, **self.kwargs) @@ -32,7 +38,11 @@ if self.results: return self.results[0] -class PluginManager: +class PyPlugins: + """ + Manage Plugins: Load plugins and manage calls to plugins. + """ + def __init__(self): self.plugins = [] self._callbacks = [] @@ -80,22 +90,22 @@ def call_each(self, methname, *args, **kwargs): """ return call object for executing a plugin call. """ - return Call(self.listattr(methname), *args, **kwargs).execute() + return MultiCall(self.listattr(methname), *args, **kwargs).execute() def call_firstresult(self, methname, *args, **kwargs): """ return first non-None result of a plugin method. """ - return Call(self.listattr(methname), *args, **kwargs).execute(firstresult=True) + return MultiCall(self.listattr(methname), *args, **kwargs).execute(firstresult=True) def call_plugin(self, plugin, methname, *args, **kwargs): - return Call(self.listattr(methname, plugins=[plugin]), + return MultiCall(self.listattr(methname, plugins=[plugin]), *args, **kwargs).execute(firstresult=True) def notify(self, eventname, *args, **kwargs): #print "notifying", eventname, args, kwargs - Call(self.listattr("pyevent_" + eventname), + MultiCall(self.listattr("pyevent_" + eventname), *args, **kwargs).execute() #print "calling anonymous hooks", args, kwargs - Call(self._callbacks + self.listattr("pyevent"), + MultiCall(self._callbacks + self.listattr("pyevent"), eventname, *args, **kwargs).execute() def subscribe(self, callback): @@ -107,5 +117,4 @@ def unsubscribe(self, callback): self._callbacks.remove(callback) -pluginmanager = PluginManager() -bus = pluginmanager +pyplugins = PyPlugins() Modified: py/branch/pytestplugin/py/initpkg.py ============================================================================== --- py/branch/pytestplugin/py/initpkg.py (original) +++ py/branch/pytestplugin/py/initpkg.py Sat Feb 21 13:49:52 2009 @@ -273,5 +273,5 @@ ENVKEY = pkgname.upper() + "_AUTOIMPORT" if ENVKEY in os.environ: for impname in os.environ[ENVKEY].split(","): - py._com.bus.notify("autoimport", impname) + py._com.pyplugins.notify("autoimport", impname) __import__(impname) Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Sat Feb 21 13:49:52 2009 @@ -1,31 +1,31 @@ import py import os -from py._com import PluginManager, Call +from py._com import PyPlugins, MultiCall pytest_plugins = "xfail" -class TestCall: - def test_basic(self): +class TestMultiCall: + def test_call_passing(self): class P1: - def m(self, call, x): - assert call.currentmethod == self.m - assert len(call.results) == 1 - assert not call.methods + def m(self, __call__, x): + assert __call__.currentmethod == self.m + assert len(__call__.results) == 1 + assert not __call__.methods return 17 class P2: - def m(self, call, x): - assert call.currentmethod == self.m - assert call.args - assert call.results == [] - assert call.methods + def m(self, __call__, x): + assert __call__.currentmethod == self.m + assert __call__.args + assert __call__.results == [] + assert __call__.methods return 23 p1 = P1() p2 = P2() - call = Call([p1.m, p2.m], 23) - reslist = call.execute() + multicall = MultiCall([p1.m, p2.m], 23) + reslist = multicall.execute() assert len(reslist) == 2 # ensure reversed order assert reslist == [23, 17] @@ -34,13 +34,13 @@ class P1: def m(self, x): return x - call = Call([P1().m], 23) + call = MultiCall([P1().m], 23) assert call.execute() == [23] assert call.execute(firstresult=True) == 23 -class TestPluginManager: +class TestPyPlugins: def test_register(self): - pm = PluginManager() + pm = PyPlugins() class MyPlugin: pass my = MyPlugin() @@ -51,7 +51,7 @@ def test_onregister(self): py.test.skip("implement exitfirst plugin and " "modify xfail plugin to override exitfirst behaviour?") - pm = PluginManager() + pm = PyPlugins() l = [] class MyApi: def pyevent_pluginregistered(self, plugin): @@ -62,12 +62,12 @@ assert l[0] is myapi def test_call_methods(self): - pm = PluginManager() + pm = PyPlugins() class api1: - def m(self, call, x): + def m(self, __call__, x): return x class api2: - def m(self, call, x, y=33): + def m(self, __call__, x, y=33): return y pm.register(api1()) pm.register(api2()) @@ -85,12 +85,12 @@ assert pm.call_plugin(api2(), 't') is None def test_call_none_is_no_result(self): - pm = PluginManager() + pm = PyPlugins() class api1: - def m(self, call): + def m(self): return None class api2: - def m(self, call): + def m(self, __call__): return 41 pm.register(api1()) pm.register(api1()) @@ -99,17 +99,17 @@ assert pm.call_each('m') == [41] def test_call_noneasresult(self): - pm = PluginManager() + pm = PyPlugins() class api1: - def m(self, call): - return call.NONEASRESULT + def m(self, __call__): + return __call__.NONEASRESULT pm.register(api1()) pm.register(api1()) assert pm.call_firstresult('m') is None assert pm.call_each('m') == [None, None] def test_listattr(self): - pm = PluginManager() + pm = PyPlugins() class api1: x = 42 class api2: @@ -121,7 +121,7 @@ assert l == [41, 42] def test_notify_anonymous_ordered(self): - pm = PluginManager() + pm = PyPlugins() l = [] class api1: def pyevent_hello(self): @@ -138,7 +138,7 @@ def test_consider_env(self): # XXX write a helper for preserving os.environ - pm = PluginManager() + pm = PyPlugins() KEY = "XXPYLIB" old = os.environ.get(KEY, None) try: @@ -151,7 +151,7 @@ os.environ[KEY] = old def test_consider_module(self): - pm = PluginManager() + pm = PyPlugins() mod = py.std.new.module("temp") mod.pylib = ["xxx nomod"] excinfo = py.test.raises(ImportError, "pm.consider_module(mod)") @@ -166,11 +166,11 @@ assert l[0] == (mod.pylib) def test_api_and_defaults(): - assert isinstance(py._com.pluginmanager, PluginManager) + assert isinstance(py._com.pyplugins, PyPlugins) def test_subprocess_env(): # XXX write a helper for preserving os.environ - pm = PluginManager() + pm = PyPlugins() KEY = "PYLIB" old = os.environ.get(KEY, None) olddir = py.path.local(py.__file__).dirpath().dirpath().chdir() @@ -188,9 +188,9 @@ else: os.environ[KEY] = old -class TestPluginManagerEvents: +class TestPyPluginsEvents: def test_pyevent_named_dispatch(self): - pm = PluginManager() + pm = PyPlugins() l = [] class A: def pyevent_name(self, x): @@ -200,7 +200,7 @@ assert l == [13] def test_pyevent_anonymous_dispatch(self): - pm = PluginManager() + pm = PyPlugins() l = [] class A: def pyevent(self, name, *args, **kwargs): @@ -212,7 +212,7 @@ assert l == [(13, ), {'x':15}] def test_pyevent_subscription(self): - pm = PluginManager() + pm = PyPlugins() l = [] pm.subscribe(l.append) assert pm.issubscribed(l.append) Modified: py/branch/pytestplugin/py/misc/testing/test_warn.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_warn.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_warn.py Sat Feb 21 13:49:52 2009 @@ -4,7 +4,7 @@ class TestWarningPlugin: def setup_method(self, method): - self.bus = py._com.PluginManager() + self.bus = py._com.PyPlugins() self.wb = WarningPlugin(self.bus) self.bus.register(self) self.warnings = [] @@ -46,4 +46,4 @@ def test_default(): from py.__.misc.warn import APIWARN - assert APIWARN.im_self in py._com.pluginmanager.plugins + assert APIWARN.im_self in py._com.pyplugins.plugins Modified: py/branch/pytestplugin/py/misc/warn.py ============================================================================== --- py/branch/pytestplugin/py/misc/warn.py (original) +++ py/branch/pytestplugin/py/misc/warn.py Sat Feb 21 13:49:52 2009 @@ -10,7 +10,7 @@ def __str__(self): return self.msg -# XXX probably only apiwarn() + py._com.bus forwarding +# XXX probably only apiwarn() + py._com.pyplugins forwarding # warn_explicit is actually needed class WarningPlugin(object): @@ -65,5 +65,5 @@ self.bus.notify("WARNING", warning) # singleton api warner for py lib -apiwarner = WarningPlugin(py._com.pluginmanager) +apiwarner = WarningPlugin(py._com.pyplugins) APIWARN = apiwarner.apiwarn Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Sat Feb 21 13:49:52 2009 @@ -73,7 +73,7 @@ def _initafterpickle(self, topdir): self.__init__( #issue1 - #pluginmanager=py.test._PytestPluginManager(py._com.pluginmanager) + #pluginmanager=py.test._PytestPluginManager(py._com.pyplugins) ) self._initialized = True self.topdir = py.path.local(topdir) @@ -261,7 +261,7 @@ # this is the one per-process instance of py.test configuration config_per_process = Config( - pluginmanager=py.test._PytestPluginManager(py._com.pluginmanager) + pluginmanager=py.test._PytestPluginManager(py._com.pyplugins) ) # default import paths for sessions Modified: py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/handleplugin.py Sat Feb 21 13:49:52 2009 @@ -8,7 +8,7 @@ class PytestPluginManager(object): def __init__(self, pm=None): if pm is None: - pm = py._com.PluginManager() + pm = py._com.PyPlugins() self.pm = pm self._plugins = {} Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Sat Feb 21 13:49:52 2009 @@ -265,7 +265,7 @@ self.bus.unsubscribe(self.record) def test_eventrecorder(): - bus = py._com.PluginManager() + bus = py._com.PyPlugins() recorder = EventRecorder(bus) bus.notify("anonymous", event.NOP()) assert recorder.events Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Sat Feb 21 13:49:52 2009 @@ -356,7 +356,7 @@ def fillarg(self, argname, kwargs): value = argfinalizer = None pm = self._config.pluginmanager.pm - call = py._com.Call( + call = py._com.MultiCall( pm.listattr("pytest_pyfunc_arg", extra=[self.parent.obj]), pyfuncitem=self, argname=argname ) Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Sat Feb 21 13:49:52 2009 @@ -115,8 +115,8 @@ # have correct plugin initialization #XXX assert config2.pluginmanager.pm._plugins #XXX assert config2.bus.issubscribed(config2.pluginmanager.forward_event) - assert config2.bus == py._com.bus - assert config2.pluginmanager.pm == py._com.pluginmanager + assert config2.bus == py._com.pyplugins + assert config2.pluginmanager.pm == py._com.pyplugins def test_config_initafterpickle_some(): tmp = py.test.ensuretemp("test_config_initafterpickle_some") @@ -465,5 +465,5 @@ assert newcol2.fspath.relto(topdir) def test_default_bus(): - assert py.test.config.bus is py._com.bus + assert py.test.config.bus is py._com.pyplugins From hpk at codespeak.net Sat Feb 21 14:16:21 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 21 Feb 2009 14:16:21 +0100 (CET) Subject: [py-svn] r62075 - in py/branch/pytestplugin/py: . test test/plugin test/testing Message-ID: <20090221131621.C3B501684C1@codespeak.net> Author: hpk Date: Sat Feb 21 14:16:19 2009 New Revision: 62075 Added: py/branch/pytestplugin/py/test/pytestplugin.py (contents, props changed) - copied, changed from r62074, py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (contents, props changed) - copied, changed from r62059, py/branch/pytestplugin/py/test/testing/test_handleplugin.py Removed: py/branch/pytestplugin/py/test/handleplugin.py py/branch/pytestplugin/py/test/testing/test_handleplugin.py Modified: py/branch/pytestplugin/py/__init__.py py/branch/pytestplugin/py/test/cmdline.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/runner.py py/branch/pytestplugin/py/test/testing/test_config.py Log: some more py.test plugin related internal renamings Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Sat Feb 21 14:16:19 2009 @@ -71,7 +71,7 @@ # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), - 'test._PytestPluginManager' : ('./test/handleplugin.py', 'PytestPluginManager'), + 'test._PytestPlugins' : ('./test/pytestplugin.py', 'PytestPlugins'), 'test.raises' : ('./test/outcome.py', 'raises'), 'test.keywords' : ('./test/outcome.py', 'keywords',), 'test.deprecated_call' : ('./test/outcome.py', 'deprecated_call'), Modified: py/branch/pytestplugin/py/test/cmdline.py ============================================================================== --- py/branch/pytestplugin/py/test/cmdline.py (original) +++ py/branch/pytestplugin/py/test/cmdline.py Sat Feb 21 14:16:19 2009 @@ -10,10 +10,10 @@ args = py.std.sys.argv[1:] config = py.test.config config.parse(args) - config.pluginmanager.configure(config) + config.pytestplugins.configure(config) session = config.initsession() exitstatus = session.main() - config.pluginmanager.unconfigure(config) + config.pytestplugins.unconfigure(config) raise SystemExit(exitstatus) def warn_about_missing_assertion(): Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Sat Feb 21 14:16:19 2009 @@ -29,16 +29,16 @@ Option = optparse.Option _initialized = False - def __init__(self, pluginmanager=None): + def __init__(self, pytestplugins=None): self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") - if pluginmanager is None: - pluginmanager = py.test._PytestPluginManager() - assert isinstance(pluginmanager, py.test._PytestPluginManager) - self.bus = pluginmanager.pm - self.pluginmanager = pluginmanager - self._conftest = Conftest(onimport=self.pluginmanager.consider_module) + if pytestplugins is None: + pytestplugins = py.test._PytestPlugins() + assert isinstance(pytestplugins, py.test._PytestPlugins) + self.bus = pytestplugins.pyplugins + self.pytestplugins = pytestplugins + self._conftest = Conftest(onimport=self.pytestplugins.consider_module) def parse(self, args): """ parse cmdline arguments into this config object. @@ -49,10 +49,10 @@ self._initialized = True adddefaultoptions(self) self._conftest.setinitial(args) - #print "parse, seeing pluginmanager", self.pluginmanager.pm - #self.pluginmanager.setinitial(self._conftest.getconftestmodules(None)) - #print "plugins", self.pluginmanager.pm.list - self.pluginmanager.add_cmdlineoptions(self) + #print "parse, seeing pytestplugins", self.pytestplugins.pyplugins + #self.pytestplugins.setinitial(self._conftest.getconftestmodules(None)) + #print "plugins", self.pytestplugins.pyplugins.list + self.pytestplugins.add_cmdlineoptions(self) args = [str(x) for x in args] cmdlineoption, args = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) @@ -73,13 +73,13 @@ def _initafterpickle(self, topdir): self.__init__( #issue1 - #pluginmanager=py.test._PytestPluginManager(py._com.pyplugins) + #pytestplugins=py.test._PytestPlugins(py._com.pyplugins) ) self._initialized = True self.topdir = py.path.local(topdir) self._mergerepr(self._repr) del self._repr - self.pluginmanager.configure(config=self) + self.pytestplugins.configure(config=self) def _makerepr(self): l = [] @@ -261,7 +261,7 @@ # this is the one per-process instance of py.test configuration config_per_process = Config( - pluginmanager=py.test._PytestPluginManager(py._com.pyplugins) + pytestplugins=py.test._PytestPlugins(py._com.pyplugins) ) # default import paths for sessions Deleted: /py/branch/pytestplugin/py/test/handleplugin.py ============================================================================== --- /py/branch/pytestplugin/py/test/handleplugin.py Sat Feb 21 14:16:19 2009 +++ (empty file) @@ -1,117 +0,0 @@ -""" -handling py.test plugins. -""" -import py - -# XXX rename PytestPluginManager to PytestCom(...) -# -class PytestPluginManager(object): - def __init__(self, pm=None): - if pm is None: - pm = py._com.PyPlugins() - self.pm = pm - self._plugins = {} - - # API for bootstrapping - # - def getplugin(self, pname): - return self._plugins[pname.lower()] - - def setinitial(self, modules): - for module in modules: - self.consider_module(module) - - def consider_module(self, mod): - attr = getattr(mod, "pytest_plugins", ()) - if not isinstance(attr, (list, tuple)): - attr = (attr,) - for spec in attr: - if spec: - self.import_plugin(spec) - - def import_plugin(self, spec): - if isinstance(spec, str): - modname, clsname = canonical_names(spec) - if clsname.lower() not in self._plugins: - mod = importplugin(modname) - plugin = registerplugin(self.pm.register, mod, clsname) - self._plugins[clsname.lower()] = plugin - self.consider_module(mod) - else: - clsname = spec.__name__ - plugin = spec() - self.pm.register(plugin) - #print "registered", plugin, "at", self - - # - # - # API for interacting with registered and instantiated plugin objects - # - # - def getfirst(self, methname): - for x in self.pm.listattr(methname): - return x - - def call_firstresult(self, *args, **kwargs): - return self.pm.call_firstresult(*args, **kwargs) - - def call_each(self, *args, **kwargs): - return self.pm.call_each(*args, **kwargs) - - def notify(self, eventname, *args, **kwargs): - return self.pm.notify(eventname, *args, **kwargs) - - def subscribe(self, callback): - bus = self.pm.subscribe(callback) - - def unsubscribe(self, callback): - bus = self.pm.unsubscribe(callback) - - def add_cmdlineoptions(self, config): - # XXX think about sorting/grouping of options from user-perspective - #assert self.pm.list - opts = [] - for options in self.pm.listattr("pytest_cmdlineoptions"): - opts.extend(options) - config.addoptions("ungrouped options added by plugins", *opts) - - def pyevent_pluginregistered(self, plugin): - if hasattr(self, '_config'): - self.pm.call_plugin(plugin, "pytest_configure", config=self._config) - - def configure(self, config): - assert not hasattr(self, '_config') - config.bus.register(self) - self._config = config - self.pm.call_each("pytest_configure", config=self._config) - - def unconfigure(self, config): - config = self._config - del self._config - self.pm.call_each("pytest_unconfigure", config=config) - -# -# XXX old code to automatically load classes -# -def canonical_names(importspec): - importspec = importspec.lower() - modprefix = "pytest_" - if not importspec.startswith(modprefix): - importspec = modprefix + importspec - clsname = importspec[len(modprefix):].capitalize() - return importspec, clsname - -def registerplugin(registerfunc, mod, clsname): - pluginclass = getattr(mod, clsname) - plugin = pluginclass() - registerfunc(plugin) - return plugin - -def importplugin(importspec): - try: - return __import__(importspec) - except ImportError, e: - try: - return __import__("py.__.test.plugin.%s" %(importspec), None, None, '__doc__') - except ImportError: - raise ImportError(importspec) Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Sat Feb 21 14:16:19 2009 @@ -27,7 +27,7 @@ class PluginTester(Support): def testdir(self): # XXX import differently, eg. - # FSTester = self.pyfuncitem._config.pluginmanager.getpluginattr("pytester", "FSTester") + # FSTester = self.pyfuncitem._config.pytestplugins.getpluginattr("pytester", "FSTester") from pytest_pytester import TmpTestdir crunner = TmpTestdir(self.pyfuncitem) # @@ -35,10 +35,9 @@ return crunner def apicheck(self, impclass): - from py.__.test.handleplugin import PytestPluginManager print "loading and checking", impclass fail = False - pm = PytestPluginManager() + pm = py.test._PytestPlugins() plugin = pm.import_plugin(impclass) methods = collectattr(plugin.__class__) hooks = collectattr(PytestPluginHooks) Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Sat Feb 21 14:16:19 2009 @@ -85,19 +85,19 @@ def inline_runsession(self, session): config = session.config - config.pluginmanager.configure(config) + config.pytestplugins.configure(config) sorter = EventRecorder(config.bus) session.main() - config.pluginmanager.unconfigure(config) + config.pytestplugins.unconfigure(config) return sorter def inline_run(self, *args): config = self.parseconfig(*args) - config.pluginmanager.configure(config) + config.pytestplugins.configure(config) session = config.initsession() sorter = EventRecorder(config.bus) session.main() - config.pluginmanager.unconfigure(config) + config.pytestplugins.unconfigure(config) return sorter def parseconfig(self, *args): Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Sat Feb 21 14:16:19 2009 @@ -65,7 +65,7 @@ self._tw.sep(sep, title, **markup) def getcategoryletterword(self, event): - res = self.config.pluginmanager.call_firstresult("pytest_report_teststatus", event=event) + res = self.config.pytestplugins.call_firstresult("pytest_report_teststatus", event=event) if res: return res for cat in 'skipped failed passed ???'.split(): @@ -159,7 +159,7 @@ rev = py.__pkg__.getrev() self.write_line("using py lib: %s " % ( py.path.local(py.__file__).dirpath(), rev)) - plugins = ", ".join(list(self.config.pluginmanager._plugins)) + plugins = ", ".join(list(self.config.pytestplugins._plugins)) self.write_line("active plugins: %s" %(plugins,)) for i, testarg in py.builtin.enumerate(self.config.args): self.write_line("test object %d: %s" %(i+1, testarg)) @@ -169,7 +169,7 @@ if ev.exitstatus in (0, 1, 2): self.summary_failures() self.summary_skips() - self.config.pluginmanager.call_each("pytest_terminal_summary", terminalreporter=self) + self.config.pytestplugins.call_each("pytest_terminal_summary", terminalreporter=self) if ev.excrepr is not None: self.summary_final_exc(ev.excrepr) if ev.exitstatus == 2: Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Sat Feb 21 14:16:19 2009 @@ -137,7 +137,7 @@ return self.join(name) def makeitem(self, name, obj): - res = self._config.pluginmanager.call_firstresult( + res = self._config.pytestplugins.call_firstresult( "pytest_pymodule_makeitem", modcol=self, name=name, obj=obj) if res: return res @@ -169,7 +169,7 @@ # we assume we are only called once per module mod = self.fspath.pyimport() #print "imported test module", mod - self._config.pluginmanager.consider_module(mod) + self._config.pytestplugins.consider_module(mod) return mod def setup(self): @@ -330,7 +330,7 @@ """ execute the given test function. """ if not self._deprecated_testexecution(): kw = self.getkwargs() - pytest_pyfunc_call = self._config.pluginmanager.getfirst("pytest_pyfunc_call") + pytest_pyfunc_call = self._config.pytestplugins.getfirst("pytest_pyfunc_call") if pytest_pyfunc_call is not None: if pytest_pyfunc_call(pyfuncitem=self, args=self._args, kwargs=kw): return @@ -355,21 +355,21 @@ def fillarg(self, argname, kwargs): value = argfinalizer = None - pm = self._config.pluginmanager.pm - call = py._com.MultiCall( - pm.listattr("pytest_pyfunc_arg", extra=[self.parent.obj]), + plugins = self._config.pytestplugins.pyplugins + multicall = py._com.MultiCall( + plugins.listattr("pytest_pyfunc_arg", extra=[self.parent.obj]), pyfuncitem=self, argname=argname ) - res = call.execute(firstresult=True) + res = multicall.execute(firstresult=True) if res: value, argfinalizer = res kwargs[argname] = value if argfinalizer is not None: self._argfinalizers.append(argfinalizer) else: - print "pluginmanager is", self._config.pluginmanager + print "pytestplugins is", self._config.pytestplugins print "could not find argument %r, plugins=%r" %( - argname,self._config.pluginmanager._plugins) + argname,self._config.pytestplugins._plugins) for x in call.methods: print x raise TypeError("could not provide funcargument %r" %(argname,)) Copied: py/branch/pytestplugin/py/test/pytestplugin.py (from r62074, py/branch/pytestplugin/py/test/handleplugin.py) ============================================================================== --- py/branch/pytestplugin/py/test/handleplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Sat Feb 21 14:16:19 2009 @@ -3,13 +3,13 @@ """ import py -# XXX rename PytestPluginManager to PytestCom(...) +# XXX rename PytestPlugins to PytestCom(...) # -class PytestPluginManager(object): - def __init__(self, pm=None): - if pm is None: - pm = py._com.PyPlugins() - self.pm = pm +class PytestPlugins(object): + def __init__(self, pyplugins=None): + if pyplugins is None: + pyplugins = py._com.PyPlugins() + self.pyplugins = pyplugins self._plugins = {} # API for bootstrapping @@ -34,13 +34,13 @@ modname, clsname = canonical_names(spec) if clsname.lower() not in self._plugins: mod = importplugin(modname) - plugin = registerplugin(self.pm.register, mod, clsname) + plugin = registerplugin(self.pyplugins.register, mod, clsname) self._plugins[clsname.lower()] = plugin self.consider_module(mod) else: clsname = spec.__name__ plugin = spec() - self.pm.register(plugin) + self.pyplugins.register(plugin) #print "registered", plugin, "at", self # @@ -49,46 +49,46 @@ # # def getfirst(self, methname): - for x in self.pm.listattr(methname): + for x in self.pyplugins.listattr(methname): return x def call_firstresult(self, *args, **kwargs): - return self.pm.call_firstresult(*args, **kwargs) + return self.pyplugins.call_firstresult(*args, **kwargs) def call_each(self, *args, **kwargs): - return self.pm.call_each(*args, **kwargs) + return self.pyplugins.call_each(*args, **kwargs) def notify(self, eventname, *args, **kwargs): - return self.pm.notify(eventname, *args, **kwargs) + return self.pyplugins.notify(eventname, *args, **kwargs) def subscribe(self, callback): - bus = self.pm.subscribe(callback) + bus = self.pyplugins.subscribe(callback) def unsubscribe(self, callback): - bus = self.pm.unsubscribe(callback) + bus = self.pyplugins.unsubscribe(callback) def add_cmdlineoptions(self, config): # XXX think about sorting/grouping of options from user-perspective - #assert self.pm.list + #assert self.pyplugins.list opts = [] - for options in self.pm.listattr("pytest_cmdlineoptions"): + for options in self.pyplugins.listattr("pytest_cmdlineoptions"): opts.extend(options) config.addoptions("ungrouped options added by plugins", *opts) def pyevent_pluginregistered(self, plugin): if hasattr(self, '_config'): - self.pm.call_plugin(plugin, "pytest_configure", config=self._config) + self.pyplugins.call_plugin(plugin, "pytest_configure", config=self._config) def configure(self, config): assert not hasattr(self, '_config') config.bus.register(self) self._config = config - self.pm.call_each("pytest_configure", config=self._config) + self.pyplugins.call_each("pytest_configure", config=self._config) def unconfigure(self, config): config = self._config del self._config - self.pm.call_each("pytest_unconfigure", config=config) + self.pyplugins.call_each("pytest_unconfigure", config=config) # # XXX old code to automatically load classes Modified: py/branch/pytestplugin/py/test/runner.py ============================================================================== --- py/branch/pytestplugin/py/test/runner.py (original) +++ py/branch/pytestplugin/py/test/runner.py Sat Feb 21 14:16:19 2009 @@ -54,9 +54,9 @@ def teardown(self): self.colitem._setupstate.teardown_exact(self.colitem) def execute(self): - #self.colitem.config.pluginmanager.pre_execute(self.colitem) + #self.colitem.config.pytestplugins.pre_execute(self.colitem) self.colitem.runtest() - #self.colitem.config.pluginmanager.post_execute(self.colitem) + #self.colitem.config.pytestplugins.post_execute(self.colitem) def makereport(self, res, when, excinfo, outerr): testrep = event.ItemTestReport(self.colitem, excinfo, when, outerr) Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Sat Feb 21 14:16:19 2009 @@ -101,7 +101,7 @@ assert gettopdir([c]) == a assert gettopdir([c, Z]) == tmp - at py.test.keywords(xfail="issue1 config's pluginmanager/bus initialization") + at py.test.keywords(xfail="issue1 config's pytestplugins/bus initialization") def test_config_initafterpickle_plugin(testdir): testdir.makepyfile(__init__="", conftest="x=1; y=2") hello = testdir.makepyfile(hello="") @@ -113,10 +113,10 @@ config2._initafterpickle(topdir=tmp.dirpath()) # we check that config "remote" config objects # have correct plugin initialization - #XXX assert config2.pluginmanager.pm._plugins - #XXX assert config2.bus.issubscribed(config2.pluginmanager.forward_event) + #XXX assert config2.pytestplugins.pm._plugins + #XXX assert config2.bus.issubscribed(config2.pytestplugins.forward_event) assert config2.bus == py._com.pyplugins - assert config2.pluginmanager.pm == py._com.pyplugins + assert config2.pytestplugins.pm == py._com.pyplugins def test_config_initafterpickle_some(): tmp = py.test.ensuretemp("test_config_initafterpickle_some") Deleted: /py/branch/pytestplugin/py/test/testing/test_handleplugin.py ============================================================================== --- /py/branch/pytestplugin/py/test/testing/test_handleplugin.py Sat Feb 21 14:16:19 2009 +++ (empty file) @@ -1,322 +0,0 @@ -import py -from py.__.test.handleplugin import PytestPluginManager, canonical_names -from py.__.test.handleplugin import registerplugin, importplugin -from py.__.test.config import Config as pytestConfig - -def test_considermodule(): - pp = PytestPluginManager() - mod = py.std.new.module("hello") - mod.pytest_plugins = "helloworldmod_42" - py.test.raises(ImportError, "pp.consider_module(mod)") - -def test_setinitial(): - pp = PytestPluginManager() - mod = py.std.new.module("hello") - mod.pytest_plugins = "helloworldmod_42" - py.test.raises(ImportError, "pp.setinitial([mod])") - -def test_configure(): - config = pytestConfig() - l = [] - events = [] - class A: - def pytest_configure(self, config): - l.append(self) - def pyevent_hello(self, obj): - events.append(obj) - - config.bus.register(A()) - assert len(l) == 0 - config.pluginmanager.configure(config=config) - assert len(l) == 1 - config.bus.register(A()) # this should lead to a configured() plugin - assert len(l) == 2 - assert l[0] != l[1] - - config.bus.notify("hello", 42) - assert len(events) == 2 - assert events == [42,42] - - config.pluginmanager.unconfigure(config=config) - config.bus.register(A()) - assert len(l) == 2 - -def test_addcmdlineoptions(): - class PseudoConfig: - opts = [] - def addoptions(self, *opts): - self.opts.append(opts) - config = PseudoConfig() - pc = PytestPluginManager() - class A: - pytest_cmdlineoptions = [42] - pc.pm.register(A) - pc.add_cmdlineoptions(config) - assert len(config.opts) == 1 - assert config.opts[0][1] == 42 - -def test_canonical_names(): - for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz': - impname, clsname = canonical_names(name) - assert impname == "pytest_xyz" - assert clsname == "Xyz" - -def test_registerplugin(): - l = [] - registerfunc = l.append - registerplugin(registerfunc, py.io, "TerminalWriter") - assert len(l) == 1 - assert isinstance(l[0], py.io.TerminalWriter) - -def test_importplugin(): - assert importplugin("py") == py - py.test.raises(ImportError, "importplugin('laksjd.qwe')") - mod = importplugin("pytest_terminal") - assert mod is py.__.test.plugin.pytest_terminal - -class TestPluginManager: - def test_importplugin(self, testdir): - mod = py.std.new.module("x") - mod.pytest_plugins = "pytest_a" - aplugin = testdir.makepyfile(pytest_a="""class A: pass""") - pm = PytestPluginManager() - l = [] - pm.pm.subscribe(lambda *args: l.append(args)) - #syspath.prepend(aplugin.dirpath()) - py.std.sys.path.insert(0, str(aplugin.dirpath())) - pm.consider_module(mod) - assert pm.getplugin('a').__class__.__name__ == "A" - assert len(l) == 1 - pm.consider_module(mod) - assert len(l) == 1 - -import os, sys -import py -from py.__.test.event import NOP -from py.__.test.config import Config as pytestConfig - -class TestInitialization: - disabled = True - def setup_method(self, method): - self.tmpdir = py.test.ensuretemp("%s.%s.%s" % - (__name__, self.__class__.__name__, method.__name__)) - - def test_import_plugin_importname(self): - pm = PytestPluginManager() - py.test.raises(ImportError, 'pm.import_plugin("x.y")') - py.test.raises(ImportError, 'pm.import_plugin("pytest_x.y")') - - sys.path.insert(0, str(self.tmpdir)) - try: - pluginname = "pytest_hello" - self.tmpdir.join(pluginname + ".py").write(py.code.Source(""" - class Hello: - pass - """)) - pm.import_plugin("hello") - pm.import_plugin("pytest_hello") - plugin = pm.getplugin("hello") - finally: - sys.path.remove(str(self.tmpdir)) - - def test_import_plugin_defaults_to_pytestplugin(self): - from py.__.test.defaultconftest import pytest_plugins - for name in pytest_plugins: - if isinstance(name, str): - break - sys.path.insert(0, str(self.tmpdir)) - try: - self.tmpdir.join(name + ".py").write(py.code.Source(""" - class Terminal: - pass - class AnotherPlugin: - pass - pytest_plugins = AnotherPlugin - """)) - pm = PytestPluginManager() - pm.import_plugin(name) - plugin = pm.getplugin("terminal") - #del sys.modules[name] - print pm._plugins - plugin = pm.getplugin("anotherplugin") - finally: - sys.path.remove(str(self.tmpdir)) - - def test_import_plugin_class(self): - pm = PytestPluginManager() - class SomePlugin: - pass - pm.import_plugin(SomePlugin) - plugin = pm.getplugin("someplugin") - assert isinstance(plugin, SomePlugin) - i = len(pm._plugins) - pm.import_plugin(SomePlugin) - assert len(pm._plugins) == i - - def test_addpluginclass_post_configure(self): - pm = PytestPluginManager() - l = [] - class SomePlugin: - def pytest_configure(self, config): - l.append(config) - conf = pytestConfig() - pm.configure(config=conf) - pm.import_plugin(SomePlugin) - assert len(l) == 1 - assert l[0] is conf - - def test_consider_module(self): - pm = PytestPluginManager() - sys.path.insert(0, str(self.tmpdir)) - try: - self.tmpdir.join("pytest_plug1.py").write("class Plug1: pass") - self.tmpdir.join("pytest_plug2.py").write("class Plug2: pass") - mod = py.std.new.module("temp") - mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] - pm.consider_module(mod) - assert pm.getplugin("plug1").__class__.__name__ == "Plug1" - assert pm.getplugin("plug2").__class__.__name__ == "Plug2" - finally: - sys.path.remove(str(self.tmpdir)) - - - def test_addpluginclass(self): - pm = PytestPluginManager() - class My: - pass - pm.addpluginclass(My) - assert len(pm._plugins) == 1 - pm.addpluginclass(My) - assert len(pm._plugins) == 1 - - def test_getplugin(self): - pm = PytestPluginManager() - assert py.test.raises(LookupError, "pm.getplugin('_xxx')") - - class PluGin: pass - pm.addpluginclass(PluGin) - myplugin1 = pm.getplugin("plugin") - myplugin2 = pm.getplugin("Plugin") - assert myplugin1 is myplugin2 - -class TestPluginInteraction: - disabled = True - def test_getfirst(self): - pm = PytestPluginManager() - class My1: - x = 1 - assert pm.getfirst("x") is None - pm.addpluginclass(My1) - assert pm.getfirst("x") == 1 - - def test_call_plugins(self): - pm = PytestPluginManager() - class My: - def method(self, arg): - pass - pm.addpluginclass(My) - py.test.raises(TypeError, 'pm.call_plugins("method")') - py.test.raises(TypeError, 'pm.call_plugins("method", 42)') - pm.call_plugins("method", arg=42) - py.test.raises(TypeError, 'pm.call_plugins("method", arg=42, s=13)') - - def test_call_firstresult(self): - pm = PytestPluginManager() - class My1: - def method(self): - pass - class My2: - def method(self): - return True - class My3: - def method(self): - return None - assert pm.call_firstresult("method") is None - assert pm.call_firstresult("methodnotexists") is None - pm.addpluginclass(My1) - assert pm.call_firstresult("method") is None - pm.addpluginclass(My2) - assert pm.call_firstresult("method") == True - pm.addpluginclass(My3) - assert pm.call_firstresult("method") == True - - def test_listattr(self): - pm = PytestPluginManager() - class My2: - x = 42 - pm.addpluginclass(My2) - assert not pm.listattr("hello") - assert pm.listattr("x") == [('my2', 42)] - - def test_eventbus_interaction(self): - pm = PytestPluginManager() - l = [] - class My3: - def pytest_configure(self, config): - l.append("configure") - def pytest_unconfigure(self, config): - l.append("unconfigure") - pm.addpluginclass(My3) - config = pytestConfig() - pm.configure(config) - assert config.bus.issubscribed(pm.forward_event) - pm.unconfigure(config) - assert not config.bus.issubscribed(pm.forward_event) - assert l == ['configure', 'unconfigure'] - - def test_pytest_event(self): - pm = PytestPluginManager() - l = [] - class My2: - def pytest_event(self, event): - l.append(event) - pm.addpluginclass(My2) - ev = NOP() - pm.forward_event(ev) - assert len(l) == 1 - assert l[0] is ev - - def test_addcmdlineoptions(self): - pm = PytestPluginManager() - class My: - pytest_cmdlineoptions = [ - py.test.config.Option("--hello", dest="dest", default=242) - ] - pm.addpluginclass(My) - config = pytestConfig() - pm.add_cmdlineoptions(config) - opt = config._parser.get_option("--hello") - assert opt - assert opt.default == 242 - - def test_setinitial_env(self): - pm = PytestPluginManager() - KEY = "PYTEST_PLUGINS" - old = os.environ.get(KEY, None) - try: - os.environ[KEY] = "test_setinitial" - py.test.raises(ImportError, "pm.setinitial([])") - finally: - if old is None: - del os.environ[KEY] - else: - os.environ[KEY] = old - - def test_conftest_specifies_plugin(self, testdir): - testdir.makepyfile( - conftest=""" - import py - class MyPlugin: - pytest_cmdlineoptions = [ - py.test.config.Option("--myplugin-option", dest="myplugin", - help="myplugin option", - ) - ] - pytest_plugins = MyPlugin - """ - ) - result = testdir.runpytest(testdir.tmpdir, '-h') - result.stdout.fnmatch_lines([ - "*--myplugin-option*", - ]) - Copied: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (from r62059, py/branch/pytestplugin/py/test/testing/test_handleplugin.py) ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_handleplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Sat Feb 21 14:16:19 2009 @@ -1,16 +1,16 @@ import py -from py.__.test.handleplugin import PytestPluginManager, canonical_names -from py.__.test.handleplugin import registerplugin, importplugin +from py.__.test.pytestplugin import PytestPlugins, canonical_names +from py.__.test.pytestplugin import registerplugin, importplugin from py.__.test.config import Config as pytestConfig def test_considermodule(): - pp = PytestPluginManager() + pp = PytestPlugins() mod = py.std.new.module("hello") mod.pytest_plugins = "helloworldmod_42" py.test.raises(ImportError, "pp.consider_module(mod)") def test_setinitial(): - pp = PytestPluginManager() + pp = PytestPlugins() mod = py.std.new.module("hello") mod.pytest_plugins = "helloworldmod_42" py.test.raises(ImportError, "pp.setinitial([mod])") @@ -27,7 +27,7 @@ config.bus.register(A()) assert len(l) == 0 - config.pluginmanager.configure(config=config) + config.pytestplugins.configure(config=config) assert len(l) == 1 config.bus.register(A()) # this should lead to a configured() plugin assert len(l) == 2 @@ -37,7 +37,7 @@ assert len(events) == 2 assert events == [42,42] - config.pluginmanager.unconfigure(config=config) + config.pytestplugins.unconfigure(config=config) config.bus.register(A()) assert len(l) == 2 @@ -47,10 +47,10 @@ def addoptions(self, *opts): self.opts.append(opts) config = PseudoConfig() - pc = PytestPluginManager() + pc = PytestPlugins() class A: pytest_cmdlineoptions = [42] - pc.pm.register(A) + pc.pyplugins.register(A) pc.add_cmdlineoptions(config) assert len(config.opts) == 1 assert config.opts[0][1] == 42 @@ -79,15 +79,15 @@ mod = py.std.new.module("x") mod.pytest_plugins = "pytest_a" aplugin = testdir.makepyfile(pytest_a="""class A: pass""") - pm = PytestPluginManager() + plugins = PytestPlugins() l = [] - pm.pm.subscribe(lambda *args: l.append(args)) + plugins.pyplugins.subscribe(lambda *args: l.append(args)) #syspath.prepend(aplugin.dirpath()) py.std.sys.path.insert(0, str(aplugin.dirpath())) - pm.consider_module(mod) - assert pm.getplugin('a').__class__.__name__ == "A" + plugins.consider_module(mod) + assert plugins.getplugin('a').__class__.__name__ == "A" assert len(l) == 1 - pm.consider_module(mod) + plugins.consider_module(mod) assert len(l) == 1 import os, sys @@ -102,9 +102,9 @@ (__name__, self.__class__.__name__, method.__name__)) def test_import_plugin_importname(self): - pm = PytestPluginManager() - py.test.raises(ImportError, 'pm.import_plugin("x.y")') - py.test.raises(ImportError, 'pm.import_plugin("pytest_x.y")') + plugins = PytestPlugins() + py.test.raises(ImportError, 'plugins.import_plugin("x.y")') + py.test.raises(ImportError, 'plugins.import_plugin("pytest_x.y")') sys.path.insert(0, str(self.tmpdir)) try: @@ -113,9 +113,9 @@ class Hello: pass """)) - pm.import_plugin("hello") - pm.import_plugin("pytest_hello") - plugin = pm.getplugin("hello") + plugins.import_plugin("hello") + plugins.import_plugin("pytest_hello") + plugin = plugins.getplugin("hello") finally: sys.path.remove(str(self.tmpdir)) @@ -133,95 +133,95 @@ pass pytest_plugins = AnotherPlugin """)) - pm = PytestPluginManager() - pm.import_plugin(name) - plugin = pm.getplugin("terminal") + plugins = PytestPlugins() + plugins.import_plugin(name) + plugin = plugins.getplugin("terminal") #del sys.modules[name] - print pm._plugins - plugin = pm.getplugin("anotherplugin") + print plugins._plugins + plugin = plugins.getplugin("anotherplugin") finally: sys.path.remove(str(self.tmpdir)) def test_import_plugin_class(self): - pm = PytestPluginManager() + plugins = PytestPlugins() class SomePlugin: pass - pm.import_plugin(SomePlugin) - plugin = pm.getplugin("someplugin") + plugins.import_plugin(SomePlugin) + plugin = plugins.getplugin("someplugin") assert isinstance(plugin, SomePlugin) - i = len(pm._plugins) - pm.import_plugin(SomePlugin) - assert len(pm._plugins) == i + i = len(plugins._plugins) + plugins.import_plugin(SomePlugin) + assert len(plugins._plugins) == i def test_addpluginclass_post_configure(self): - pm = PytestPluginManager() + plugins = PytestPlugins() l = [] class SomePlugin: def pytest_configure(self, config): l.append(config) conf = pytestConfig() - pm.configure(config=conf) - pm.import_plugin(SomePlugin) + plugins.configure(config=conf) + plugins.import_plugin(SomePlugin) assert len(l) == 1 assert l[0] is conf def test_consider_module(self): - pm = PytestPluginManager() + plugins = PytestPlugins() sys.path.insert(0, str(self.tmpdir)) try: self.tmpdir.join("pytest_plug1.py").write("class Plug1: pass") self.tmpdir.join("pytest_plug2.py").write("class Plug2: pass") mod = py.std.new.module("temp") mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] - pm.consider_module(mod) - assert pm.getplugin("plug1").__class__.__name__ == "Plug1" - assert pm.getplugin("plug2").__class__.__name__ == "Plug2" + plugins.consider_module(mod) + assert plugins.getplugin("plug1").__class__.__name__ == "Plug1" + assert plugins.getplugin("plug2").__class__.__name__ == "Plug2" finally: sys.path.remove(str(self.tmpdir)) def test_addpluginclass(self): - pm = PytestPluginManager() + plugins = PytestPlugins() class My: pass - pm.addpluginclass(My) - assert len(pm._plugins) == 1 - pm.addpluginclass(My) - assert len(pm._plugins) == 1 + plugins.addpluginclass(My) + assert len(plugins._plugins) == 1 + plugins.addpluginclass(My) + assert len(plugins._plugins) == 1 def test_getplugin(self): - pm = PytestPluginManager() - assert py.test.raises(LookupError, "pm.getplugin('_xxx')") + plugins = PytestPlugins() + assert py.test.raises(LookupError, "plugins.getplugin('_xxx')") class PluGin: pass - pm.addpluginclass(PluGin) - myplugin1 = pm.getplugin("plugin") - myplugin2 = pm.getplugin("Plugin") + plugins.addpluginclass(PluGin) + myplugin1 = plugins.getplugin("plugin") + myplugin2 = plugins.getplugin("Plugin") assert myplugin1 is myplugin2 class TestPluginInteraction: disabled = True def test_getfirst(self): - pm = PytestPluginManager() + plugins = PytestPlugins() class My1: x = 1 - assert pm.getfirst("x") is None - pm.addpluginclass(My1) - assert pm.getfirst("x") == 1 + assert plugins.getfirst("x") is None + plugins.addpluginclass(My1) + assert plugins.getfirst("x") == 1 def test_call_plugins(self): - pm = PytestPluginManager() + plugins = PytestPlugins() class My: def method(self, arg): pass - pm.addpluginclass(My) - py.test.raises(TypeError, 'pm.call_plugins("method")') - py.test.raises(TypeError, 'pm.call_plugins("method", 42)') - pm.call_plugins("method", arg=42) - py.test.raises(TypeError, 'pm.call_plugins("method", arg=42, s=13)') + plugins.addpluginclass(My) + py.test.raises(TypeError, 'plugins.call_plugins("method")') + py.test.raises(TypeError, 'plugins.call_plugins("method", 42)') + plugins.call_plugins("method", arg=42) + py.test.raises(TypeError, 'plugins.call_plugins("method", arg=42, s=13)') def test_call_firstresult(self): - pm = PytestPluginManager() + plugins = PytestPlugins() class My1: def method(self): pass @@ -231,71 +231,71 @@ class My3: def method(self): return None - assert pm.call_firstresult("method") is None - assert pm.call_firstresult("methodnotexists") is None - pm.addpluginclass(My1) - assert pm.call_firstresult("method") is None - pm.addpluginclass(My2) - assert pm.call_firstresult("method") == True - pm.addpluginclass(My3) - assert pm.call_firstresult("method") == True + assert plugins.call_firstresult("method") is None + assert plugins.call_firstresult("methodnotexists") is None + plugins.addpluginclass(My1) + assert plugins.call_firstresult("method") is None + plugins.addpluginclass(My2) + assert plugins.call_firstresult("method") == True + plugins.addpluginclass(My3) + assert plugins.call_firstresult("method") == True def test_listattr(self): - pm = PytestPluginManager() + plugins = PytestPlugins() class My2: x = 42 - pm.addpluginclass(My2) - assert not pm.listattr("hello") - assert pm.listattr("x") == [('my2', 42)] + plugins.addpluginclass(My2) + assert not plugins.listattr("hello") + assert plugins.listattr("x") == [('my2', 42)] def test_eventbus_interaction(self): - pm = PytestPluginManager() + plugins = PytestPlugins() l = [] class My3: def pytest_configure(self, config): l.append("configure") def pytest_unconfigure(self, config): l.append("unconfigure") - pm.addpluginclass(My3) + plugins.addpluginclass(My3) config = pytestConfig() - pm.configure(config) - assert config.bus.issubscribed(pm.forward_event) - pm.unconfigure(config) - assert not config.bus.issubscribed(pm.forward_event) + plugins.configure(config) + assert config.bus.issubscribed(plugins.forward_event) + plugins.unconfigure(config) + assert not config.bus.issubscribed(plugins.forward_event) assert l == ['configure', 'unconfigure'] def test_pytest_event(self): - pm = PytestPluginManager() + plugins = PytestPlugins() l = [] class My2: def pytest_event(self, event): l.append(event) - pm.addpluginclass(My2) + plugins.addpluginclass(My2) ev = NOP() - pm.forward_event(ev) + plugins.forward_event(ev) assert len(l) == 1 assert l[0] is ev def test_addcmdlineoptions(self): - pm = PytestPluginManager() + plugins = PytestPlugins() class My: pytest_cmdlineoptions = [ py.test.config.Option("--hello", dest="dest", default=242) ] - pm.addpluginclass(My) + plugins.addpluginclass(My) config = pytestConfig() - pm.add_cmdlineoptions(config) + plugins.add_cmdlineoptions(config) opt = config._parser.get_option("--hello") assert opt assert opt.default == 242 def test_setinitial_env(self): - pm = PytestPluginManager() + plugins = PytestPlugins() KEY = "PYTEST_PLUGINS" old = os.environ.get(KEY, None) try: os.environ[KEY] = "test_setinitial" - py.test.raises(ImportError, "pm.setinitial([])") + py.test.raises(ImportError, "plugins.setinitial([])") finally: if old is None: del os.environ[KEY] From hpk at codespeak.net Sat Feb 21 15:25:26 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 21 Feb 2009 15:25:26 +0100 (CET) Subject: [py-svn] r62076 - in py/branch/pytestplugin/py: . misc/testing test test/plugin test/testing Message-ID: <20090221142526.EF33416850A@codespeak.net> Author: hpk Date: Sat Feb 21 15:25:23 2009 New Revision: 62076 Modified: py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/misc/testing/test_com.py py/branch/pytestplugin/py/misc/testing/test_warn.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: get rid of generic subscribe/unsubscribe in favour of using plugin register/unregister everywhere. Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Sat Feb 21 15:25:23 2009 @@ -6,7 +6,6 @@ Simple example: MultiCall([list1.append, list2.append], 42).execute() """ - NONEASRESULT = object() def __init__(self, methods, *args, **kwargs): @@ -42,9 +41,8 @@ """ Manage Plugins: Load plugins and manage calls to plugins. """ - def __init__(self): - self.plugins = [] + self._plugins = [] self._callbacks = [] def import_module(self, modspec): @@ -71,14 +69,20 @@ def register(self, plugin): assert not isinstance(plugin, str) - self.plugins.append(plugin) - #print "registering", self, plugin - self.notify("pluginregistered", plugin) + self._plugins.append(plugin) + self.notify("plugin_registered", plugin) + + def unregister(self, plugin): + self._plugins.remove(plugin) + self.notify("plugin_unregistered", plugin) + + def isregistered(self, plugin): + return plugin in self._plugins def listattr(self, attrname, plugins=None, extra=()): l = [] if plugins is None: - plugins = self.plugins + plugins = self._plugins if extra: plugins += list(extra) for plugin in plugins: @@ -105,16 +109,7 @@ MultiCall(self.listattr("pyevent_" + eventname), *args, **kwargs).execute() #print "calling anonymous hooks", args, kwargs - MultiCall(self._callbacks + self.listattr("pyevent"), + MultiCall(self.listattr("pyevent"), eventname, *args, **kwargs).execute() - def subscribe(self, callback): - self._callbacks.append(callback) - - def issubscribed(self, callback): - return callback in self._callbacks - - def unsubscribe(self, callback): - self._callbacks.remove(callback) - pyplugins = PyPlugins() Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Sat Feb 21 15:25:23 2009 @@ -40,88 +40,94 @@ class TestPyPlugins: def test_register(self): - pm = PyPlugins() + plugins = PyPlugins() class MyPlugin: pass my = MyPlugin() - pm.register(my) - assert my in pm.plugins + plugins.register(my) + assert plugins.isregistered(my) + plugins.unregister(my) + assert not plugins.isregistered(my) #@py.test.keywords(xfail=True) def test_onregister(self): py.test.skip("implement exitfirst plugin and " "modify xfail plugin to override exitfirst behaviour?") - pm = PyPlugins() + plugins = PyPlugins() l = [] class MyApi: - def pyevent_pluginregistered(self, plugin): + def pyevent_plugin_registered(self, plugin): l.append(plugin) + def pyevent_plugin_unregistered(self, plugin): + l.remove(plugin) myapi = MyApi() - pm.register(myapi) + plugins.register(myapi) assert len(l) == 1 assert l[0] is myapi + plugins.unregister(myapi) + assert not l def test_call_methods(self): - pm = PyPlugins() + plugins = PyPlugins() class api1: def m(self, __call__, x): return x class api2: def m(self, __call__, x, y=33): return y - pm.register(api1()) - pm.register(api2()) - res = pm.call_firstresult("m", x=5) - assert pm.call_firstresult("notexist") is None + plugins.register(api1()) + plugins.register(api2()) + res = plugins.call_firstresult("m", x=5) + assert plugins.call_firstresult("notexist") is None assert res == 33 - reslist = pm.call_each("m", x=5) + reslist = plugins.call_each("m", x=5) assert len(reslist) == 2 assert 5 in reslist assert 33 in reslist - assert pm.call_each("notexist") == [] + assert plugins.call_each("notexist") == [] - assert pm.call_plugin(api1(), 'm', x=12) == 12 - assert pm.call_plugin(api2(), 't') is None + assert plugins.call_plugin(api1(), 'm', x=12) == 12 + assert plugins.call_plugin(api2(), 't') is None def test_call_none_is_no_result(self): - pm = PyPlugins() + plugins = PyPlugins() class api1: def m(self): return None class api2: def m(self, __call__): return 41 - pm.register(api1()) - pm.register(api1()) - pm.register(api2()) - assert pm.call_firstresult('m') == 41 - assert pm.call_each('m') == [41] + plugins.register(api1()) + plugins.register(api1()) + plugins.register(api2()) + assert plugins.call_firstresult('m') == 41 + assert plugins.call_each('m') == [41] def test_call_noneasresult(self): - pm = PyPlugins() + plugins = PyPlugins() class api1: def m(self, __call__): return __call__.NONEASRESULT - pm.register(api1()) - pm.register(api1()) - assert pm.call_firstresult('m') is None - assert pm.call_each('m') == [None, None] + plugins.register(api1()) + plugins.register(api1()) + assert plugins.call_firstresult('m') is None + assert plugins.call_each('m') == [None, None] def test_listattr(self): - pm = PyPlugins() + plugins = PyPlugins() class api1: x = 42 class api2: x = 41 - pm.register(api1()) - pm.register(api2()) - l = list(pm.listattr('x')) + plugins.register(api1()) + plugins.register(api2()) + l = list(plugins.listattr('x')) l.sort() assert l == [41, 42] def test_notify_anonymous_ordered(self): - pm = PyPlugins() + plugins = PyPlugins() l = [] class api1: def pyevent_hello(self): @@ -130,20 +136,19 @@ def pyevent(self, name, *args): if name == "hello": l.append(name + "anonymous") - pm.register(api1()) - pm.register(api2()) - pm.subscribe(l.append) - pm.notify('hello') - assert l == ["hellospecific", "helloanonymous", "hello"] + plugins.register(api1()) + plugins.register(api2()) + plugins.notify('hello') + assert l == ["hellospecific", "helloanonymous"] def test_consider_env(self): # XXX write a helper for preserving os.environ - pm = PyPlugins() + plugins = PyPlugins() KEY = "XXPYLIB" old = os.environ.get(KEY, None) try: os.environ[KEY] = "unknownconsider_env" - py.test.raises(ImportError, "pm.consider_env(KEY)") + py.test.raises(ImportError, "plugins.consider_env(KEY)") finally: if old is None: del os.environ[KEY] @@ -151,17 +156,17 @@ os.environ[KEY] = old def test_consider_module(self): - pm = PyPlugins() + plugins = PyPlugins() mod = py.std.new.module("temp") mod.pylib = ["xxx nomod"] - excinfo = py.test.raises(ImportError, "pm.consider_module(mod)") + excinfo = py.test.raises(ImportError, "plugins.consider_module(mod)") mod.pylib = "os" class Events(list): def pyevent_importingmodule(self, mod): self.append(mod) l = Events() - pm.register(l) - pm.consider_module(mod) + plugins.register(l) + plugins.consider_module(mod) assert len(l) == 1 assert l[0] == (mod.pylib) @@ -170,7 +175,7 @@ def test_subprocess_env(): # XXX write a helper for preserving os.environ - pm = PyPlugins() + plugins = PyPlugins() KEY = "PYLIB" old = os.environ.get(KEY, None) olddir = py.path.local(py.__file__).dirpath().dirpath().chdir() @@ -190,33 +195,24 @@ class TestPyPluginsEvents: def test_pyevent_named_dispatch(self): - pm = PyPlugins() + plugins = PyPlugins() l = [] class A: def pyevent_name(self, x): l.append(x) - pm.register(A()) - pm.notify("name", 13) + plugins.register(A()) + plugins.notify("name", 13) assert l == [13] def test_pyevent_anonymous_dispatch(self): - pm = PyPlugins() + plugins = PyPlugins() l = [] class A: def pyevent(self, name, *args, **kwargs): if name == "name": l.extend([args, kwargs]) - pm.register(A()) - pm.notify("name", 13, x=15) + plugins.register(A()) + plugins.notify("name", 13, x=15) assert l == [(13, ), {'x':15}] - def test_pyevent_subscription(self): - pm = PyPlugins() - l = [] - pm.subscribe(l.append) - assert pm.issubscribed(l.append) - pm.notify("hello") - assert l == ['hello'] - pm.unsubscribe(l.append) - assert not pm.issubscribed(l.append) Modified: py/branch/pytestplugin/py/misc/testing/test_warn.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_warn.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_warn.py Sat Feb 21 15:25:23 2009 @@ -46,4 +46,4 @@ def test_default(): from py.__.misc.warn import APIWARN - assert APIWARN.im_self in py._com.pyplugins.plugins + assert py._com.pyplugins.isregistered(APIWARN.im_self) Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Sat Feb 21 15:25:23 2009 @@ -181,16 +181,20 @@ class EventRecorder(object): - def __init__(self, bus, debug=False): # True): + def __init__(self, pyplugins, debug=False): # True): self.events = [] - self.bus = bus - bus.subscribe(self.record) + self.pyplugins = pyplugins self.debug = debug + pyplugins.register(self) - def record(self, name, event): + def pyevent(self, name, *args, **kwargs): + if name == "plugin_registered" and args == (self,): + return if self.debug: - print "[event] %s: %s" %(name, event) - self.events.append((name, event)) + print "[event] %s: %s **%s" %(name, ", ".join(map(str, args)), kwargs,) + if len(args) == 1: + event, = args + self.events.append((name, event)) def get(self, cls): l = [] @@ -261,8 +265,8 @@ def clear(self): self.events[:] = [] - def unsubscribe(self): - self.bus.unsubscribe(self.record) + def unregister(self): + self.pyplugins.unregister(self) def test_eventrecorder(): bus = py._com.PyPlugins() @@ -302,7 +306,7 @@ recorder.clear() assert not recorder.events assert not recorder.getfailures() - recorder.unsubscribe() + recorder.unregister() bus.notify("itemtestreport", rep) assert not recorder.events assert not recorder.getfailures() Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Sat Feb 21 15:25:23 2009 @@ -94,7 +94,7 @@ if self.config.option.verbose: self.write_line("HostGatewayReady: %s" %(ev.host,)) - def pyevent_pluginregistered(self, plugin): + def pyevent_plugin_registered(self, plugin): self.write_line("PLUGIN registered: %s" %(plugin,)) def pyevent_hostup(self, ev): Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Sat Feb 21 15:25:23 2009 @@ -12,6 +12,13 @@ self.pyplugins = pyplugins self._plugins = {} + def register(self, plugin): + self.pyplugins.register(plugin) + def unregister(self, plugin): + self.pyplugins.unregister(plugin) + def isregistered(self, plugin): + return self.pyplugins.isregistered(plugin) + # API for bootstrapping # def getplugin(self, pname): @@ -61,12 +68,6 @@ def notify(self, eventname, *args, **kwargs): return self.pyplugins.notify(eventname, *args, **kwargs) - def subscribe(self, callback): - bus = self.pyplugins.subscribe(callback) - - def unsubscribe(self, callback): - bus = self.pyplugins.unsubscribe(callback) - def add_cmdlineoptions(self, config): # XXX think about sorting/grouping of options from user-perspective #assert self.pyplugins.list @@ -75,7 +76,7 @@ opts.extend(options) config.addoptions("ungrouped options added by plugins", *opts) - def pyevent_pluginregistered(self, plugin): + def pyevent_plugin_registered(self, plugin): if hasattr(self, '_config'): self.pyplugins.call_plugin(plugin, "pytest_configure", config=self._config) Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Sat Feb 21 15:25:23 2009 @@ -114,7 +114,7 @@ # we check that config "remote" config objects # have correct plugin initialization #XXX assert config2.pytestplugins.pm._plugins - #XXX assert config2.bus.issubscribed(config2.pytestplugins.forward_event) + #XXX assert config2.bus.isregistered(config2.pytestplugins.forward_event) assert config2.bus == py._com.pyplugins assert config2.pytestplugins.pm == py._com.pyplugins Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Sat Feb 21 15:25:23 2009 @@ -14,46 +14,76 @@ mod = py.std.new.module("hello") mod.pytest_plugins = "helloworldmod_42" py.test.raises(ImportError, "pp.setinitial([mod])") - -def test_configure(): - config = pytestConfig() - l = [] - events = [] - class A: - def pytest_configure(self, config): - l.append(self) - def pyevent_hello(self, obj): - events.append(obj) - - config.bus.register(A()) - assert len(l) == 0 - config.pytestplugins.configure(config=config) - assert len(l) == 1 - config.bus.register(A()) # this should lead to a configured() plugin - assert len(l) == 2 - assert l[0] != l[1] - config.bus.notify("hello", 42) - assert len(events) == 2 - assert events == [42,42] - - config.pytestplugins.unconfigure(config=config) - config.bus.register(A()) - assert len(l) == 2 - -def test_addcmdlineoptions(): - class PseudoConfig: - opts = [] - def addoptions(self, *opts): - self.opts.append(opts) - config = PseudoConfig() - pc = PytestPlugins() - class A: - pytest_cmdlineoptions = [42] - pc.pyplugins.register(A) - pc.add_cmdlineoptions(config) - assert len(config.opts) == 1 - assert config.opts[0][1] == 42 +class TestPytestPlugins: + def test_configure(self): + config = pytestConfig() + l = [] + events = [] + class A: + def pytest_configure(self, config): + l.append(self) + def pyevent_hello(self, obj): + events.append(obj) + + config.bus.register(A()) + assert len(l) == 0 + config.pytestplugins.configure(config=config) + assert len(l) == 1 + config.bus.register(A()) # this should lead to a configured() plugin + assert len(l) == 2 + assert l[0] != l[1] + + config.bus.notify("hello", 42) + assert len(events) == 2 + assert events == [42,42] + + config.pytestplugins.unconfigure(config=config) + config.bus.register(A()) + assert len(l) == 2 + + def test_addcmdlineoptions(self): + class PseudoConfig: + opts = [] + def addoptions(self, *opts): + self.opts.append(opts) + config = PseudoConfig() + pc = PytestPlugins() + class A: + pytest_cmdlineoptions = [42] + pc.pyplugins.register(A) + pc.add_cmdlineoptions(config) + assert len(config.opts) == 1 + assert config.opts[0][1] == 42 + + def test_registry(self): + pp = PytestPlugins() + a1, a2 = object(), object() + pp.register(a1) + assert pp.isregistered(a1) + pp.register(a2) + assert pp.isregistered(a2) + pp.unregister(a1) + assert not pp.isregistered(a1) + pp.unregister(a2) + assert not pp.isregistered(a2) + + def test_consider_module(self, testdir, EventRecorder): + mod = py.std.new.module("x") + mod.pytest_plugins = "pytest_a" + aplugin = testdir.makepyfile(pytest_a="""class A: pass""") + plugins = PytestPlugins() + evrec = EventRecorder(plugins) + #syspath.prepend(aplugin.dirpath()) + py.std.sys.path.insert(0, str(aplugin.dirpath())) + plugins.consider_module(mod) + evlist = evrec.getnamed("plugin_registered") + assert len(evlist) == 1 + assert evlist[0].__class__.__name__ == "A" + plugins.consider_module(mod) + evlist = evrec.getnamed("plugin_registered") + assert len(evlist) == 1 + def test_canonical_names(): for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz': @@ -74,22 +104,6 @@ mod = importplugin("pytest_terminal") assert mod is py.__.test.plugin.pytest_terminal -class TestPluginManager: - def test_importplugin(self, testdir): - mod = py.std.new.module("x") - mod.pytest_plugins = "pytest_a" - aplugin = testdir.makepyfile(pytest_a="""class A: pass""") - plugins = PytestPlugins() - l = [] - plugins.pyplugins.subscribe(lambda *args: l.append(args)) - #syspath.prepend(aplugin.dirpath()) - py.std.sys.path.insert(0, str(aplugin.dirpath())) - plugins.consider_module(mod) - assert plugins.getplugin('a').__class__.__name__ == "A" - assert len(l) == 1 - plugins.consider_module(mod) - assert len(l) == 1 - import os, sys import py from py.__.test.event import NOP From hpk at codespeak.net Sat Feb 21 18:05:45 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 21 Feb 2009 18:05:45 +0100 (CET) Subject: [py-svn] r62077 - py/branch/pytestplugin/py/test/testing Message-ID: <20090221170545.AEF051684C7@codespeak.net> Author: hpk Date: Sat Feb 21 18:05:43 2009 New Revision: 62077 Added: py/branch/pytestplugin/py/test/testing/test_collect_pickle.py (contents, props changed) - copied, changed from r62008, py/branch/pytestplugin/py/test/testing/test_collect.py py/branch/pytestplugin/py/test/testing/test_pycollect.py (contents, props changed) - copied, changed from r62008, py/branch/pytestplugin/py/test/testing/test_collect.py Modified: py/branch/pytestplugin/py/test/testing/test_collect.py Log: regroup collection tests using the newly obtained flexibility from the dynamic funcarg setup. Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Sat Feb 21 18:05:43 2009 @@ -1,59 +1,69 @@ -from __future__ import generators import py -from py.__.test import event, outcome -from py.__.test.conftesthandle import Conftest -from py.__.test.collect import SetupState -from py.__.test.pycollect import DoctestFileContent +class TestCollector: + def test_collect_versus_item(self): + from py.__.test.collect import Collector, Item + assert not issubclass(Collector, Item) + assert not issubclass(Item, Collector) + + def test_check_equality_and_cmp_basic(self, testdir): + modcol = testdir.getmodulecol(""" + def test_pass(): pass + def test_fail(): assert 0 + """) + fn1 = modcol.collect_by_name("test_pass") + assert isinstance(fn1, py.test.collect.Function) + fn2 = modcol.collect_by_name("test_pass") + assert isinstance(fn2, py.test.collect.Function) + assert fn1 == fn2 + assert fn1 != modcol + assert cmp(fn1, fn2) == 0 + assert hash(fn1) == hash(fn2) + + fn3 = modcol.collect_by_name("test_fail") + assert isinstance(fn3, py.test.collect.Function) + assert not (fn1 == fn3) + assert fn1 != fn3 + assert cmp(fn1, fn3) == -1 + + assert cmp(fn1, 10) == -1 + assert cmp(fn2, 10) == -1 + assert cmp(fn3, 10) == -1 + for fn in fn1,fn2,fn3: + assert fn != 3 + assert fn != modcol + assert fn != [1,2,3] + assert [1,2,3] != fn + assert modcol != fn -class DummyConfig: - def __init__(self): - self._conftest = Conftest() - self._setupstate = SetupState() - self.args = [] - class dummyoption: - nomagic = False - self.option = dummyoption - def getvalue(self, name, fspath): - return self._conftest.rget(name, fspath) - -def setup_module(mod): - mod.dummyconfig = DummyConfig() - -def test_collect_versus_item(): - from py.__.test.collect import Collector, Item - assert not issubclass(Collector, Item) - assert not issubclass(Item, Collector) - -def test_ignored_certain_directories(tmpdir): - tmpdir.ensure("_darcs", 'test_notfound.py') - tmpdir.ensure("CVS", 'test_notfound.py') - tmpdir.ensure("{arch}", 'test_notfound.py') - tmpdir.ensure(".whatever", 'test_notfound.py') - tmpdir.ensure(".bzr", 'test_notfound.py') - tmpdir.ensure("normal", 'test_found.py') - tmpdir.ensure('test_found.py') - - col = py.test.collect.Directory(tmpdir, config=dummyconfig) - items = col.collect() - names = [x.name for x in items] - assert len(items) == 2 - assert 'normal' in names - assert 'test_found.py' in names - -class TestCollect: - def test_failing_import(self, testdir): - modcol = testdir.getmodulecol("import alksdjalskdjalkjals") - py.test.raises(ImportError, modcol.collect) - py.test.raises(ImportError, modcol.collect) - py.test.raises(ImportError, modcol.run) - - def test_syntax_error_in_module(self, testdir): - modcol = testdir.getmodulecol("this is a syntax error") - py.test.raises(SyntaxError, modcol.collect) - py.test.raises(SyntaxError, modcol.collect) - py.test.raises(SyntaxError, modcol.run) + def test_totrail_and_back(self, tmpdir): + a = tmpdir.ensure("a", dir=1) + tmpdir.ensure("a", "__init__.py") + x = tmpdir.ensure("a", "trail.py") + config = py.test.config._reparse([x]) + col = config.getfsnode(x) + trail = col._totrail() + assert len(trail) == 2 + assert trail[0] == a.relto(config.topdir) + assert trail[1] == ('trail.py',) + col2 = py.test.collect.Collector._fromtrail(trail, config) + assert col2.listnames() == col.listnames() + + def test_totrail_topdir_and_beyond(self, tmpdir): + config = py.test.config._reparse([tmpdir]) + col = config.getfsnode(config.topdir) + trail = col._totrail() + assert len(trail) == 2 + assert trail[0] == '.' + assert trail[1] == () + col2 = py.test.collect.Collector._fromtrail(trail, config) + assert col2.fspath == config.topdir + assert len(col2.listchain()) == 1 + col3 = config.getfsnode(config.topdir.dirpath()) + py.test.raises(ValueError, + "col3._totrail()") + def test_listnames_and__getitembynames(self, testdir): modcol = testdir.getmodulecol("pass", withinit=True) @@ -84,166 +94,32 @@ node = dircol._getitembynames(names) assert isinstance(node, py.test.collect.File) +class TestCollectFS: + def test_ignored_certain_directories(self, testdir): + tmpdir = testdir.tmpdir + tmpdir.ensure("_darcs", 'test_notfound.py') + tmpdir.ensure("CVS", 'test_notfound.py') + tmpdir.ensure("{arch}", 'test_notfound.py') + tmpdir.ensure(".whatever", 'test_notfound.py') + tmpdir.ensure(".bzr", 'test_notfound.py') + tmpdir.ensure("normal", 'test_found.py') + tmpdir.ensure('test_found.py') + + col = testdir.parseconfig(tmpdir).getfsnode(tmpdir) + items = col.collect() + names = [x.name for x in items] + assert len(items) == 2 + assert 'normal' in names + assert 'test_found.py' in names + def test_found_certain_testfiles(self, testdir): p1 = testdir.makepyfile(test_found = "pass", found_test="pass") - col = py.test.collect.Directory(p1.dirpath(), config=dummyconfig) + col = testdir.parseconfig(p1).getfsnode(p1.dirpath()) items = col.collect() # Directory collect returns files sorted by name assert len(items) == 2 assert items[1].name == 'test_found.py' assert items[0].name == 'found_test.py' - def test_disabled_class(self, testdir): - modcol = testdir.getmodulecol(""" - class TestClass: - disabled = True - def test_method(self): - pass - """) - l = modcol.collect() - assert len(l) == 1 - modcol = l[0] - assert isinstance(modcol, py.test.collect.Class) - assert not modcol.collect() - - def test_disabled_module(self, testdir): - modcol = testdir.getmodulecol(""" - disabled = True - def setup_module(mod): - raise ValueError - """) - assert not modcol.collect() - assert not modcol.run() - - 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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == '[0]' - assert gencolitems[0].obj.func_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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == '[0]' - assert gencolitems[0].obj.func_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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == "['seventeen']" - assert gencolitems[0].obj.func_name == 'func1' - assert gencolitems[1].name == "['fortytwo']" - assert gencolitems[1].obj.func_name == 'func1' - - 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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == "['m1']" - assert gencolitems[0].obj.func_name == 'func1' - assert gencolitems[1].name == "['m2']" - assert gencolitems[1].obj.func_name == 'func1' - - def test_module_assertion_setup(self, testdir): - modcol = testdir.getmodulecol("pass") - from py.__.magic import assertion - l = [] - py.magic.patch(assertion, "invoke", lambda: l.append(None)) - try: - modcol.setup() - finally: - py.magic.revert(assertion, "invoke") - x = l.pop() - assert x is None - py.magic.patch(assertion, "revoke", lambda: l.append(None)) - try: - modcol.teardown() - finally: - py.magic.revert(assertion, "revoke") - x = l.pop() - assert x is None - - def test_check_equality_and_cmp_basic(self, testdir): - modcol = testdir.getmodulecol(""" - def test_pass(): pass - def test_fail(): assert 0 - """) - fn1 = modcol.collect_by_name("test_pass") - assert isinstance(fn1, py.test.collect.Function) - fn2 = modcol.collect_by_name("test_pass") - assert isinstance(fn2, py.test.collect.Function) - - assert fn1 == fn2 - assert fn1 != modcol - assert cmp(fn1, fn2) == 0 - assert hash(fn1) == hash(fn2) - - fn3 = modcol.collect_by_name("test_fail") - assert isinstance(fn3, py.test.collect.Function) - assert not (fn1 == fn3) - assert fn1 != fn3 - assert cmp(fn1, fn3) == -1 - - assert cmp(fn1, 10) == -1 - assert cmp(fn2, 10) == -1 - assert cmp(fn3, 10) == -1 - for fn in fn1,fn2,fn3: - assert fn != 3 - assert fn != modcol - assert fn != [1,2,3] - assert [1,2,3] != fn - assert modcol != fn - def test_directory_file_sorting(self, testdir): p1 = testdir.makepyfile(test_one="hello") p1.dirpath().mkdir("x") @@ -261,78 +137,7 @@ res2 = modcol.collect() assert res1 == [x.name for x in res2] - def test_allow_sane_sorting_for_decorators(self, testdir): - modcol = testdir.getmodulecol(""" - def dec(f): - g = lambda: f(2) - g.place_as = f - return g - - - def test_a(y): - pass - test_a = dec(test_a) - - def test_b(y): - pass - test_b = dec(test_b) - """) - colitems = modcol.collect() - assert len(colitems) == 2 - f1, f2 = colitems - assert cmp(f2, f1) > 0 - - class TestCustomConftests: - def test_extra_python_files_and_functions(self, testdir): - testdir.makepyfile(conftest=""" - import py - class MyFunction(py.test.collect.Function): - pass - class Directory(py.test.collect.Directory): - def consider_file(self, path): - if path.check(fnmatch="check_*.py"): - return self.Module(path, parent=self) - return super(Directory, self).consider_file(path) - class myfuncmixin: - Function = MyFunction - def funcnamefilter(self, name): - return name.startswith('check_') - class Module(myfuncmixin, py.test.collect.Module): - def classnamefilter(self, name): - return name.startswith('CustomTestClass') - class Instance(myfuncmixin, py.test.collect.Instance): - pass - """) - checkfile = testdir.makepyfile(check_file=""" - def check_func(): - assert 42 == 42 - class CustomTestClass: - def check_method(self): - assert 23 == 23 - """) - # check that directory collects "check_" files - config = testdir.parseconfig() - col = config.getfsnode(checkfile.dirpath()) - colitems = col.collect() - assert len(colitems) == 1 - assert isinstance(colitems[0], py.test.collect.Module) - - # check that module collects "check_" functions and methods - config = testdir.parseconfig(checkfile) - col = config.getfsnode(checkfile) - assert isinstance(col, py.test.collect.Module) - colitems = col.collect() - assert len(colitems) == 2 - funccol = colitems[0] - assert isinstance(funccol, py.test.collect.Function) - assert funccol.name == "check_func" - clscol = colitems[1] - assert isinstance(clscol, py.test.collect.Class) - colitems = clscol.collect()[0].collect() - assert len(colitems) == 1 - assert colitems[0].name == "check_method" - def test_non_python_files(self, testdir): testdir.makepyfile(conftest=""" import py @@ -358,188 +163,6 @@ assert item.name == "hello.xxx" assert item.__class__.__name__ == "CustomItem" -def test_module_file_not_found(tmpdir): - fn = tmpdir.join('nada','no') - col = py.test.collect.Module(fn, config=dummyconfig) - py.test.raises(py.error.ENOENT, col.collect) - - -def test_order_of_execution_generator_same_codeline(testdir, tmpdir): - o = testdir.makepyfile(""" - def test_generative_order_of_execution(): - test_list = [] - expected_list = range(6) - - def list_append(item): - test_list.append(item) - - def assert_order_of_execution(): - print 'expected order', expected_list - print 'but got ', test_list - assert test_list == expected_list - - for i in expected_list: - yield list_append, i - yield assert_order_of_execution - """) - sorter = testdir.inline_run(o) - passed, skipped, failed = sorter.countoutcomes() - assert passed == 7 - assert not skipped and not failed - -def test_order_of_execution_generator_different_codeline(testdir): - o = testdir.makepyfile(""" - def test_generative_tests_different_codeline(): - test_list = [] - expected_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(): - print 'expected order', expected_list - 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 - """) - sorter = testdir.inline_run(o) # .events_from_cmdline([o]) - passed, skipped, failed = sorter.countoutcomes() - assert passed == 4 - assert not skipped and not failed - -def test_function_equality(tmpdir): - config = py.test.config._reparse([tmpdir]) - f1 = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance) - f2 = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=callable) - assert not f1 == f2 - assert f1 != f2 - f3 = py.test.collect.Function(name="name", config=config, - args=(1,2), callobj=callable) - assert not f3 == f2 - assert f3 != f2 - - assert not f3 == f1 - assert f3 != f1 - - f1_b = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance) - assert f1 == f1_b - assert not f1 != f1_b - -class Testgenitems: - def test_check_collect_hashes(self, testdir): - p = testdir.makepyfile(""" - def test_1(): - pass - - def test_2(): - pass - """) - p.copy(p.dirpath(p.purebasename + "2" + ".py")) - items, events = testdir.inline_genitems(p.dirpath()) - assert len(items) == 4 - for numi, i in enumerate(items): - for numj, j in enumerate(items): - if numj != numi: - assert hash(i) != hash(j) - assert i != j - - def test_root_conftest_syntax_error(self, testdir): - # do we want to unify behaviour with - # test_subdir_conftest_error? - p = testdir.makepyfile(conftest="raise SyntaxError\n") - py.test.raises(SyntaxError, testdir.inline_genitems, p.dirpath()) - - def test_subdir_conftest_error(self, testdir): - tmp = testdir.tmpdir - tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") - items, events = testdir.inline_genitems(tmp) - collectionfailures = events.getfailedcollections() - assert len(collectionfailures) == 1 - ev = collectionfailures[0] - assert ev.longrepr.reprcrash.message.startswith("SyntaxError") - - def test_example_items1(self, testdir): - p = testdir.makepyfile(''' - def testone(): - pass - - class TestX: - def testmethod_one(self): - pass - - class TestY(TestX): - pass - ''') - items, events = testdir.inline_genitems(p) - assert len(items) == 3 - assert items[0].name == 'testone' - assert items[1].name == 'testmethod_one' - assert items[2].name == 'testmethod_one' - - # let's also test getmodpath here - assert items[0].getmodpath() == "testone" - assert items[1].getmodpath() == "TestX.testmethod_one" - assert items[2].getmodpath() == "TestY.testmethod_one" - - s = items[0].getmodpath(stopatmodule=False) - assert s.endswith("test_example_items1.testone") - print s - - def test_collect_doctest_files_with_test_prefix(self, testdir): - testdir.maketxtfile(whatever="") - checkfile = testdir.maketxtfile(test_something=""" - alskdjalsdk - >>> i = 5 - >>> i-1 - 4 - """) - for x in (testdir.tmpdir, checkfile): - #print "checking that %s returns custom items" % (x,) - items, events = testdir.inline_genitems(x) - assert len(items) == 1 - assert isinstance(items[0], DoctestFileContent) - -class TestCollector: - def test_totrail_and_back(self, tmpdir): - a = tmpdir.ensure("a", dir=1) - tmpdir.ensure("a", "__init__.py") - x = tmpdir.ensure("a", "trail.py") - config = py.test.config._reparse([x]) - col = config.getfsnode(x) - trail = col._totrail() - assert len(trail) == 2 - assert trail[0] == a.relto(config.topdir) - assert trail[1] == ('trail.py',) - col2 = py.test.collect.Collector._fromtrail(trail, config) - assert col2.listnames() == col.listnames() - - def test_totrail_topdir_and_beyond(self, tmpdir): - config = py.test.config._reparse([tmpdir]) - col = config.getfsnode(config.topdir) - trail = col._totrail() - assert len(trail) == 2 - assert trail[0] == '.' - assert trail[1] == () - col2 = py.test.collect.Collector._fromtrail(trail, config) - assert col2.fspath == config.topdir - assert len(col2.listchain()) == 1 - col3 = config.getfsnode(config.topdir.dirpath()) - py.test.raises(ValueError, - "col3._totrail()") - class TestCollectorReprs: def test_repr_metainfo_basic_item(self, testdir): modcol = testdir.getmodulecol("") @@ -599,55 +222,3 @@ def test_method(self): pass """ - -from py.__.test.dsession.mypickle import ImmutablePickler -class PickleTransport: - def __init__(self): - self.p1 = ImmutablePickler(uneven=0) - self.p2 = ImmutablePickler(uneven=1) - - def p1_to_p2(self, obj): - return self.p2.loads(self.p1.dumps(obj)) - - def p2_to_p1(self, obj): - return self.p1.loads(self.p2.dumps(obj)) - -class TestPickling: - def setup_method(self, method): - pt = PickleTransport() - self.p1_to_p2 = pt.p1_to_p2 - self.p2_to_p1 = pt.p2_to_p1 - - def unifyconfig(self, config): - p2config = self.p1_to_p2(config) - p2config._initafterpickle(config.topdir) - return p2config - - def test_pickle_config(self): - config1 = py.test.config._reparse([]) - p2config = self.unifyconfig(config1) - assert p2config.topdir == config1.topdir - config_back = self.p2_to_p1(p2config) - assert config_back is config1 - - def test_pickle_module(self, testdir): - modcol1 = testdir.getmodulecol("def test_one(): pass") - self.unifyconfig(modcol1._config) - - modcol2a = self.p1_to_p2(modcol1) - modcol2b = self.p1_to_p2(modcol1) - assert modcol2a is modcol2b - - modcol1_back = self.p2_to_p1(modcol2a) - assert modcol1_back - - def test_pickle_func(self, testdir): - modcol1 = testdir.getmodulecol("def test_one(): pass") - self.unifyconfig(modcol1._config) - item = modcol1.collect_by_name("test_one") - item2a = self.p1_to_p2(item) - assert item is not item2a # of course - assert item2a.name == item.name - modback = self.p2_to_p1(item2a.parent) - assert modback is modcol1 - Copied: py/branch/pytestplugin/py/test/testing/test_collect_pickle.py (from r62008, py/branch/pytestplugin/py/test/testing/test_collect.py) ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect_pickle.py Sat Feb 21 18:05:43 2009 @@ -1,608 +1,9 @@ -from __future__ import generators import py -from py.__.test import event, outcome -from py.__.test.conftesthandle import Conftest -from py.__.test.collect import SetupState -from py.__.test.pycollect import DoctestFileContent - -class DummyConfig: - def __init__(self): - self._conftest = Conftest() - self._setupstate = SetupState() - self.args = [] - class dummyoption: - nomagic = False - self.option = dummyoption - def getvalue(self, name, fspath): - return self._conftest.rget(name, fspath) - -def setup_module(mod): - mod.dummyconfig = DummyConfig() - -def test_collect_versus_item(): - from py.__.test.collect import Collector, Item - assert not issubclass(Collector, Item) - assert not issubclass(Item, Collector) - -def test_ignored_certain_directories(tmpdir): - tmpdir.ensure("_darcs", 'test_notfound.py') - tmpdir.ensure("CVS", 'test_notfound.py') - tmpdir.ensure("{arch}", 'test_notfound.py') - tmpdir.ensure(".whatever", 'test_notfound.py') - tmpdir.ensure(".bzr", 'test_notfound.py') - tmpdir.ensure("normal", 'test_found.py') - tmpdir.ensure('test_found.py') - - col = py.test.collect.Directory(tmpdir, config=dummyconfig) - items = col.collect() - names = [x.name for x in items] - assert len(items) == 2 - assert 'normal' in names - assert 'test_found.py' in names - -class TestCollect: - def test_failing_import(self, testdir): - modcol = testdir.getmodulecol("import alksdjalskdjalkjals") - py.test.raises(ImportError, modcol.collect) - py.test.raises(ImportError, modcol.collect) - py.test.raises(ImportError, modcol.run) - - def test_syntax_error_in_module(self, testdir): - modcol = testdir.getmodulecol("this is a syntax error") - py.test.raises(SyntaxError, modcol.collect) - py.test.raises(SyntaxError, modcol.collect) - py.test.raises(SyntaxError, modcol.run) - - def test_listnames_and__getitembynames(self, testdir): - modcol = testdir.getmodulecol("pass", withinit=True) - names = modcol.listnames() - print names - dircol = modcol._config.getfsnode(modcol._config.topdir) - x = dircol._getitembynames(names) - assert modcol.name == x.name - assert modcol.name == x.name - - def test_listnames_getitembynames_custom(self, testdir): - hello = testdir.makefile(".xxx", hello="world") - testdir.makepyfile(conftest=""" - import py - class CustomFile(py.test.collect.File): - pass - class MyDirectory(py.test.collect.Directory): - def collect(self): - return [CustomFile(self.fspath.join("hello.xxx"), parent=self)] - Directory = MyDirectory - """) - config = testdir.parseconfig(hello) - node = config.getfsnode(hello) - assert isinstance(node, py.test.collect.File) - assert node.name == "hello.xxx" - names = node.listnames()[1:] - dircol = config.getfsnode(config.topdir) - node = dircol._getitembynames(names) - assert isinstance(node, py.test.collect.File) - - def test_found_certain_testfiles(self, testdir): - p1 = testdir.makepyfile(test_found = "pass", found_test="pass") - col = py.test.collect.Directory(p1.dirpath(), config=dummyconfig) - items = col.collect() # Directory collect returns files sorted by name - assert len(items) == 2 - assert items[1].name == 'test_found.py' - assert items[0].name == 'found_test.py' - - def test_disabled_class(self, testdir): - modcol = testdir.getmodulecol(""" - class TestClass: - disabled = True - def test_method(self): - pass - """) - l = modcol.collect() - assert len(l) == 1 - modcol = l[0] - assert isinstance(modcol, py.test.collect.Class) - assert not modcol.collect() - - def test_disabled_module(self, testdir): - modcol = testdir.getmodulecol(""" - disabled = True - def setup_module(mod): - raise ValueError - """) - assert not modcol.collect() - assert not modcol.run() - - 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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == '[0]' - assert gencolitems[0].obj.func_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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == '[0]' - assert gencolitems[0].obj.func_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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == "['seventeen']" - assert gencolitems[0].obj.func_name == 'func1' - assert gencolitems[1].name == "['fortytwo']" - assert gencolitems[1].obj.func_name == 'func1' - - 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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == "['m1']" - assert gencolitems[0].obj.func_name == 'func1' - assert gencolitems[1].name == "['m2']" - assert gencolitems[1].obj.func_name == 'func1' - - def test_module_assertion_setup(self, testdir): - modcol = testdir.getmodulecol("pass") - from py.__.magic import assertion - l = [] - py.magic.patch(assertion, "invoke", lambda: l.append(None)) - try: - modcol.setup() - finally: - py.magic.revert(assertion, "invoke") - x = l.pop() - assert x is None - py.magic.patch(assertion, "revoke", lambda: l.append(None)) - try: - modcol.teardown() - finally: - py.magic.revert(assertion, "revoke") - x = l.pop() - assert x is None - - def test_check_equality_and_cmp_basic(self, testdir): - modcol = testdir.getmodulecol(""" - def test_pass(): pass - def test_fail(): assert 0 - """) - fn1 = modcol.collect_by_name("test_pass") - assert isinstance(fn1, py.test.collect.Function) - fn2 = modcol.collect_by_name("test_pass") - assert isinstance(fn2, py.test.collect.Function) - - assert fn1 == fn2 - assert fn1 != modcol - assert cmp(fn1, fn2) == 0 - assert hash(fn1) == hash(fn2) - - fn3 = modcol.collect_by_name("test_fail") - assert isinstance(fn3, py.test.collect.Function) - assert not (fn1 == fn3) - assert fn1 != fn3 - assert cmp(fn1, fn3) == -1 - - assert cmp(fn1, 10) == -1 - assert cmp(fn2, 10) == -1 - assert cmp(fn3, 10) == -1 - for fn in fn1,fn2,fn3: - assert fn != 3 - assert fn != modcol - assert fn != [1,2,3] - assert [1,2,3] != fn - assert modcol != fn - - def test_directory_file_sorting(self, testdir): - p1 = testdir.makepyfile(test_one="hello") - p1.dirpath().mkdir("x") - p1.dirpath().mkdir("dir1") - testdir.makepyfile(test_two="hello") - p1.dirpath().mkdir("dir2") - config = testdir.parseconfig() - col = config.getfsnode(p1.dirpath()) - names = [x.name for x in col.collect()] - assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"] - - def test_collector_deprecated_run_method(self, testdir): - modcol = testdir.getmodulecol("pass") - res1 = py.test.deprecated_call(modcol.run) - res2 = modcol.collect() - assert res1 == [x.name for x in res2] - - def test_allow_sane_sorting_for_decorators(self, testdir): - modcol = testdir.getmodulecol(""" - def dec(f): - g = lambda: f(2) - g.place_as = f - return g - - - def test_a(y): - pass - test_a = dec(test_a) - - def test_b(y): - pass - test_b = dec(test_b) - """) - colitems = modcol.collect() - assert len(colitems) == 2 - f1, f2 = colitems - assert cmp(f2, f1) > 0 - - -class TestCustomConftests: - def test_extra_python_files_and_functions(self, testdir): - testdir.makepyfile(conftest=""" - import py - class MyFunction(py.test.collect.Function): - pass - class Directory(py.test.collect.Directory): - def consider_file(self, path): - if path.check(fnmatch="check_*.py"): - return self.Module(path, parent=self) - return super(Directory, self).consider_file(path) - class myfuncmixin: - Function = MyFunction - def funcnamefilter(self, name): - return name.startswith('check_') - class Module(myfuncmixin, py.test.collect.Module): - def classnamefilter(self, name): - return name.startswith('CustomTestClass') - class Instance(myfuncmixin, py.test.collect.Instance): - pass - """) - checkfile = testdir.makepyfile(check_file=""" - def check_func(): - assert 42 == 42 - class CustomTestClass: - def check_method(self): - assert 23 == 23 - """) - # check that directory collects "check_" files - config = testdir.parseconfig() - col = config.getfsnode(checkfile.dirpath()) - colitems = col.collect() - assert len(colitems) == 1 - assert isinstance(colitems[0], py.test.collect.Module) - - # check that module collects "check_" functions and methods - config = testdir.parseconfig(checkfile) - col = config.getfsnode(checkfile) - assert isinstance(col, py.test.collect.Module) - colitems = col.collect() - assert len(colitems) == 2 - funccol = colitems[0] - assert isinstance(funccol, py.test.collect.Function) - assert funccol.name == "check_func" - clscol = colitems[1] - assert isinstance(clscol, py.test.collect.Class) - colitems = clscol.collect()[0].collect() - assert len(colitems) == 1 - assert colitems[0].name == "check_method" - - def test_non_python_files(self, testdir): - testdir.makepyfile(conftest=""" - import py - class CustomItem(py.test.collect.Item): - def run(self): - pass - class Directory(py.test.collect.Directory): - def consider_file(self, fspath): - if fspath.ext == ".xxx": - return CustomItem(fspath.basename, parent=self) - """) - checkfile = testdir.makefile(ext="xxx", hello="world") - testdir.makepyfile(x="") - testdir.maketxtfile(x="") - config = testdir.parseconfig() - dircol = config.getfsnode(checkfile.dirpath()) - colitems = dircol.collect() - assert len(colitems) == 1 - assert colitems[0].name == "hello.xxx" - assert colitems[0].__class__.__name__ == "CustomItem" - - item = config.getfsnode(checkfile) - assert item.name == "hello.xxx" - assert item.__class__.__name__ == "CustomItem" - -def test_module_file_not_found(tmpdir): - fn = tmpdir.join('nada','no') - col = py.test.collect.Module(fn, config=dummyconfig) - py.test.raises(py.error.ENOENT, col.collect) - - -def test_order_of_execution_generator_same_codeline(testdir, tmpdir): - o = testdir.makepyfile(""" - def test_generative_order_of_execution(): - test_list = [] - expected_list = range(6) - - def list_append(item): - test_list.append(item) - - def assert_order_of_execution(): - print 'expected order', expected_list - print 'but got ', test_list - assert test_list == expected_list - - for i in expected_list: - yield list_append, i - yield assert_order_of_execution - """) - sorter = testdir.inline_run(o) - passed, skipped, failed = sorter.countoutcomes() - assert passed == 7 - assert not skipped and not failed - -def test_order_of_execution_generator_different_codeline(testdir): - o = testdir.makepyfile(""" - def test_generative_tests_different_codeline(): - test_list = [] - expected_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(): - print 'expected order', expected_list - 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 - """) - sorter = testdir.inline_run(o) # .events_from_cmdline([o]) - passed, skipped, failed = sorter.countoutcomes() - assert passed == 4 - assert not skipped and not failed - -def test_function_equality(tmpdir): - config = py.test.config._reparse([tmpdir]) - f1 = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance) - f2 = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=callable) - assert not f1 == f2 - assert f1 != f2 - f3 = py.test.collect.Function(name="name", config=config, - args=(1,2), callobj=callable) - assert not f3 == f2 - assert f3 != f2 - - assert not f3 == f1 - assert f3 != f1 - - f1_b = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance) - assert f1 == f1_b - assert not f1 != f1_b - -class Testgenitems: - def test_check_collect_hashes(self, testdir): - p = testdir.makepyfile(""" - def test_1(): - pass - - def test_2(): - pass - """) - p.copy(p.dirpath(p.purebasename + "2" + ".py")) - items, events = testdir.inline_genitems(p.dirpath()) - assert len(items) == 4 - for numi, i in enumerate(items): - for numj, j in enumerate(items): - if numj != numi: - assert hash(i) != hash(j) - assert i != j - - def test_root_conftest_syntax_error(self, testdir): - # do we want to unify behaviour with - # test_subdir_conftest_error? - p = testdir.makepyfile(conftest="raise SyntaxError\n") - py.test.raises(SyntaxError, testdir.inline_genitems, p.dirpath()) - - def test_subdir_conftest_error(self, testdir): - tmp = testdir.tmpdir - tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") - items, events = testdir.inline_genitems(tmp) - collectionfailures = events.getfailedcollections() - assert len(collectionfailures) == 1 - ev = collectionfailures[0] - assert ev.longrepr.reprcrash.message.startswith("SyntaxError") - - def test_example_items1(self, testdir): - p = testdir.makepyfile(''' - def testone(): - pass - - class TestX: - def testmethod_one(self): - pass - - class TestY(TestX): - pass - ''') - items, events = testdir.inline_genitems(p) - assert len(items) == 3 - assert items[0].name == 'testone' - assert items[1].name == 'testmethod_one' - assert items[2].name == 'testmethod_one' - - # let's also test getmodpath here - assert items[0].getmodpath() == "testone" - assert items[1].getmodpath() == "TestX.testmethod_one" - assert items[2].getmodpath() == "TestY.testmethod_one" - - s = items[0].getmodpath(stopatmodule=False) - assert s.endswith("test_example_items1.testone") - print s - - def test_collect_doctest_files_with_test_prefix(self, testdir): - testdir.maketxtfile(whatever="") - checkfile = testdir.maketxtfile(test_something=""" - alskdjalsdk - >>> i = 5 - >>> i-1 - 4 - """) - for x in (testdir.tmpdir, checkfile): - #print "checking that %s returns custom items" % (x,) - items, events = testdir.inline_genitems(x) - assert len(items) == 1 - assert isinstance(items[0], DoctestFileContent) - -class TestCollector: - def test_totrail_and_back(self, tmpdir): - a = tmpdir.ensure("a", dir=1) - tmpdir.ensure("a", "__init__.py") - x = tmpdir.ensure("a", "trail.py") - config = py.test.config._reparse([x]) - col = config.getfsnode(x) - trail = col._totrail() - assert len(trail) == 2 - assert trail[0] == a.relto(config.topdir) - assert trail[1] == ('trail.py',) - col2 = py.test.collect.Collector._fromtrail(trail, config) - assert col2.listnames() == col.listnames() - - def test_totrail_topdir_and_beyond(self, tmpdir): - config = py.test.config._reparse([tmpdir]) - col = config.getfsnode(config.topdir) - trail = col._totrail() - assert len(trail) == 2 - assert trail[0] == '.' - assert trail[1] == () - col2 = py.test.collect.Collector._fromtrail(trail, config) - assert col2.fspath == config.topdir - assert len(col2.listchain()) == 1 - col3 = config.getfsnode(config.topdir.dirpath()) - py.test.raises(ValueError, - "col3._totrail()") - -class TestCollectorReprs: - def test_repr_metainfo_basic_item(self, testdir): - modcol = testdir.getmodulecol("") - Item = py.test.collect.Item - item = Item("virtual", parent=modcol) - info = item.repr_metainfo() - assert info.fspath == modcol.fspath - assert not info.lineno - assert info.modpath == "Item" - - def test_repr_metainfo_func(self, testdir): - item = testdir.getitem("def test_func(): pass") - info = item.repr_metainfo() - assert info.fspath == item.fspath - assert info.lineno == 0 - assert info.modpath == "test_func" - - def test_repr_metainfo_class(self, testdir): - modcol = testdir.getmodulecol(""" - # lineno 0 - class TestClass: - def test_hello(self): pass - """) - classcol = modcol.collect_by_name("TestClass") - info = classcol.repr_metainfo() - assert info.fspath == modcol.fspath - assert info.lineno == 1 - assert info.modpath == "TestClass" - - def test_repr_metainfo_generator(self, testdir): - modcol = testdir.getmodulecol(""" - # lineno 0 - def test_gen(): - def check(x): - assert x - yield check, 3 - """) - gencol = modcol.collect_by_name("test_gen") - info = gencol.repr_metainfo() - assert info.fspath == modcol.fspath - assert info.lineno == 1 - assert info.modpath == "test_gen" - - genitem = gencol.collect()[0] - info = genitem.repr_metainfo() - assert info.fspath == modcol.fspath - assert info.lineno == 2 - assert info.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 - """ - -from py.__.test.dsession.mypickle import ImmutablePickler class PickleTransport: def __init__(self): + from py.__.test.dsession.mypickle import ImmutablePickler self.p1 = ImmutablePickler(uneven=0) self.p2 = ImmutablePickler(uneven=1) @@ -612,42 +13,42 @@ def p2_to_p1(self, obj): return self.p1.loads(self.p2.dumps(obj)) -class TestPickling: - def setup_method(self, method): - pt = PickleTransport() - self.p1_to_p2 = pt.p1_to_p2 - self.p2_to_p1 = pt.p2_to_p1 - def unifyconfig(self, config): p2config = self.p1_to_p2(config) p2config._initafterpickle(config.topdir) return p2config - def test_pickle_config(self): +class TestPickling: + def pytest_pyfunc_arg(self, pyfuncitem, argname): + if argname == "pickletransport": + pt = PickleTransport() + return pt, None + + def test_pickle_config(self, pickletransport): config1 = py.test.config._reparse([]) - p2config = self.unifyconfig(config1) + p2config = pickletransport.unifyconfig(config1) assert p2config.topdir == config1.topdir - config_back = self.p2_to_p1(p2config) + config_back = pickletransport.p2_to_p1(p2config) assert config_back is config1 - def test_pickle_module(self, testdir): + def test_pickle_module(self, testdir, pickletransport): modcol1 = testdir.getmodulecol("def test_one(): pass") - self.unifyconfig(modcol1._config) + pickletransport.unifyconfig(modcol1._config) - modcol2a = self.p1_to_p2(modcol1) - modcol2b = self.p1_to_p2(modcol1) + modcol2a = pickletransport.p1_to_p2(modcol1) + modcol2b = pickletransport.p1_to_p2(modcol1) assert modcol2a is modcol2b - modcol1_back = self.p2_to_p1(modcol2a) + modcol1_back = pickletransport.p2_to_p1(modcol2a) assert modcol1_back - def test_pickle_func(self, testdir): + def test_pickle_func(self, testdir, pickletransport): modcol1 = testdir.getmodulecol("def test_one(): pass") - self.unifyconfig(modcol1._config) + pickletransport.unifyconfig(modcol1._config) item = modcol1.collect_by_name("test_one") - item2a = self.p1_to_p2(item) + item2a = pickletransport.p1_to_p2(item) assert item is not item2a # of course assert item2a.name == item.name - modback = self.p2_to_p1(item2a.parent) + modback = pickletransport.p2_to_p1(item2a.parent) assert modback is modcol1 Copied: py/branch/pytestplugin/py/test/testing/test_pycollect.py (from r62008, py/branch/pytestplugin/py/test/testing/test_collect.py) ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pycollect.py Sat Feb 21 18:05:43 2009 @@ -1,48 +1,12 @@ -from __future__ import generators import py -from py.__.test import event, outcome -from py.__.test.conftesthandle import Conftest -from py.__.test.collect import SetupState -from py.__.test.pycollect import DoctestFileContent +class TestModule: + def test_module_file_not_found(self, testdir): + tmpdir = testdir.tmpdir + fn = tmpdir.join('nada','no') + col = py.test.collect.Module(fn, config=testdir.parseconfig(tmpdir)) + py.test.raises(py.error.ENOENT, col.collect) - -class DummyConfig: - def __init__(self): - self._conftest = Conftest() - self._setupstate = SetupState() - self.args = [] - class dummyoption: - nomagic = False - self.option = dummyoption - def getvalue(self, name, fspath): - return self._conftest.rget(name, fspath) - -def setup_module(mod): - mod.dummyconfig = DummyConfig() - -def test_collect_versus_item(): - from py.__.test.collect import Collector, Item - assert not issubclass(Collector, Item) - assert not issubclass(Item, Collector) - -def test_ignored_certain_directories(tmpdir): - tmpdir.ensure("_darcs", 'test_notfound.py') - tmpdir.ensure("CVS", 'test_notfound.py') - tmpdir.ensure("{arch}", 'test_notfound.py') - tmpdir.ensure(".whatever", 'test_notfound.py') - tmpdir.ensure(".bzr", 'test_notfound.py') - tmpdir.ensure("normal", 'test_found.py') - tmpdir.ensure('test_found.py') - - col = py.test.collect.Directory(tmpdir, config=dummyconfig) - items = col.collect() - names = [x.name for x in items] - assert len(items) == 2 - assert 'normal' in names - assert 'test_found.py' in names - -class TestCollect: def test_failing_import(self, testdir): modcol = testdir.getmodulecol("import alksdjalskdjalkjals") py.test.raises(ImportError, modcol.collect) @@ -55,43 +19,35 @@ py.test.raises(SyntaxError, modcol.collect) py.test.raises(SyntaxError, modcol.run) - def test_listnames_and__getitembynames(self, testdir): - modcol = testdir.getmodulecol("pass", withinit=True) - names = modcol.listnames() - print names - dircol = modcol._config.getfsnode(modcol._config.topdir) - x = dircol._getitembynames(names) - assert modcol.name == x.name - assert modcol.name == x.name + def test_module_assertion_setup(self, testdir): + modcol = testdir.getmodulecol("pass") + from py.__.magic import assertion + l = [] + py.magic.patch(assertion, "invoke", lambda: l.append(None)) + try: + modcol.setup() + finally: + py.magic.revert(assertion, "invoke") + x = l.pop() + assert x is None + py.magic.patch(assertion, "revoke", lambda: l.append(None)) + try: + modcol.teardown() + finally: + py.magic.revert(assertion, "revoke") + x = l.pop() + assert x is None - def test_listnames_getitembynames_custom(self, testdir): - hello = testdir.makefile(".xxx", hello="world") - testdir.makepyfile(conftest=""" - import py - class CustomFile(py.test.collect.File): - pass - class MyDirectory(py.test.collect.Directory): - def collect(self): - return [CustomFile(self.fspath.join("hello.xxx"), parent=self)] - Directory = MyDirectory - """) - config = testdir.parseconfig(hello) - node = config.getfsnode(hello) - assert isinstance(node, py.test.collect.File) - assert node.name == "hello.xxx" - names = node.listnames()[1:] - dircol = config.getfsnode(config.topdir) - node = dircol._getitembynames(names) - assert isinstance(node, py.test.collect.File) - - def test_found_certain_testfiles(self, testdir): - p1 = testdir.makepyfile(test_found = "pass", found_test="pass") - col = py.test.collect.Directory(p1.dirpath(), config=dummyconfig) - items = col.collect() # Directory collect returns files sorted by name - assert len(items) == 2 - assert items[1].name == 'test_found.py' - assert items[0].name == 'found_test.py' + def test_disabled_module(self, testdir): + modcol = testdir.getmodulecol(""" + disabled = True + def setup_module(mod): + raise ValueError + """) + assert not modcol.collect() + assert not modcol.run() +class TestClass: def test_disabled_class(self, testdir): modcol = testdir.getmodulecol(""" class TestClass: @@ -105,15 +61,7 @@ assert isinstance(modcol, py.test.collect.Class) assert not modcol.collect() - def test_disabled_module(self, testdir): - modcol = testdir.getmodulecol(""" - disabled = True - def setup_module(mod): - raise ValueError - """) - assert not modcol.collect() - assert not modcol.run() - +class TestGenerator: def test_generative_functions(self, testdir): modcol = testdir.getmodulecol(""" def func1(arg, arg2): @@ -194,25 +142,82 @@ assert gencolitems[1].name == "['m2']" assert gencolitems[1].obj.func_name == 'func1' - def test_module_assertion_setup(self, testdir): - modcol = testdir.getmodulecol("pass") - from py.__.magic import assertion - l = [] - py.magic.patch(assertion, "invoke", lambda: l.append(None)) - try: - modcol.setup() - finally: - py.magic.revert(assertion, "invoke") - x = l.pop() - assert x is None - py.magic.patch(assertion, "revoke", lambda: l.append(None)) - try: - modcol.teardown() - finally: - py.magic.revert(assertion, "revoke") - x = l.pop() - assert x is None + def test_order_of_execution_generator_same_codeline(self, testdir, tmpdir): + o = testdir.makepyfile(""" + def test_generative_order_of_execution(): + test_list = [] + expected_list = range(6) + + def list_append(item): + test_list.append(item) + + def assert_order_of_execution(): + print 'expected order', expected_list + print 'but got ', test_list + assert test_list == expected_list + + for i in expected_list: + yield list_append, i + yield assert_order_of_execution + """) + sorter = testdir.inline_run(o) + passed, skipped, failed = sorter.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(): + test_list = [] + expected_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(): + print 'expected order', expected_list + 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 + """) + sorter = testdir.inline_run(o) # .events_from_cmdline([o]) + passed, skipped, failed = sorter.countoutcomes() + assert passed == 4 + assert not skipped and not failed + +class TestFunction: + def test_function_equality(tmpdir): + config = py.test.config._reparse([tmpdir]) + f1 = py.test.collect.Function(name="name", config=config, + args=(1,), callobj=isinstance) + f2 = py.test.collect.Function(name="name", config=config, + args=(1,), callobj=callable) + assert not f1 == f2 + assert f1 != f2 + f3 = py.test.collect.Function(name="name", config=config, + args=(1,2), callobj=callable) + assert not f3 == f2 + assert f3 != f2 + + assert not f3 == f1 + assert f3 != f1 + + f1_b = py.test.collect.Function(name="name", config=config, + args=(1,), callobj=isinstance) + assert f1 == f1_b + assert not f1 != f1_b +class TestSorting: def test_check_equality_and_cmp_basic(self, testdir): modcol = testdir.getmodulecol(""" def test_pass(): pass @@ -244,23 +249,6 @@ assert [1,2,3] != fn assert modcol != fn - def test_directory_file_sorting(self, testdir): - p1 = testdir.makepyfile(test_one="hello") - p1.dirpath().mkdir("x") - p1.dirpath().mkdir("dir1") - testdir.makepyfile(test_two="hello") - p1.dirpath().mkdir("dir2") - config = testdir.parseconfig() - col = config.getfsnode(p1.dirpath()) - names = [x.name for x in col.collect()] - assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"] - - def test_collector_deprecated_run_method(self, testdir): - modcol = testdir.getmodulecol("pass") - res1 = py.test.deprecated_call(modcol.run) - res2 = modcol.collect() - assert res1 == [x.name for x in res2] - def test_allow_sane_sorting_for_decorators(self, testdir): modcol = testdir.getmodulecol(""" def dec(f): @@ -282,8 +270,7 @@ f1, f2 = colitems assert cmp(f2, f1) > 0 - -class TestCustomConftests: +class TestCustomConftest: def test_extra_python_files_and_functions(self, testdir): testdir.makepyfile(conftest=""" import py @@ -333,321 +320,3 @@ assert len(colitems) == 1 assert colitems[0].name == "check_method" - def test_non_python_files(self, testdir): - testdir.makepyfile(conftest=""" - import py - class CustomItem(py.test.collect.Item): - def run(self): - pass - class Directory(py.test.collect.Directory): - def consider_file(self, fspath): - if fspath.ext == ".xxx": - return CustomItem(fspath.basename, parent=self) - """) - checkfile = testdir.makefile(ext="xxx", hello="world") - testdir.makepyfile(x="") - testdir.maketxtfile(x="") - config = testdir.parseconfig() - dircol = config.getfsnode(checkfile.dirpath()) - colitems = dircol.collect() - assert len(colitems) == 1 - assert colitems[0].name == "hello.xxx" - assert colitems[0].__class__.__name__ == "CustomItem" - - item = config.getfsnode(checkfile) - assert item.name == "hello.xxx" - assert item.__class__.__name__ == "CustomItem" - -def test_module_file_not_found(tmpdir): - fn = tmpdir.join('nada','no') - col = py.test.collect.Module(fn, config=dummyconfig) - py.test.raises(py.error.ENOENT, col.collect) - - -def test_order_of_execution_generator_same_codeline(testdir, tmpdir): - o = testdir.makepyfile(""" - def test_generative_order_of_execution(): - test_list = [] - expected_list = range(6) - - def list_append(item): - test_list.append(item) - - def assert_order_of_execution(): - print 'expected order', expected_list - print 'but got ', test_list - assert test_list == expected_list - - for i in expected_list: - yield list_append, i - yield assert_order_of_execution - """) - sorter = testdir.inline_run(o) - passed, skipped, failed = sorter.countoutcomes() - assert passed == 7 - assert not skipped and not failed - -def test_order_of_execution_generator_different_codeline(testdir): - o = testdir.makepyfile(""" - def test_generative_tests_different_codeline(): - test_list = [] - expected_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(): - print 'expected order', expected_list - 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 - """) - sorter = testdir.inline_run(o) # .events_from_cmdline([o]) - passed, skipped, failed = sorter.countoutcomes() - assert passed == 4 - assert not skipped and not failed - -def test_function_equality(tmpdir): - config = py.test.config._reparse([tmpdir]) - f1 = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance) - f2 = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=callable) - assert not f1 == f2 - assert f1 != f2 - f3 = py.test.collect.Function(name="name", config=config, - args=(1,2), callobj=callable) - assert not f3 == f2 - assert f3 != f2 - - assert not f3 == f1 - assert f3 != f1 - - f1_b = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance) - assert f1 == f1_b - assert not f1 != f1_b - -class Testgenitems: - def test_check_collect_hashes(self, testdir): - p = testdir.makepyfile(""" - def test_1(): - pass - - def test_2(): - pass - """) - p.copy(p.dirpath(p.purebasename + "2" + ".py")) - items, events = testdir.inline_genitems(p.dirpath()) - assert len(items) == 4 - for numi, i in enumerate(items): - for numj, j in enumerate(items): - if numj != numi: - assert hash(i) != hash(j) - assert i != j - - def test_root_conftest_syntax_error(self, testdir): - # do we want to unify behaviour with - # test_subdir_conftest_error? - p = testdir.makepyfile(conftest="raise SyntaxError\n") - py.test.raises(SyntaxError, testdir.inline_genitems, p.dirpath()) - - def test_subdir_conftest_error(self, testdir): - tmp = testdir.tmpdir - tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") - items, events = testdir.inline_genitems(tmp) - collectionfailures = events.getfailedcollections() - assert len(collectionfailures) == 1 - ev = collectionfailures[0] - assert ev.longrepr.reprcrash.message.startswith("SyntaxError") - - def test_example_items1(self, testdir): - p = testdir.makepyfile(''' - def testone(): - pass - - class TestX: - def testmethod_one(self): - pass - - class TestY(TestX): - pass - ''') - items, events = testdir.inline_genitems(p) - assert len(items) == 3 - assert items[0].name == 'testone' - assert items[1].name == 'testmethod_one' - assert items[2].name == 'testmethod_one' - - # let's also test getmodpath here - assert items[0].getmodpath() == "testone" - assert items[1].getmodpath() == "TestX.testmethod_one" - assert items[2].getmodpath() == "TestY.testmethod_one" - - s = items[0].getmodpath(stopatmodule=False) - assert s.endswith("test_example_items1.testone") - print s - - def test_collect_doctest_files_with_test_prefix(self, testdir): - testdir.maketxtfile(whatever="") - checkfile = testdir.maketxtfile(test_something=""" - alskdjalsdk - >>> i = 5 - >>> i-1 - 4 - """) - for x in (testdir.tmpdir, checkfile): - #print "checking that %s returns custom items" % (x,) - items, events = testdir.inline_genitems(x) - assert len(items) == 1 - assert isinstance(items[0], DoctestFileContent) - -class TestCollector: - def test_totrail_and_back(self, tmpdir): - a = tmpdir.ensure("a", dir=1) - tmpdir.ensure("a", "__init__.py") - x = tmpdir.ensure("a", "trail.py") - config = py.test.config._reparse([x]) - col = config.getfsnode(x) - trail = col._totrail() - assert len(trail) == 2 - assert trail[0] == a.relto(config.topdir) - assert trail[1] == ('trail.py',) - col2 = py.test.collect.Collector._fromtrail(trail, config) - assert col2.listnames() == col.listnames() - - def test_totrail_topdir_and_beyond(self, tmpdir): - config = py.test.config._reparse([tmpdir]) - col = config.getfsnode(config.topdir) - trail = col._totrail() - assert len(trail) == 2 - assert trail[0] == '.' - assert trail[1] == () - col2 = py.test.collect.Collector._fromtrail(trail, config) - assert col2.fspath == config.topdir - assert len(col2.listchain()) == 1 - col3 = config.getfsnode(config.topdir.dirpath()) - py.test.raises(ValueError, - "col3._totrail()") - -class TestCollectorReprs: - def test_repr_metainfo_basic_item(self, testdir): - modcol = testdir.getmodulecol("") - Item = py.test.collect.Item - item = Item("virtual", parent=modcol) - info = item.repr_metainfo() - assert info.fspath == modcol.fspath - assert not info.lineno - assert info.modpath == "Item" - - def test_repr_metainfo_func(self, testdir): - item = testdir.getitem("def test_func(): pass") - info = item.repr_metainfo() - assert info.fspath == item.fspath - assert info.lineno == 0 - assert info.modpath == "test_func" - - def test_repr_metainfo_class(self, testdir): - modcol = testdir.getmodulecol(""" - # lineno 0 - class TestClass: - def test_hello(self): pass - """) - classcol = modcol.collect_by_name("TestClass") - info = classcol.repr_metainfo() - assert info.fspath == modcol.fspath - assert info.lineno == 1 - assert info.modpath == "TestClass" - - def test_repr_metainfo_generator(self, testdir): - modcol = testdir.getmodulecol(""" - # lineno 0 - def test_gen(): - def check(x): - assert x - yield check, 3 - """) - gencol = modcol.collect_by_name("test_gen") - info = gencol.repr_metainfo() - assert info.fspath == modcol.fspath - assert info.lineno == 1 - assert info.modpath == "test_gen" - - genitem = gencol.collect()[0] - info = genitem.repr_metainfo() - assert info.fspath == modcol.fspath - assert info.lineno == 2 - assert info.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 - """ - -from py.__.test.dsession.mypickle import ImmutablePickler -class PickleTransport: - def __init__(self): - self.p1 = ImmutablePickler(uneven=0) - self.p2 = ImmutablePickler(uneven=1) - - def p1_to_p2(self, obj): - return self.p2.loads(self.p1.dumps(obj)) - - def p2_to_p1(self, obj): - return self.p1.loads(self.p2.dumps(obj)) - -class TestPickling: - def setup_method(self, method): - pt = PickleTransport() - self.p1_to_p2 = pt.p1_to_p2 - self.p2_to_p1 = pt.p2_to_p1 - - def unifyconfig(self, config): - p2config = self.p1_to_p2(config) - p2config._initafterpickle(config.topdir) - return p2config - - def test_pickle_config(self): - config1 = py.test.config._reparse([]) - p2config = self.unifyconfig(config1) - assert p2config.topdir == config1.topdir - config_back = self.p2_to_p1(p2config) - assert config_back is config1 - - def test_pickle_module(self, testdir): - modcol1 = testdir.getmodulecol("def test_one(): pass") - self.unifyconfig(modcol1._config) - - modcol2a = self.p1_to_p2(modcol1) - modcol2b = self.p1_to_p2(modcol1) - assert modcol2a is modcol2b - - modcol1_back = self.p2_to_p1(modcol2a) - assert modcol1_back - - def test_pickle_func(self, testdir): - modcol1 = testdir.getmodulecol("def test_one(): pass") - self.unifyconfig(modcol1._config) - item = modcol1.collect_by_name("test_one") - item2a = self.p1_to_p2(item) - assert item is not item2a # of course - assert item2a.name == item.name - modback = self.p2_to_p1(item2a.parent) - assert modback is modcol1 - From hpk at codespeak.net Sat Feb 21 18:08:35 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 21 Feb 2009 18:08:35 +0100 (CET) Subject: [py-svn] r62078 - py/branch/pytestplugin/py/test/testing Message-ID: <20090221170835.1890E1684DB@codespeak.net> Author: hpk Date: Sat Feb 21 18:08:34 2009 New Revision: 62078 Modified: py/branch/pytestplugin/py/test/testing/test_pycollect.py Log: ups Modified: py/branch/pytestplugin/py/test/testing/test_pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pycollect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pycollect.py Sat Feb 21 18:08:34 2009 @@ -196,7 +196,7 @@ assert not skipped and not failed class TestFunction: - def test_function_equality(tmpdir): + def test_function_equality(self, tmpdir): config = py.test.config._reparse([tmpdir]) f1 = py.test.collect.Function(name="name", config=config, args=(1,), callobj=isinstance) From hpk at codespeak.net Sat Feb 21 18:31:31 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 21 Feb 2009 18:31:31 +0100 (CET) Subject: [py-svn] r62079 - in py/branch/pytestplugin/py/test: . testing Message-ID: <20090221173131.82D6B1684AA@codespeak.net> Author: hpk Date: Sat Feb 21 18:31:30 2009 New Revision: 62079 Modified: py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/testing/test_pycollect.py Log: revised pyfuncarg protocol. Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Sat Feb 21 18:31:30 2009 @@ -343,6 +343,31 @@ call() super(Function, self).teardown() + def lookup_allargs(self): + kwargs = {} + if not self._args: + # standard Python Test function/method case + funcobj = self.obj + startindex = getattr(funcobj, 'im_self', None) and 1 or 0 + for argname in py.std.inspect.getargs(self.obj.func_code)[0][startindex:]: + kwargs[argname] = self.lookup_onearg(argname) + else: + pass # XXX lookup of arguments for yielded/generated tests as well + return kwargs + + def lookup_onearg(self, argname): + value = self._config.pytestplugins.call_firstresult( + "pytest_pyfuncarg_" + argname, pyfuncitem=self) + if value is not None: + return value + else: + print "pytestplugins is", self._config.pytestplugins + print "could not find argument %r, plugins=%r" %( + argname,self._config.pytestplugins._plugins) + for x in call.methods: + print x + raise TypeError("could not provide funcargument %r" %(argname,)) + def getkwargs(self): kwargs = {} if not self._args: @@ -370,7 +395,7 @@ print "pytestplugins is", self._config.pytestplugins print "could not find argument %r, plugins=%r" %( argname,self._config.pytestplugins._plugins) - for x in call.methods: + for x in multicall.methods: print x raise TypeError("could not provide funcargument %r" %(argname,)) Modified: py/branch/pytestplugin/py/test/testing/test_pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pycollect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pycollect.py Sat Feb 21 18:31:30 2009 @@ -217,6 +217,19 @@ assert f1 == f1_b assert not f1 != f1_b + def test_pyfuncarg_basic(self, testdir): + item = testdir.getitem("def test_func(some, other): pass") + class Provider: + def pytest_pyfuncarg_some(self, pyfuncitem): + return pyfuncitem.name + def pytest_pyfuncarg_other(self, pyfuncitem): + return 42 + item._config.pytestplugins.register(Provider()) + kw = item.lookup_allargs() + assert len(kw) == 2 + assert kw['some'] == "test_func" + assert kw['other'] == 42 + class TestSorting: def test_check_equality_and_cmp_basic(self, testdir): modcol = testdir.getmodulecol(""" From hpk at codespeak.net Sat Feb 21 18:44:29 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 21 Feb 2009 18:44:29 +0100 (CET) Subject: [py-svn] r62081 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090221174429.5BE24168487@codespeak.net> Author: hpk Date: Sat Feb 21 18:44:25 2009 New Revision: 62081 Modified: py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/testing/test_pycollect.py Log: add addfinalizer method and check for proper exception Modified: py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py Sat Feb 21 18:44:25 2009 @@ -14,7 +14,7 @@ to test functions and methods. """ def pytest_pyfunc_arg(self, pyfuncitem, argname): - print "tmpdir: receiving call", pyfuncitem, argname + #print "tmpdir: receiving call", pyfuncitem, argname if argname == "tmpdir": basename = "_".join(pyfuncitem.listnames()) # move to config Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Sat Feb 21 18:44:25 2009 @@ -316,11 +316,21 @@ """ def __init__(self, name, parent=None, config=None, args=(), callobj=_dummy): super(Function, self).__init__(name, parent, config=config) - self._argfinalizers = [] + self._finalizers = [] self._args = args if callobj is not _dummy: self._obj = callobj + def addfinalizer(self, func): + self._finalizers.append(func) + + def teardown(self): + finalizers = self._finalizers + while finalizers: + call = finalizers.pop() + call() + super(Function, self).teardown() + def readkeywords(self): d = super(Function, self).readkeywords() d.update(self.obj.func_dict) @@ -336,13 +346,6 @@ return self.obj(*self._args, **kw) - def teardown(self): - finalizers = self._argfinalizers - while finalizers: - call = finalizers.pop() - call() - super(Function, self).teardown() - def lookup_allargs(self): kwargs = {} if not self._args: @@ -361,12 +364,7 @@ if value is not None: return value else: - print "pytestplugins is", self._config.pytestplugins - print "could not find argument %r, plugins=%r" %( - argname,self._config.pytestplugins._plugins) - for x in call.methods: - print x - raise TypeError("could not provide funcargument %r" %(argname,)) + raise LookupError("could not provide funcargument %r" %(argname,)) def getkwargs(self): kwargs = {} Modified: py/branch/pytestplugin/py/test/testing/test_pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pycollect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pycollect.py Sat Feb 21 18:44:25 2009 @@ -217,6 +217,10 @@ assert f1 == f1_b assert not f1 != f1_b + def test_pyfuncarg_lookupfails(self, testdir): + item = testdir.getitem("def test_func(some, other): pass") + kw = py.test.raises(LookupError, "item.lookup_allargs()") + def test_pyfuncarg_basic(self, testdir): item = testdir.getitem("def test_func(some, other): pass") class Provider: @@ -230,6 +234,22 @@ assert kw['some'] == "test_func" assert kw['other'] == 42 + def test_pyfuncarg_finalize(self, testdir): + item = testdir.getitem("def test_func(some): pass") + l = [] + class Provider: + def pytest_pyfuncarg_some(self, pyfuncitem): + pyfuncitem.addfinalizer(lambda: l.append(42)) + return 3 + item._config.pytestplugins.register(Provider()) + kw = item.lookup_allargs() + assert len(kw) == 1 + assert kw['some'] == 3 + assert len(l) == 0 + item.teardown() + assert len(l) == 1 + assert l[0] == 42 + class TestSorting: def test_check_equality_and_cmp_basic(self, testdir): modcol = testdir.getmodulecol(""" From hpk at codespeak.net Sat Feb 21 21:59:59 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 21 Feb 2009 21:59:59 +0100 (CET) Subject: [py-svn] r62085 - in py/branch/pytestplugin/py: . misc/testing test test/dsession test/dsession/testing test/plugin test/testing Message-ID: <20090221205959.23CBF1684BC@codespeak.net> Author: hpk Date: Sat Feb 21 21:59:57 2009 New Revision: 62085 Modified: py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/misc/testing/test_com.py py/branch/pytestplugin/py/test/collect.py py/branch/pytestplugin/py/test/dsession/dsession.py py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py py/branch/pytestplugin/py/test/plugin/pytest_unittest.py py/branch/pytestplugin/py/test/plugin/pytest_xfail.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/testing/test_collect_pickle.py py/branch/pytestplugin/py/test/testing/test_pycollect.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py py/branch/pytestplugin/py/test/testing/test_runner_functional.py Log: * use the new pytest_pyfuncarg protocol everywhere. * have python test modules automatically participate as plugins * small refinements and additions to test suite Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Sat Feb 21 21:59:57 2009 @@ -41,8 +41,12 @@ """ Manage Plugins: Load plugins and manage calls to plugins. """ - def __init__(self): - self._plugins = [] + MultiCall = MultiCall + + def __init__(self, plugins=None): + if plugins is None: + plugins = [] + self._plugins = plugins self._callbacks = [] def import_module(self, modspec): @@ -76,6 +80,9 @@ self._plugins.remove(plugin) self.notify("plugin_unregistered", plugin) + def getplugins(self): + return list(self._plugins) + def isregistered(self, plugin): return plugin in self._plugins Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Sat Feb 21 21:59:57 2009 @@ -39,15 +39,26 @@ assert call.execute(firstresult=True) == 23 class TestPyPlugins: + def test_MultiCall(self): + plugins = PyPlugins() + assert hasattr(plugins, "MultiCall") + def test_register(self): plugins = PyPlugins() class MyPlugin: pass my = MyPlugin() plugins.register(my) + assert plugins.getplugins() == [my] + my2 = MyPlugin() + plugins.register(my2) + assert plugins.getplugins() == [my, my2] + assert plugins.isregistered(my) + assert plugins.isregistered(my2) plugins.unregister(my) assert not plugins.isregistered(my) + assert plugins.getplugins() == [my2] #@py.test.keywords(xfail=True) def test_onregister(self): Modified: py/branch/pytestplugin/py/test/collect.py ============================================================================== --- py/branch/pytestplugin/py/test/collect.py (original) +++ py/branch/pytestplugin/py/test/collect.py Sat Feb 21 21:59:57 2009 @@ -172,15 +172,17 @@ setattr(self, attrname, res) return res - def listchain(self): - """ return list of all parent collectors up to self. """ + def listchain(self, rootfirst=False): + """ return list of all parent collectors up to self, + starting form root of collection tree. """ l = [self] while 1: x = l[-1] if x.parent is not None: l.append(x.parent) else: - l.reverse() + if not rootfirst: + l.reverse() return l def listnames(self): Modified: py/branch/pytestplugin/py/test/dsession/dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/dsession.py Sat Feb 21 21:59:57 2009 @@ -103,7 +103,6 @@ continue loopstate.dowork = True - print "eventcall", eventcall eventname, args, kwargs = eventcall self.bus.notify(eventname, *args, **kwargs) ev, = args Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py Sat Feb 21 21:59:57 2009 @@ -277,7 +277,7 @@ evrec = EventRecorder(session.bus) session.queueevent("itemtestreport", run(item)) session.loop_once(loopstate) - assert not evrec.events + assert not evrec.getfirstnamed("hostdown") ev = event.HostDown(host) session.queueevent("hostdown", ev) session.loop_once(loopstate) Modified: py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py Sat Feb 21 21:59:57 2009 @@ -117,14 +117,15 @@ res = channel.receive() assert res == host.gw_remotepath +def pytest_pyfuncarg_source(pyfuncitem): + return py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir("source") +def pytest_pyfuncarg_dest(pyfuncitem): + return py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir("dest") + class TestSyncing: def _gethostinfo(self, dest): hostinfo = Host("localhost:%s" % dest) return hostinfo - - def pytest_pyfunc_arg(self, pyfuncitem, argname): - if argname in ("source", "dest"): - return py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir(argname), None def test_hrsync_filter(self, source, dest): source.ensure("dir", "file.txt") @@ -184,11 +185,6 @@ assert not res2 class TestHostManager: - def pytest_pyfunc_arg(self, pyfuncitem, argname): - if argname in ("source", "dest"): - p = py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir(argname) - return p, None - def gethostmanager(self, source, dist_hosts, dist_rsync_roots=None): l = ["dist_hosts = %r" % dist_hosts] if dist_rsync_roots: Modified: py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py Sat Feb 21 21:59:57 2009 @@ -55,11 +55,12 @@ print "exiting:", self.host.gw self.host.gw.exit() +def pytest_pyfuncarg_mysetup(pyfuncitem): + mysetup = MySetup(pyfuncitem) + pyfuncitem.addfinalizer(mysetup.finalize) + return mysetup + class TestMasterSlaveConnection: - def pytest_pyfunc_arg(self, pyfuncitem, argname): - if argname == "mysetup": - x = MySetup(pyfuncitem) - return x, x.finalize def test_crash_invalid_item(self, mysetup): node = mysetup.makenode() Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Sat Feb 21 21:59:57 2009 @@ -4,12 +4,10 @@ import py class Plugintester: - def pytest_pyfunc_arg(self, pyfuncitem, argname): - if argname == "plugintester": - pt = PluginTester(pyfuncitem) - else: - return None - return pt, pt.finalize + def pytest_pyfuncarg_plugintester(self, pyfuncitem): + pt = PluginTester(pyfuncitem) + pyfuncitem.addfinalizer(pt.finalize) + return pt class Support(object): def __init__(self, pyfuncitem): @@ -38,13 +36,19 @@ print "loading and checking", impclass fail = False pm = py.test._PytestPlugins() - plugin = pm.import_plugin(impclass) + plugin = impclass() methods = collectattr(plugin.__class__) hooks = collectattr(PytestPluginHooks) getargs = py.std.inspect.getargs + + def isgenerichook(name): + return name.startswith("pyevent_") or name.startswith("pytest_pyfuncarg") + while methods: name, method = methods.popitem() - if name not in hooks: + if isgenerichook(name): + continue # XXX also do some checks + if name not in hooks and not isgenerichook(name): print "found unknown hook: %s" % name fail = True else: @@ -99,12 +103,12 @@ def pytest_event(self, event): """ called for each internal py.test event. """ - def pytest_pyfunc_arg(self, pyfuncitem, argname): - """ provide (value, finalizer) for an open test function argument. - - the finalizer (if not None) will be called after the test - function has been executed (i.e. pyfuncitem.execute() returns). - """ + #def pytest_pyfuncarg_NAME(self, pyfuncitem, argname): + # """ provide (value, finalizer) for an open test function argument. + # + # the finalizer (if not None) will be called after the test + # function has been executed (i.e. pyfuncitem.execute() returns). + # """ def pytest_pyfunc_call(self, pyfuncitem, args, kwargs): """ return True if we consumed/did the call to the python function item. """ Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Sat Feb 21 21:59:57 2009 @@ -67,17 +67,13 @@ return items, rec def runitem(self, source, **runnerargs): - # used from runner functional + # used from runner functional tests item = self.getitem(source) - # XXX - runner = self.pyfuncitem.obj.im_self.getrunner() + # the test class where we are called from wants to provide the runner + testclassinstance = self.pyfuncitem.obj.im_self + runner = testclassinstance.getrunner() return runner(item, **runnerargs) - #evrec = self.inline_runsource(source, *cmdlineargs) - #flist = evrec.getnamed("itemtestreport") - #assert len(flist) == 1 - #return flist[0] - def inline_runsource(self, source, *cmdlineargs): p = self.makepyfile(source) l = list(cmdlineargs) + [p] @@ -369,13 +365,14 @@ class Pytester: - def pytest_pyfunc_arg(self, pyfuncitem, argname): - if argname == "linecomp": - return LineComp(), None - elif argname == "LineMatcher": - return LineMatcher, None - elif argname == "testdir": - testdir = TmpTestdir(pyfuncitem) - return testdir, None - elif argname == "EventRecorder": - return EventRecorder, None # or do some finalization + def pytest_pyfuncarg_linecomp(self, pyfuncitem): + return LineComp() + + def pytest_pyfuncarg_LineMatcher(self, pyfuncitem): + return LineMatcher + + def pytest_pyfuncarg_testdir(self, pyfuncitem): + return TmpTestdir(pyfuncitem) + + def pytest_pyfuncarg_EventRecorder(self, pyfuncitem): + return EventRecorder Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Sat Feb 21 21:59:57 2009 @@ -95,7 +95,12 @@ self.write_line("HostGatewayReady: %s" %(ev.host,)) def pyevent_plugin_registered(self, plugin): - self.write_line("PLUGIN registered: %s" %(plugin,)) + if self.config.option.traceconfig: + msg = "PLUGIN registered: %s" %(plugin,) + # XXX this event may happen during setup/teardown time + # which unfortunately captures our output here + # which garbles our output if we use self.write_line + self.write_line(msg) def pyevent_hostup(self, ev): d = ev.platinfo.copy() Modified: py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py Sat Feb 21 21:59:57 2009 @@ -13,12 +13,10 @@ """ pytest plugin for providing temporary directories to test functions and methods. """ - def pytest_pyfunc_arg(self, pyfuncitem, argname): - #print "tmpdir: receiving call", pyfuncitem, argname - if argname == "tmpdir": - basename = "_".join(pyfuncitem.listnames()) - # move to config - return py.test.ensuretemp(basename), None + def pytest_pyfuncarg_tmpdir(self, pyfuncitem): + basename = "_".join(pyfuncitem.listnames()) + # XXX make creation dependent on config + return py.test.ensuretemp(basename) # =============================================================================== # @@ -29,13 +27,9 @@ def test_generic(plugintester): plugintester.apicheck(Tmpdir) -def test_pypyfunc_arg(): - class Pseudoitem: - def listnames(self): - return ['a', 'b'] - t = Tmpdir() - p, finalizer = t.pytest_pyfunc_arg(Pseudoitem(), "tmpdir") - assert finalizer is None +def test_pyfuncarg(testdir): + item = testdir.getitem("def test_func(tmpdir): pass") + tmpdir = Tmpdir() + p = tmpdir.pytest_pyfuncarg_tmpdir(item) assert p.check() - assert p.basename == "a_b" - assert t.pytest_pyfunc_arg(Pseudoitem(), "something") is None + assert p.basename.endswith("test_func") Modified: py/branch/pytestplugin/py/test/plugin/pytest_unittest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_unittest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_unittest.py Sat Feb 21 21:59:57 2009 @@ -74,9 +74,9 @@ plugintester.apicheck(Unittest) def test_simple_unittest(testdir): - testpath = testdir.makepyfile(test_simple_unittest=""" + testpath = testdir.makepyfile(""" import unittest - pytest_plugins = "pytest_unittest" # XXX + pytest_plugins = "pytest_unittest" class MyTestCase(unittest.TestCase): def testpassing(self): self.assertEquals('foo', 'foo') Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Sat Feb 21 21:59:57 2009 @@ -43,8 +43,7 @@ # =============================================================================== def test_generic(plugintester): - impname = "pytest_xfail" - plugintester.apicheck(impname) + plugintester.apicheck(Xfail) def test_xfail(plugintester, linecomp): testdir = plugintester.testdir() Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Sat Feb 21 21:59:57 2009 @@ -176,8 +176,10 @@ if not self._config.option.nomagic: #print "*" * 20, "INVOKE assertion", self py.magic.invoke(assertion=1) - if hasattr(self.obj, 'setup_module'): - self.obj.setup_module(self.obj) + mod = self.obj + self._config.pytestplugins.register(mod) + if hasattr(mod, 'setup_module'): + self.obj.setup_module(mod) def teardown(self): if hasattr(self.obj, 'teardown_module'): @@ -185,6 +187,7 @@ if not self._config.option.nomagic: #print "*" * 20, "revoke assertion", self py.magic.revoke(assertion=1) + self._config.pytestplugins.unregister(self.obj) class Class(PyCollectorMixin, py.test.collect.Collector): @@ -339,7 +342,7 @@ def runtest(self): """ execute the given test function. """ if not self._deprecated_testexecution(): - kw = self.getkwargs() + kw = self.lookup_allargs() pytest_pyfunc_call = self._config.pytestplugins.getfirst("pytest_pyfunc_call") if pytest_pyfunc_call is not None: if pytest_pyfunc_call(pyfuncitem=self, args=self._args, kwargs=kw): @@ -364,38 +367,9 @@ if value is not None: return value else: - raise LookupError("could not provide funcargument %r" %(argname,)) - - def getkwargs(self): - kwargs = {} - if not self._args: - # XXX care for yielded/generated tests as well?! - funcobj = self.obj - startindex = getattr(funcobj, 'im_self', None) and 1 or 0 - for argname in py.std.inspect.getargs(self.obj.func_code)[0][startindex:]: - res = self.fillarg(argname, kwargs) - return kwargs - - def fillarg(self, argname, kwargs): - value = argfinalizer = None - plugins = self._config.pytestplugins.pyplugins - multicall = py._com.MultiCall( - plugins.listattr("pytest_pyfunc_arg", extra=[self.parent.obj]), - pyfuncitem=self, argname=argname - ) - res = multicall.execute(firstresult=True) - if res: - value, argfinalizer = res - kwargs[argname] = value - if argfinalizer is not None: - self._argfinalizers.append(argfinalizer) - else: - print "pytestplugins is", self._config.pytestplugins - print "could not find argument %r, plugins=%r" %( - argname,self._config.pytestplugins._plugins) - for x in multicall.methods: - print x - raise TypeError("could not provide funcargument %r" %(argname,)) + metainfo = self.repr_metainfo() + #self._config.bus.notify("pyfuncarg_lookuperror", argname) + raise LookupError("funcargument %r not found for: %s" %(argname,metainfo.verboseline())) def __eq__(self, other): try: Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Sat Feb 21 21:59:57 2009 @@ -10,6 +10,7 @@ if pyplugins is None: pyplugins = py._com.PyPlugins() self.pyplugins = pyplugins + self.MultiCall = self.pyplugins.MultiCall self._plugins = {} def register(self, plugin): @@ -19,6 +20,9 @@ def isregistered(self, plugin): return self.pyplugins.isregistered(plugin) + def getplugins(self): + return self.pyplugins.getplugins() + # API for bootstrapping # def getplugin(self, pname): @@ -30,26 +34,20 @@ def consider_module(self, mod): attr = getattr(mod, "pytest_plugins", ()) - if not isinstance(attr, (list, tuple)): - attr = (attr,) - for spec in attr: - if spec: + if attr: + if not isinstance(attr, (list, tuple)): + attr = (attr,) + for spec in attr: self.import_plugin(spec) def import_plugin(self, spec): - if isinstance(spec, str): - modname, clsname = canonical_names(spec) - if clsname.lower() not in self._plugins: - mod = importplugin(modname) - plugin = registerplugin(self.pyplugins.register, mod, clsname) - self._plugins[clsname.lower()] = plugin - self.consider_module(mod) - else: - clsname = spec.__name__ - plugin = spec() - self.pyplugins.register(plugin) - #print "registered", plugin, "at", self - + assert isinstance(spec, str) + modname, clsname = canonical_names(spec) + if clsname.lower() not in self._plugins: + mod = importplugin(modname) + plugin = registerplugin(self.pyplugins.register, mod, clsname) + self._plugins[clsname.lower()] = plugin + self.consider_module(mod) # # # API for interacting with registered and instantiated plugin objects Modified: py/branch/pytestplugin/py/test/testing/test_collect_pickle.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect_pickle.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect_pickle.py Sat Feb 21 21:59:57 2009 @@ -1,5 +1,7 @@ import py +def pytest_pyfuncarg_pickletransport(pyfuncitem): + return PickleTransport() class PickleTransport: def __init__(self): @@ -19,10 +21,6 @@ return p2config class TestPickling: - def pytest_pyfunc_arg(self, pyfuncitem, argname): - if argname == "pickletransport": - pt = PickleTransport() - return pt, None def test_pickle_config(self, pickletransport): config1 = py.test.config._reparse([]) Modified: py/branch/pytestplugin/py/test/testing/test_pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pycollect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pycollect.py Sat Feb 21 21:59:57 2009 @@ -47,6 +47,17 @@ assert not modcol.collect() assert not modcol.run() + def test_module_participates_as_plugin(self, testdir): + modcol = testdir.getmodulecol("") + modcol.setup() + assert modcol._config.pytestplugins.isregistered(modcol.obj) + modcol.teardown() + assert not modcol._config.pytestplugins.isregistered(modcol.obj) + + def test_module_considers_pytestplugins_at_import(self, testdir): + modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',") + py.test.raises(ImportError, "modcol.obj") + class TestClass: def test_disabled_class(self, testdir): modcol = testdir.getmodulecol(""" @@ -234,7 +245,7 @@ assert kw['some'] == "test_func" assert kw['other'] == 42 - def test_pyfuncarg_finalize(self, testdir): + def test_pyfuncarg_addfinalizer(self, testdir): item = testdir.getitem("def test_func(some): pass") l = [] class Provider: @@ -250,6 +261,27 @@ assert len(l) == 1 assert l[0] == 42 + def test_pyfuncarg_lookup_modulelevel(self, testdir): + modcol = testdir.getmodulecol(""" + def pytest_pyfuncarg_something(pyfuncitem): + return pyfuncitem.name + + class TestClass: + def test_method(self, something): + pass + def test_func(something): + pass + """) + item1, item2 = testdir.genitems([modcol]) + modcol.setup() + assert modcol._config.pytestplugins.isregistered(modcol.obj) + kwargs = item1.lookup_allargs() + assert kwargs['something'] == "test_method" + kwargs = item2.lookup_allargs() + assert kwargs['something'] == "test_func" + modcol.teardown() + assert not modcol._config.pytestplugins.isregistered(modcol.obj) + class TestSorting: def test_check_equality_and_cmp_basic(self, testdir): modcol = testdir.getmodulecol(""" Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Sat Feb 21 21:59:57 2009 @@ -56,6 +56,10 @@ assert len(config.opts) == 1 assert config.opts[0][1] == 42 + def test_MultiCall(self): + pp = PytestPlugins() + assert hasattr(pp, 'MultiCall') + def test_registry(self): pp = PytestPlugins() a1, a2 = object(), object() @@ -63,12 +67,13 @@ assert pp.isregistered(a1) pp.register(a2) assert pp.isregistered(a2) + assert pp.getplugins() == [a1, a2] pp.unregister(a1) assert not pp.isregistered(a1) pp.unregister(a2) assert not pp.isregistered(a2) - def test_consider_module(self, testdir, EventRecorder): + def test_consider_module_import_module(self, testdir, EventRecorder): mod = py.std.new.module("x") mod.pytest_plugins = "pytest_a" aplugin = testdir.makepyfile(pytest_a="""class A: pass""") @@ -80,6 +85,8 @@ evlist = evrec.getnamed("plugin_registered") assert len(evlist) == 1 assert evlist[0].__class__.__name__ == "A" + + # check that it is not registered twice plugins.consider_module(mod) evlist = evrec.getnamed("plugin_registered") assert len(evlist) == 1 Modified: py/branch/pytestplugin/py/test/testing/test_runner_functional.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_runner_functional.py (original) +++ py/branch/pytestplugin/py/test/testing/test_runner_functional.py Sat Feb 21 21:59:57 2009 @@ -4,64 +4,6 @@ from py.__.code.excinfo import ReprExceptionInfo class BaseTests: - def test_getwkargs_function(self, testdir): - evrec = testdir.inline_runsource(""" - import py - - provided = [] - class MyPlugin: - def pytest_pyfunc_arg(self, pyfuncitem, argname): - if argname == "myxyz": - res = 42 - provided.append(res) - return res, provided.pop - if argname == "item": - return pyfuncitem, None - - pytest_plugins = MyPlugin, - - def test_func(myxyz, item): - assert myxyz == 42 - assert item._argfinalizers - assert len(provided) == 1 - """) - ev, = evrec.getnamed("itemtestreport") - assert ev.passed, ev.longrepr - - def test_getwkargs_boundmethod(self, testdir): - evrec = testdir.inline_runsource(""" - import py - - provided = [] - class MyPlugin: - def pytest_pyfunc_arg(self, pyfuncitem, argname): - assert argname != 'self' - if argname == "hello": - return "world", None - - pytest_plugins = MyPlugin, - - class TestForMethod: - def test_func(self, hello): - assert hello == "world" - """) - ev, = evrec.getnamed("itemtestreport") - assert ev.passed, ev.longrepr - - def test_getwkargs_boundmethod_send_within_class(self, testdir): - evrec = testdir.inline_runsource(""" - class TestClass: - def pytest_pyfunc_arg(self, pyfuncitem, argname): - assert argname != 'self' - if argname == "hello": - return "world", None - - def test_func(self, hello): - assert hello == "world" - """) - ev, = evrec.getnamed("itemtestreport") - assert ev.passed, ev.longrepr - def test_funcattr(self, testdir): ev = testdir.runitem(""" import py From hpk at codespeak.net Sun Feb 22 21:18:04 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 22 Feb 2009 21:18:04 +0100 (CET) Subject: [py-svn] r62088 - in py/branch/pytestplugin/py/test: . dsession dsession/testing testing Message-ID: <20090222201804.EF05216845A@codespeak.net> Author: hpk Date: Sun Feb 22 21:18:02 2009 New Revision: 62088 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/defaultconftest.py py/branch/pytestplugin/py/test/dsession/dsession.py py/branch/pytestplugin/py/test/dsession/masterslave.py py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py py/branch/pytestplugin/py/test/testing/test_config.py Log: getting rid of "tracedir" option that kind of doubles what one can get with event recording Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Sun Feb 22 21:18:02 2009 @@ -225,40 +225,6 @@ raise ValueError("unknown io capturing: " + iocapture) - def gettracedir(self): - """ return a tracedirectory or None, depending on --tracedir. """ - if self.option.tracedir is not None: - return py.path.local(self.option.tracedir) - - def maketrace(self, name, flush=True): - """ return a tracedirectory or None, depending on --tracedir. """ - tracedir = self.gettracedir() - if tracedir is None: - return NullTracer() - tracedir.ensure(dir=1) - return Tracer(tracedir.join(name), flush=flush) - -class Tracer(object): - file = None - def __init__(self, path, flush=True): - self.file = path.open(mode='w') - self.flush = flush - - def __call__(self, *args): - time = round(py.std.time.time(), 3) - print >>self.file, time, " ".join(map(str, args)) - if self.flush: - self.file.flush() - - def close(self): - self.file.close() - -class NullTracer: - def __call__(self, *args): - pass - def close(self): - pass - # this is the one per-process instance of py.test configuration config_per_process = Config( pytestplugins=py.test._PytestPlugins(py._com.pyplugins) Modified: py/branch/pytestplugin/py/test/defaultconftest.py ============================================================================== --- py/branch/pytestplugin/py/test/defaultconftest.py (original) +++ py/branch/pytestplugin/py/test/defaultconftest.py Sun Feb 22 21:18:02 2009 @@ -54,9 +54,6 @@ Option('', '--pdb', action="store_true", dest="usepdb", default=False, help="start pdb (the Python debugger) on errors."), - Option('', '--tracedir', - action="store", dest="tracedir", default=None, - help="write tracing information to the given directory."), Option('', '--tb', action="store", dest="tbstyle", default='long', type="choice", choices=['long', 'short', 'no'], Modified: py/branch/pytestplugin/py/test/dsession/dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/dsession.py Sun Feb 22 21:18:02 2009 @@ -41,7 +41,6 @@ self.host2pending = {} self.item2host = {} self._testsfailed = False - self.trace = config.maketrace("dsession.log") def fixoptions(self): """ check, fix and determine conflicting options. """ @@ -178,7 +177,6 @@ pending = self.host2pending.pop(host) for item in pending: del self.item2host[item] - self.trace("removehost %s, pending=%r" %(host.hostid, pending)) return pending def triggertesting(self, colitems): @@ -203,7 +201,6 @@ if room > 0: sending = tosend[:room] host.node.sendlist(sending) - self.trace("sent to host %s: %r" %(host.hostid, sending)) for item in sending: #assert item not in self.item2host, ( # "sending same item %r to multiple hosts " @@ -223,7 +220,7 @@ raise AssertionError(item, self.item2host) host = self.item2host.pop(item) self.host2pending[host].remove(item) - self.trace("removed %r, host=%r" %(item,host.hostid)) + #self.config.bus.notify("removeitem", item, host.hostid) def handle_crashitem(self, item, host): longrepr = "!!! Host %r crashed during running of test %r" %(host, item) Modified: py/branch/pytestplugin/py/test/dsession/masterslave.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/masterslave.py (original) +++ py/branch/pytestplugin/py/test/dsession/masterslave.py Sun Feb 22 21:18:02 2009 @@ -117,15 +117,12 @@ channel = self.channel self.host = host = channel.receive() self.sendevent("hostup", makehostup(host)) - self.trace = self.config.maketrace(host.hostid) - self.trace("initialized") try: while 1: task = channel.receive() - self.trace("received", task) + self.config.bus.notify("masterslave_receivedtask", task) if task is None: # shutdown self.channel.send(None) - self.trace("shutting down, send None to", channel) break if isinstance(task, list): for item in task: @@ -136,13 +133,9 @@ raise except: self.sendevent("internalerror", event.InternalException()) - self.trace("sending back internal exception report, breaking loop") raise - else: - self.trace("normal shutdown") def runtest(self, item): runner = item._getrunner() testrep = runner(item) self.sendevent("itemtestreport", testrep) - self.trace("notified testreport", testrep) Modified: py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py Sun Feb 22 21:18:02 2009 @@ -287,6 +287,7 @@ for host in hm.hosts: eventcall = queue.get(timeout=2.0) name, args, kwargs = eventcall + print name, args, kwargs assert name == "hostdown" def XXXtest_ssh_rsync_samehost_twice(self): Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Sun Feb 22 21:18:02 2009 @@ -176,28 +176,6 @@ assert option.gdest == 11 class TestSessionAndOptions: - def test_tracedir_tracer(self, testdir): - tracedir = testdir.mkdir("tracedir") - config = testdir.parseconfig("--tracedir=%s" % tracedir) - assert config.gettracedir() == tracedir - - trace = config.maketrace("trace1.log") # flush=True by default - trace("hello", "world") - class A: pass - trace(A()) - p = tracedir.join("trace1.log") - lines = p.readlines(cr=0) - assert lines[0].endswith("hello world") - assert lines[1].find("A") != -1 - trace.close() - - def test_trace_null(self, testdir): - config = testdir.parseconfig(testdir.tmpdir) - assert config.gettracedir() is None - trace = config.maketrace("hello", flush=True) - trace("hello", "world") - trace.close() - def test_implied_dsession(self, testdir): for x in 'startserver runbrowser rest'.split(): config = testdir.parseconfig(testdir.tmpdir, '--dist', '--%s' % x) From hpk at codespeak.net Sun Feb 22 22:25:40 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 22 Feb 2009 22:25:40 +0100 (CET) Subject: [py-svn] r62089 - in py/branch/pytestplugin/py/test: . plugin Message-ID: <20090222212540.B9441168402@codespeak.net> Author: hpk Date: Sun Feb 22 22:25:38 2009 New Revision: 62089 Modified: py/branch/pytestplugin/py/test/defaultconftest.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Log: eventlog and resultlog are default plugins but are not enabled by default. use e.g. a "pytest_plugins=resultlog" definition to enable them. Modified: py/branch/pytestplugin/py/test/defaultconftest.py ============================================================================== --- py/branch/pytestplugin/py/test/defaultconftest.py (original) +++ py/branch/pytestplugin/py/test/defaultconftest.py Sun Feb 22 22:25:38 2009 @@ -10,7 +10,7 @@ conf_iocapture = "fd" # overridable from conftest.py -pytest_plugins = "terminal resultlog eventlog".split() +pytest_plugins = "terminal xfail".split() # =================================================== # Distributed testing specific options Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Sun Feb 22 22:25:38 2009 @@ -29,7 +29,11 @@ from pytest_pytester import TmpTestdir crunner = TmpTestdir(self.pyfuncitem) # - #crunner.ensureplugin(impname) + for colitem in self.pyfuncitem.listchain(): + if isinstance(colitem, py.test.collect.Module) and \ + colitem.name.startswith("pytest_"): + crunner.ensureplugin(colitem.fspath.purebasename) + break return crunner def apicheck(self, impclass): Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Sun Feb 22 22:25:38 2009 @@ -131,7 +131,8 @@ pstring = repr(self._plugins) p.write("import py ; pytest_plugins = %s" % pstring) else: - assert not self._plugins + if self._plugins: + print "warning, reusing existing", p def popen(self, cmdargs, stdout, stderr, **kw): if not hasattr(py.std, 'subprocess'): Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Sun Feb 22 22:25:38 2009 @@ -1,7 +1,8 @@ import py class Resultlog: - """resultlog plugin for machine-readable logging of test results. + """resultlog plugin for machine-readable logging of test results. + Useful for buildbot integration code. """ pytest_cmdlineoptions = [ py.test.config.Option('--resultlog', action="store", dest="resultlog", default=None, @@ -44,7 +45,6 @@ return ''.join(gpath) class ResultLog(object): - def __init__(self, logfile): self.logfile = logfile # preferably line buffered From py-svn at codespeak.net Sun Feb 22 22:54:00 2009 From: py-svn at codespeak.net (Best Price) Date: Sun, 22 Feb 2009 22:54:00 +0100 (CET) Subject: [py-svn] Dear py-svn@codespeak.net Sun, 22 Feb 2009 10:52:18 +0100 83% 0FF Message-ID: <20090222115218.3171.qmail@dmx1.bfi0.com.com> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Sun Feb 22 23:06:33 2009 From: py-svn at codespeak.net (jamalnpgbxt) Date: Sun, 22 Feb 2009 23:06:33 +0100 (CET) Subject: [py-svn] So big my pants don't fit Message-ID: <20090222220633.003A2168449@codespeak.net> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Sun Feb 22 23:20:55 2009 From: py-svn at codespeak.net (Arssixbuex) Date: Sun, 22 Feb 2009 23:20:55 +0100 (CET) Subject: [py-svn] She moaned in pain and pleasure Message-ID: <20090222222055.35B14168053@codespeak.net> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Mon Feb 23 03:10:03 2009 From: py-svn at codespeak.net (manriqueotmywr) Date: Mon, 23 Feb 2009 03:10:03 +0100 (CET) Subject: [py-svn] Intensify her climaxes Message-ID: <20090223021003.4169116843D@codespeak.net> An HTML attachment was scrubbed... URL: From hpk at codespeak.net Mon Feb 23 16:23:43 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 23 Feb 2009 16:23:43 +0100 (CET) Subject: [py-svn] r62099 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090223152343.4418E16847A@codespeak.net> Author: hpk Date: Mon Feb 23 16:23:42 2009 New Revision: 62099 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_pycollect.py Log: regrouping config related tests in a more logical/concept based way Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Mon Feb 23 16:23:42 2009 @@ -100,6 +100,9 @@ self.option = cmdlineopts self._conftest.setinitial(self.args) + def getcolitems(self): + return [self.getfsnode(arg) for arg in self.args] + def getfsnode(self, path): path = py.path.local(path) assert path.check(), "%s: path does not exist" %(path,) Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Mon Feb 23 16:23:42 2009 @@ -49,6 +49,9 @@ def mkdir(self, name): return self.tmpdir.mkdir(name) + + def chdir(self): + return self.tmpdir.chdir() def ensureplugin(self, impname): assert isinstance(impname, str) @@ -97,6 +100,8 @@ return sorter def parseconfig(self, *args): + if not args: + args = (self.tmpdir,) return py.test.config._reparse(list(args)) def getitem(self, source, funcname="test_func"): Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Mon Feb 23 16:23:42 2009 @@ -1,287 +1,47 @@ -from __future__ import generators import py -from py.__.test.config import gettopdir - -def getcolitems(config): - return [config.getfsnode(arg) for arg in config.args] - -def test_tmpdir(): - d1 = py.test.ensuretemp('hello') - d2 = py.test.ensuretemp('hello') - assert d1 == d2 - assert d1.check(dir=1) - -def test_config_cmdline_options(): - o = py.test.ensuretemp('configoptions') - o.ensure("conftest.py").write(py.code.Source(""" - import py - def _callback(option, opt_str, value, parser, *args, **kwargs): - option.tdest = True - Option = py.test.config.Option - option = py.test.config.addoptions("testing group", - Option('-G', '--glong', action="store", default=42, - type="int", dest="gdest", help="g value."), - # XXX note: special case, option without a destination - Option('-T', '--tlong', action="callback", callback=_callback, - help='t value'), - ) - """)) - old = o.chdir() - try: - config = py.test.config._reparse(['-G', '17']) - finally: - old.chdir() - assert config.option.gdest == 17 - -def test_config_cmdline_options_only_lowercase(): - o = py.test.ensuretemp('test_config_cmdline_options_only_lowercase') - o = o.mkdir("onemore") # neccessary because collection of o.dirpath() - # could see our conftest.py - o.ensure("conftest.py").write(py.code.Source(""" - import py - Option = py.test.config.Option - options = py.test.config.addoptions("testing group", - Option('-g', '--glong', action="store", default=42, - type="int", dest="gdest", help="g value."), - ) - """)) - old = o.chdir() - try: - py.test.raises(ValueError, """ - py.test.config._reparse(['-g', '17']) - """) - finally: - old.chdir() - -def test_parsing_again_fails(): - dir = py.test.ensuretemp("parsing_again_fails") - config = py.test.config._reparse([str(dir)]) - py.test.raises(AssertionError, "config.parse([])") - -def test_config_getvalue_honours_conftest(): - o = py.test.ensuretemp('testconfigget') - o.ensure("conftest.py").write("x=1") - o.ensure("sub", "conftest.py").write("x=2 ; y = 3") - config = py.test.config._reparse([str(o)]) - assert config.getvalue("x") == 1 - assert config.getvalue("x", o.join('sub')) == 2 - py.test.raises(KeyError, "config.getvalue('y')") - config = py.test.config._reparse([str(o.join('sub'))]) - assert config.getvalue("x") == 2 - assert config.getvalue("y") == 3 - assert config.getvalue("x", o) == 1 - py.test.raises(KeyError, 'config.getvalue("y", o)') - - -def test_config_overwrite(): - o = py.test.ensuretemp('testconfigget') - o.ensure("conftest.py").write("x=1") - config = py.test.config._reparse([str(o)]) - assert config.getvalue('x') == 1 - config.option.x = 2 - assert config.getvalue('x') == 2 - config = py.test.config._reparse([str(o)]) - assert config.getvalue('x') == 1 - -def test_gettopdir(): - tmp = py.test.ensuretemp("topdir") - assert gettopdir([tmp]) == tmp - topdir =gettopdir([tmp.join("hello"), tmp.join("world")]) - assert topdir == tmp - somefile = tmp.ensure("somefile.py") - assert gettopdir([somefile]) == tmp - -def test_gettopdir_pypkg(): - tmp = py.test.ensuretemp("topdir2") - a = tmp.ensure('a', dir=1) - b = tmp.ensure('a', 'b', '__init__.py') - c = tmp.ensure('a', 'b', 'c.py') - Z = tmp.ensure('Z', dir=1) - assert gettopdir([c]) == a - assert gettopdir([c, Z]) == tmp - - at py.test.keywords(xfail="issue1 config's pytestplugins/bus initialization") -def test_config_initafterpickle_plugin(testdir): - testdir.makepyfile(__init__="", conftest="x=1; y=2") - hello = testdir.makepyfile(hello="") - tmp = testdir.tmpdir - config = py.test.config._reparse([hello]) - config2 = py.test.config._reparse([tmp.dirpath()]) - config2._initialized = False # we have to do that from tests - config2._repr = config._makerepr() - config2._initafterpickle(topdir=tmp.dirpath()) - # we check that config "remote" config objects - # have correct plugin initialization - #XXX assert config2.pytestplugins.pm._plugins - #XXX assert config2.bus.isregistered(config2.pytestplugins.forward_event) - assert config2.bus == py._com.pyplugins - assert config2.pytestplugins.pm == py._com.pyplugins - -def test_config_initafterpickle_some(): - tmp = py.test.ensuretemp("test_config_initafterpickle_some") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write("x=1 ; y=2") - hello = tmp.ensure("test_hello.py") - config = py.test.config._reparse([hello]) - config2 = py.test.config._reparse([tmp.dirpath()]) - config2._initialized = False # we have to do that from tests - config2._repr = config._makerepr() - config2._initafterpickle(topdir=tmp.dirpath()) - - for col1, col2 in zip(getcolitems(config), getcolitems(config2)): - assert col1.fspath == col2.fspath - cols = getcolitems(config2) - assert len(cols) == 1 - col = cols[0] - assert col.name == 'test_hello.py' - assert col.parent.name == tmp.basename - assert col.parent.parent is None - -def test_config_make_and__mergerepr(): - tmp = py.test.ensuretemp("reprconfig1") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write("x=1") - config = py.test.config._reparse([tmp]) - repr = config._makerepr() - config.option.verbose = 42 - repr2 = config._makerepr() - config = py.test.config._reparse([tmp.dirpath()]) - py.test.raises(KeyError, "config.getvalue('x')") - config._mergerepr(repr) - assert config.getvalue('x') == 1 - config._mergerepr(repr2) - assert config.option.verbose == 42 - -def test_config_rconfig(): - tmp = py.test.ensuretemp("rconfigopt") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write(py.code.Source(""" - import py - Option = py.test.config.Option - option = py.test.config.addoptions("testing group", - Option('-G', '--glong', action="store", default=42, - type="int", dest="gdest", help="g value.")) - """)) - config = py.test.config._reparse([tmp, "-G", "11"]) - assert config.option.gdest == 11 - repr = config._makerepr() - config = py.test.config._reparse([tmp.dirpath()]) - py.test.raises(AttributeError, "config.option.gdest") - config._mergerepr(repr) - option = config.addoptions("testing group", - config.Option('-G', '--glong', action="store", default=42, - type="int", dest="gdest", help="g value.")) - assert config.option.gdest == 11 - assert option.gdest == 11 - -class TestSessionAndOptions: - def test_implied_dsession(self, testdir): - for x in 'startserver runbrowser rest'.split(): - config = testdir.parseconfig(testdir.tmpdir, '--dist', '--%s' % x) - assert config._getestdirname() == 'DSession' - - def test_implied_different_sessions(self, tmpdir): - config = py.test.config._reparse([tmpdir]) - assert config._getestdirname() == 'Session' - config = py.test.config._reparse([tmpdir, '--dist']) - assert config._getestdirname() == 'DSession' - config = py.test.config._reparse([tmpdir, '-n3']) - assert config._getestdirname() == 'DSession' - config = py.test.config._reparse([tmpdir, '--looponfailing']) - assert config._getestdirname() == 'LooponfailingSession' - config = py.test.config._reparse([tmpdir, '--exec=x']) - assert config._getestdirname() == 'DSession' - config = py.test.config._reparse([tmpdir, '--dist', '--exec=x']) - assert config._getestdirname() == 'DSession' - config = py.test.config._reparse([tmpdir, '-f', - '--dist', '--exec=x']) - assert config._getestdirname() == 'LooponfailingSession' - config = py.test.config._reparse([tmpdir, '-f', '-n3', - '--dist', '--exec=x', - '--collectonly']) - assert config._getestdirname() == 'Session' - - def test_sessionname_lookup_custom(self, testdir): +class TestConfigCmdlineParsing: + def test_config_cmdline_options(self, testdir): testdir.makepyfile(conftest=""" - from py.__.test.session import Session - class MySession(Session): - pass - """) - config = testdir.parseconfig("--session=MySession", testdir.tmpdir) - session = config.initsession() - assert session.__class__.__name__ == 'MySession' - - def test_initsession(self, tmpdir): - config = py.test.config._reparse([tmpdir]) - session = config.initsession() - assert session.config is config - - def test_boxed_option_default(self, testdir): - testdir.makepyfile(conftest="dist_hosts=[]") - tmpdir = testdir.tmpdir.ensure("subdir", dir=1) - config = py.test.config._reparse([tmpdir]) - config.initsession() - assert not config.option.boxed - config = py.test.config._reparse(['--dist', tmpdir]) - config.initsession() - assert not config.option.boxed - - def test_boxed_option_from_conftest(self, testdir): - testdir.makepyfile(conftest="dist_hosts=[]") - tmpdir = testdir.tmpdir.ensure("subdir", dir=1) - tmpdir.join("conftest.py").write(py.code.Source(""" - dist_hosts = [] - dist_boxed = True - """)) - config = py.test.config._reparse(['--dist', tmpdir]) - config.initsession() - assert config.option.boxed - - def test_boxed_option_from_conftest(self, testdir): - testdir.makepyfile(conftest="dist_boxed=False") - config = py.test.config._reparse([testdir.tmpdir, '--box']) - assert config.option.boxed - config.initsession() - assert config.option.boxed - - def test_getvalue_pathlist(self, tmpdir): - somepath = tmpdir.join("x", "y", "z") - p = tmpdir.join("conftest.py") - p.write("pathlist = ['.', %r]" % str(somepath)) - config = py.test.config._reparse([p]) - assert config.getvalue_pathlist('notexist') is None - pl = config.getvalue_pathlist('pathlist') - print pl - assert len(pl) == 2 - assert pl[0] == tmpdir - assert pl[1] == somepath + import py + def _callback(option, opt_str, value, parser, *args, **kwargs): + option.tdest = True + Option = py.test.config.Option + option = py.test.config.addoptions("testing group", + Option('-G', '--glong', action="store", default=42, + type="int", dest="gdest", help="g value."), + # XXX note: special case, option without a destination + Option('-T', '--tlong', action="callback", callback=_callback, + help='t value'), + ) + """) + old = testdir.chdir() + try: + config = py.test.config._reparse(['-G', '17']) + finally: + old.chdir() + assert config.option.gdest == 17 - config.option.mypathlist = [py.path.local()] - pl = config.getvalue_pathlist('mypathlist') - assert pl == [py.path.local()] + def test_config_cmdline_options_only_lowercase(self, testdir): + testdir.makepyfile(conftest=""" + import py + Option = py.test.config.Option + options = py.test.config.addoptions("testing group", + Option('-g', '--glong', action="store", default=42, + type="int", dest="gdest", help="g value."), + ) + """) + old = testdir.chdir() + try: + py.test.raises(ValueError, """ + py.test.config._reparse(['-g', '17']) + """) + finally: + old.chdir() - def test_config_iocapturing(self, testdir): - config = testdir.parseconfig(testdir.tmpdir) - assert config.getvalue("conf_iocapture") - tmpdir = testdir.tmpdir.ensure("sub-with-conftest", dir=1) - tmpdir.join("conftest.py").write(py.code.Source(""" - conf_iocapture = "no" - """)) + def test_parsing_again_fails(self, tmpdir): config = py.test.config._reparse([tmpdir]) - assert config.getvalue("conf_iocapture") == "no" - capture = config._getcapture() - assert isinstance(capture, py.io.StdCapture) - assert not capture._out - assert not capture._err - assert not capture._in - assert isinstance(capture, py.io.StdCapture) - for opt, cls in (("sys", py.io.StdCapture), - ("fd", py.io.StdCaptureFD), - ): - config.option.conf_iocapture = opt - capture = config._getcapture() - assert isinstance(capture, cls) + py.test.raises(AssertionError, "config.parse([])") def test_conflict_options(self): def check_conflict_option(opts): @@ -299,32 +59,60 @@ for spec in conflict_options: opts = spec.split() yield check_conflict_option, opts - - def test_default_session_options(self, testdir): - def runfiletest(opts): - sorter = testdir.inline_run(*opts) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 2 - assert skipped == passed == 0 - path = testdir.makepyfile(""" - def test_f1(): assert 0 - def test_f2(): assert 0 - """) - - for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], - ['--tb=long'], ['--fulltrace'], ['--nomagic'], - ['--traceconfig'], ['-v'], ['-v', '-v']): - runfiletest(opts + [path]) - def test_is_not_boxed_by_default(self, testdir): - config = py.test.config._reparse([testdir.tmpdir]) - assert not config.option.boxed +class TestConfigAPI: + def test_tmpdir(self): + d1 = py.test.ensuretemp('hello') + d2 = py.test.ensuretemp('hello') + assert d1 == d2 + assert d1.check(dir=1) + + def test_config_getvalue_honours_conftest(self, testdir): + testdir.makepyfile(conftest="x=1") + testdir.mkdir("sub").join("conftest.py").write("x=2 ; y = 3") + config = testdir.parseconfig() + o = testdir.tmpdir + assert config.getvalue("x") == 1 + assert config.getvalue("x", o.join('sub')) == 2 + py.test.raises(KeyError, "config.getvalue('y')") + config = py.test.config._reparse([str(o.join('sub'))]) + assert config.getvalue("x") == 2 + assert config.getvalue("y") == 3 + assert config.getvalue("x", o) == 1 + py.test.raises(KeyError, 'config.getvalue("y", o)') + + + def test_config_overwrite(self, testdir): + o = testdir.tmpdir + o.ensure("conftest.py").write("x=1") + config = py.test.config._reparse([str(o)]) + assert config.getvalue('x') == 1 + config.option.x = 2 + assert config.getvalue('x') == 2 + config = py.test.config._reparse([str(o)]) + assert config.getvalue('x') == 1 + + def test_getvalue_pathlist(self, tmpdir): + somepath = tmpdir.join("x", "y", "z") + p = tmpdir.join("conftest.py") + p.write("pathlist = ['.', %r]" % str(somepath)) + config = py.test.config._reparse([p]) + assert config.getvalue_pathlist('notexist') is None + pl = config.getvalue_pathlist('pathlist') + print pl + assert len(pl) == 2 + assert pl[0] == tmpdir + assert pl[1] == somepath + + config.option.mypathlist = [py.path.local()] + pl = config.getvalue_pathlist('mypathlist') + assert pl == [py.path.local()] -class TestConfigColitems: +class TestConfigApi_getcolitems: def test_getcolitems_onedir(self, tmpdir): config = py.test.config._reparse([tmpdir]) - colitems = getcolitems(config) + colitems = config.getcolitems() assert len(colitems) == 1 col = colitems[0] assert isinstance(col, py.test.collect.Directory) @@ -333,7 +121,7 @@ def test_getcolitems_twodirs(self, tmpdir): config = py.test.config._reparse([tmpdir, tmpdir]) - colitems = getcolitems(config) + colitems = config.getcolitems() assert len(colitems) == 2 col1, col2 = colitems assert col1.name == col2.name @@ -342,7 +130,7 @@ def test_getcolitems_curdir_and_subdir(self, tmpdir): a = tmpdir.ensure("a", dir=1) config = py.test.config._reparse([tmpdir, a]) - colitems = getcolitems(config) + colitems = config.getcolitems() assert len(colitems) == 2 col1, col2 = colitems assert col1.name == tmpdir.basename @@ -384,6 +172,160 @@ for col in col.listchain(): assert col._config is config + + + +class TestOptionEffects: + def test_boxed_option_default(self, testdir): + testdir.makepyfile(conftest="dist_hosts=[]") + tmpdir = testdir.tmpdir.ensure("subdir", dir=1) + config = py.test.config._reparse([tmpdir]) + config.initsession() + assert not config.option.boxed + config = py.test.config._reparse(['--dist', tmpdir]) + config.initsession() + assert not config.option.boxed + + def test_is_not_boxed_by_default(self, testdir): + config = py.test.config._reparse([testdir.tmpdir]) + assert not config.option.boxed + + def test_boxed_option_from_conftest(self, testdir): + testdir.makepyfile(conftest="dist_hosts=[]") + tmpdir = testdir.tmpdir.ensure("subdir", dir=1) + tmpdir.join("conftest.py").write(py.code.Source(""" + dist_hosts = [] + dist_boxed = True + """)) + config = py.test.config._reparse(['--dist', tmpdir]) + config.initsession() + assert config.option.boxed + + def test_boxed_option_from_conftest(self, testdir): + testdir.makepyfile(conftest="dist_boxed=False") + config = py.test.config._reparse([testdir.tmpdir, '--box']) + assert config.option.boxed + config.initsession() + assert config.option.boxed + + def test_config_iocapturing(self, testdir): + config = testdir.parseconfig(testdir.tmpdir) + assert config.getvalue("conf_iocapture") + tmpdir = testdir.tmpdir.ensure("sub-with-conftest", dir=1) + tmpdir.join("conftest.py").write(py.code.Source(""" + conf_iocapture = "no" + """)) + config = py.test.config._reparse([tmpdir]) + assert config.getvalue("conf_iocapture") == "no" + capture = config._getcapture() + assert isinstance(capture, py.io.StdCapture) + assert not capture._out + assert not capture._err + assert not capture._in + assert isinstance(capture, py.io.StdCapture) + for opt, cls in (("sys", py.io.StdCapture), + ("fd", py.io.StdCaptureFD), + ): + config.option.conf_iocapture = opt + capture = config._getcapture() + assert isinstance(capture, cls) + + +class TestConfig_gettopdir: + def test_gettopdir(self, testdir): + from py.__.test.config import gettopdir + tmp = testdir.tmpdir + assert gettopdir([tmp]) == tmp + topdir = gettopdir([tmp.join("hello"), tmp.join("world")]) + assert topdir == tmp + somefile = tmp.ensure("somefile.py") + assert gettopdir([somefile]) == tmp + + def test_gettopdir_pypkg(self, testdir): + from py.__.test.config import gettopdir + tmp = testdir.tmpdir + a = tmp.ensure('a', dir=1) + b = tmp.ensure('a', 'b', '__init__.py') + c = tmp.ensure('a', 'b', 'c.py') + Z = tmp.ensure('Z', dir=1) + assert gettopdir([c]) == a + assert gettopdir([c, Z]) == tmp + +class TestConfigPickling: + @py.test.keywords(xfail="issue1 config's pytestplugins/bus initialization") + def test_config_initafterpickle_plugin(self, testdir): + testdir.makepyfile(__init__="", conftest="x=1; y=2") + hello = testdir.makepyfile(hello="") + tmp = testdir.tmpdir + config = py.test.config._reparse([hello]) + config2 = py.test.config._reparse([tmp.dirpath()]) + config2._initialized = False # we have to do that from tests + config2._repr = config._makerepr() + config2._initafterpickle(topdir=tmp.dirpath()) + # we check that config "remote" config objects + # have correct plugin initialization + #XXX assert config2.pytestplugins.pm._plugins + #XXX assert config2.bus.isregistered(config2.pytestplugins.forward_event) + assert config2.bus == py._com.pyplugins + assert config2.pytestplugins.pm == py._com.pyplugins + + def test_config_initafterpickle_some(self, testdir): + tmp = testdir.tmpdir + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write("x=1 ; y=2") + hello = tmp.ensure("test_hello.py") + config = py.test.config._reparse([hello]) + config2 = py.test.config._reparse([tmp.dirpath()]) + config2._initialized = False # we have to do that from tests + config2._repr = config._makerepr() + config2._initafterpickle(topdir=tmp.dirpath()) + + for col1, col2 in zip(config.getcolitems(), config2.getcolitems()): + assert col1.fspath == col2.fspath + cols = config2.getcolitems() + assert len(cols) == 1 + col = cols[0] + assert col.name == 'test_hello.py' + assert col.parent.name == tmp.basename + assert col.parent.parent is None + + def test_config_make_and__mergerepr(self, testdir): + tmp = testdir.tmpdir + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write("x=1") + config = py.test.config._reparse([tmp]) + repr = config._makerepr() + config.option.verbose = 42 + repr2 = config._makerepr() + config = py.test.config._reparse([tmp.dirpath()]) + py.test.raises(KeyError, "config.getvalue('x')") + config._mergerepr(repr) + assert config.getvalue('x') == 1 + config._mergerepr(repr2) + assert config.option.verbose == 42 + + def test_config_rconfig(self, testdir): + tmp = testdir.tmpdir + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write(py.code.Source(""" + import py + Option = py.test.config.Option + option = py.test.config.addoptions("testing group", + Option('-G', '--glong', action="store", default=42, + type="int", dest="gdest", help="g value.")) + """)) + config = py.test.config._reparse([tmp, "-G", "11"]) + assert config.option.gdest == 11 + repr = config._makerepr() + config = py.test.config._reparse([tmp.dirpath()]) + py.test.raises(AttributeError, "config.option.gdest") + config._mergerepr(repr) + option = config.addoptions("testing group", + config.Option('-G', '--glong', action="store", default=42, + type="int", dest="gdest", help="g value.")) + assert config.option.gdest == 11 + assert option.gdest == 11 + def test_config_picklability(self, tmpdir): import cPickle config = py.test.config._reparse([tmpdir]) @@ -442,6 +384,64 @@ assert newcol2.fspath.basename == dir1.basename assert newcol2.fspath.relto(topdir) +class TestSessionAndOptions: + def test_implied_dsession(self, testdir): + for x in 'startserver runbrowser rest'.split(): + config = testdir.parseconfig(testdir.tmpdir, '--dist', '--%s' % x) + assert config._getestdirname() == 'DSession' + + def test_implied_different_sessions(self, tmpdir): + config = py.test.config._reparse([tmpdir]) + assert config._getestdirname() == 'Session' + config = py.test.config._reparse([tmpdir, '--dist']) + assert config._getestdirname() == 'DSession' + config = py.test.config._reparse([tmpdir, '-n3']) + assert config._getestdirname() == 'DSession' + config = py.test.config._reparse([tmpdir, '--looponfailing']) + assert config._getestdirname() == 'LooponfailingSession' + config = py.test.config._reparse([tmpdir, '--exec=x']) + assert config._getestdirname() == 'DSession' + config = py.test.config._reparse([tmpdir, '--dist', '--exec=x']) + assert config._getestdirname() == 'DSession' + config = py.test.config._reparse([tmpdir, '-f', + '--dist', '--exec=x']) + assert config._getestdirname() == 'LooponfailingSession' + config = py.test.config._reparse([tmpdir, '-f', '-n3', + '--dist', '--exec=x', + '--collectonly']) + assert config._getestdirname() == 'Session' + + def test_sessionname_lookup_custom(self, testdir): + testdir.makepyfile(conftest=""" + from py.__.test.session import Session + class MySession(Session): + pass + """) + config = testdir.parseconfig("--session=MySession", testdir.tmpdir) + session = config.initsession() + assert session.__class__.__name__ == 'MySession' + + def test_initsession(self, tmpdir): + config = py.test.config._reparse([tmpdir]) + session = config.initsession() + assert session.config is config + + def test_default_session_options(self, testdir): + def runfiletest(opts): + sorter = testdir.inline_run(*opts) + passed, skipped, failed = sorter.countoutcomes() + assert failed == 2 + assert skipped == passed == 0 + path = testdir.makepyfile(""" + def test_f1(): assert 0 + def test_f2(): assert 0 + """) + + for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], + ['--tb=long'], ['--fulltrace'], ['--nomagic'], + ['--traceconfig'], ['-v'], ['-v', '-v']): + runfiletest(opts + [path]) + def test_default_bus(): assert py.test.config.bus is py._com.pyplugins Modified: py/branch/pytestplugin/py/test/testing/test_pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pycollect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pycollect.py Mon Feb 23 16:23:42 2009 @@ -335,7 +335,7 @@ f1, f2 = colitems assert cmp(f2, f1) > 0 -class TestCustomConftest: +class TestConftestCustomization: def test_extra_python_files_and_functions(self, testdir): testdir.makepyfile(conftest=""" import py From hpk at codespeak.net Mon Feb 23 18:58:49 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 23 Feb 2009 18:58:49 +0100 (CET) Subject: [py-svn] r62100 - in py/branch/pytestplugin/py/test: . dsession dsession/testing looponfail/testing testing Message-ID: <20090223175849.6F4CF168433@codespeak.net> Author: hpk Date: Mon Feb 23 18:58:43 2009 New Revision: 62100 Added: py/branch/pytestplugin/py/test/testing/test_genitems.py (contents, props changed) Modified: py/branch/pytestplugin/py/test/dsession/dsession.py py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py py/branch/pytestplugin/py/test/looponfail/testing/test_util.py py/branch/pytestplugin/py/test/session.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_session.py Log: another bunch of small refactorings, shuffling of tests Modified: py/branch/pytestplugin/py/test/dsession/dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/dsession.py Mon Feb 23 18:58:43 2009 @@ -104,7 +104,10 @@ eventname, args, kwargs = eventcall self.bus.notify(eventname, *args, **kwargs) - ev, = args + if args: + ev, = args + else: + ev = None if eventname == "itemtestreport": self.removeitem(ev.colitem) if ev.failed: Modified: py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_dsession.py Mon Feb 23 18:58:43 2009 @@ -71,7 +71,6 @@ name, args, kwargs = session.queue.get(block=False) assert name == 'collectionreport' rep, = args - assert isinstance(rep, event.CollectionReport) assert len(rep.result) == 1 def test_triggertesting_item(self, testdir): @@ -203,11 +202,10 @@ item = testdir.getitem("def test_func(): pass") session = DSession(item._config) - ev = event.NOP() evrec = EventRecorder(session.bus) - session.queueevent("anonymous", ev) + session.queueevent("NOPevent", 42) session.loop_once(LoopState([])) - assert evrec.getfirstnamed('anonymous') == ev + assert evrec.getfirstnamed('NOPevent') def runthrough(self, item): session = DSession(item._config) @@ -216,7 +214,7 @@ session.addhost(host1) loopstate = LoopState([item]) - session.queueevent("anonymous", event.NOP()) + session.queueevent("NOP") session.loop_once(loopstate) assert host1.node.sent == [[item]] @@ -301,7 +299,7 @@ assert remaining == [] evname, ev = evrec.events[-1] - assert isinstance(ev, event.Deselected) + assert evname == "deselected" assert ev.items == items modcol._config.option.keyword = "test_fail" @@ -309,7 +307,7 @@ assert remaining == [items[0]] evname, ev = evrec.events[-1] - assert isinstance(ev, event.Deselected) + assert evname == "deselected" assert ev.items == [items[1]] def test_hostdown_shutdown_after_completion(self, testdir): Modified: py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py Mon Feb 23 18:58:43 2009 @@ -3,7 +3,6 @@ """ import py -from py.__.test import event from py.__.test.dsession.dsession import DSession from py.__.test.dsession.hostmanage import HostManager, Host from test_masterslave import EventQueue @@ -100,7 +99,7 @@ assert not tmpdir.join("__init__.py").check() dist = DSession(config) sorter = testdir.inline_runsession(dist) - testevents = sorter.get(event.ItemTestReport) + testevents = sorter.getnamed('itemtestreport') assert len([x for x in testevents if x.passed]) == 2 assert len([x for x in testevents if x.failed]) == 3 assert len([x for x in testevents if x.skipped]) == 0 Modified: py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_masterslave.py Mon Feb 23 18:58:43 2009 @@ -2,7 +2,6 @@ import py from py.__.test.dsession.masterslave import MasterNode from py.__.test.dsession.hostmanage import Host -from py.__.test import event class EventQueue: def __init__(self, bus, queue=None): Modified: py/branch/pytestplugin/py/test/looponfail/testing/test_util.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/testing/test_util.py (original) +++ py/branch/pytestplugin/py/test/looponfail/testing/test_util.py Mon Feb 23 18:58:43 2009 @@ -1,9 +1,8 @@ import py from py.__.test.looponfail.util import StatRecorder -from py.__.test import event -def test_filechange(): - tmp = py.test.ensuretemp("test_filechange") +def test_filechange(tmpdir): + tmp = tmpdir hello = tmp.ensure("hello.py") sd = StatRecorder([tmp]) changed = sd.check() @@ -35,8 +34,8 @@ changed = sd.check() assert changed -def test_pycremoval(): - tmp = py.test.ensuretemp("test_pycremoval") +def test_pycremoval(tmpdir): + tmp = tmpdir hello = tmp.ensure("hello.py") sd = StatRecorder([tmp]) changed = sd.check() @@ -52,8 +51,8 @@ assert not pycfile.check() -def test_waitonchange(): - tmp = py.test.ensuretemp("test_waitonchange") +def test_waitonchange(tmpdir): + tmp = tmpdir sd = StatRecorder([tmp]) wp = py._thread.WorkerPool(1) Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Mon Feb 23 18:58:43 2009 @@ -7,10 +7,8 @@ import py from py.__.test import event, outcome -import py.__.test.custompdb -# used for genitems() -from py.__.test.outcome import Exit +# imports used for genitems() Item = py.test.collect.Item Collector = py.test.collect.Collector from runner import basic_collect_report @@ -135,7 +133,8 @@ return exitstatus def runpdb(self, excinfo): - py.__.test.custompdb.post_mortem(excinfo._excinfo[2]) + from py.__.test.custompdb import post_mortem + post_mortem(excinfo._excinfo[2]) def runtest(self, item): runner = item._getrunner() Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Mon Feb 23 18:58:43 2009 @@ -62,6 +62,7 @@ class TestConfigAPI: + @py.test.keywords(issue="ensuretemp should call config.maketemp(basename)") def test_tmpdir(self): d1 = py.test.ensuretemp('hello') d2 = py.test.ensuretemp('hello') @@ -82,7 +83,6 @@ assert config.getvalue("x", o) == 1 py.test.raises(KeyError, 'config.getvalue("y", o)') - def test_config_overwrite(self, testdir): o = testdir.tmpdir o.ensure("conftest.py").write("x=1") @@ -252,7 +252,7 @@ assert gettopdir([c, Z]) == tmp class TestConfigPickling: - @py.test.keywords(xfail="issue1 config's pytestplugins/bus initialization") + @py.test.keywords(xfail=True, issue="config's pytestplugins/bus initialization") def test_config_initafterpickle_plugin(self, testdir): testdir.makepyfile(__init__="", conftest="x=1; y=2") hello = testdir.makepyfile(hello="") Added: py/branch/pytestplugin/py/test/testing/test_genitems.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/testing/test_genitems.py Mon Feb 23 18:58:43 2009 @@ -0,0 +1,138 @@ +import py + +class Test_genitems: + def test_check_collect_hashes(self, testdir): + p = testdir.makepyfile(""" + def test_1(): + pass + + def test_2(): + pass + """) + p.copy(p.dirpath(p.purebasename + "2" + ".py")) + items, events = testdir.inline_genitems(p.dirpath()) + assert len(items) == 4 + for numi, i in enumerate(items): + for numj, j in enumerate(items): + if numj != numi: + assert hash(i) != hash(j) + assert i != j + + def test_root_conftest_syntax_error(self, testdir): + # do we want to unify behaviour with + # test_subdir_conftest_error? + p = testdir.makepyfile(conftest="raise SyntaxError\n") + py.test.raises(SyntaxError, testdir.inline_genitems, p.dirpath()) + + def test_subdir_conftest_error(self, testdir): + tmp = testdir.tmpdir + tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") + items, events = testdir.inline_genitems(tmp) + collectionfailures = events.getfailedcollections() + assert len(collectionfailures) == 1 + ev = collectionfailures[0] + assert ev.longrepr.reprcrash.message.startswith("SyntaxError") + + def test_example_items1(self, testdir): + p = testdir.makepyfile(''' + def testone(): + pass + + class TestX: + def testmethod_one(self): + pass + + class TestY(TestX): + pass + ''') + items, events = testdir.inline_genitems(p) + assert len(items) == 3 + assert items[0].name == 'testone' + assert items[1].name == 'testmethod_one' + assert items[2].name == 'testmethod_one' + + # let's also test getmodpath here + assert items[0].getmodpath() == "testone" + assert items[1].getmodpath() == "TestX.testmethod_one" + assert items[2].getmodpath() == "TestY.testmethod_one" + + s = items[0].getmodpath(stopatmodule=False) + assert s.endswith("test_example_items1.testone") + print s + + def test_collect_doctest_files_with_test_prefix(self, testdir): + from py.__.test.pycollect import DoctestFileContent + testdir.maketxtfile(whatever="") + checkfile = testdir.maketxtfile(test_something=""" + alskdjalsdk + >>> i = 5 + >>> i-1 + 4 + """) + for x in (testdir.tmpdir, checkfile): + #print "checking that %s returns custom items" % (x,) + items, events = testdir.inline_genitems(x) + assert len(items) == 1 + assert isinstance(items[0], DoctestFileContent) + + +class TestKeywordSelection: + def test_select_simple(self, testdir): + file_test = testdir.makepyfile(""" + def test_one(): assert 0 + class TestClass(object): + def test_method_one(self): + assert 42 == 43 + """) + def check(keyword, name): + sorter = testdir.inline_run("-s", "-k", keyword, file_test) + passed, skipped, failed = sorter.listoutcomes() + assert len(failed) == 1 + assert failed[0].colitem.name == name + assert len(sorter.getnamed('deselected')) == 1 + + for keyword in ['test_one', 'est_on']: + #yield check, keyword, 'test_one' + check(keyword, 'test_one') + check('TestClass.test', 'test_method_one') + + def test_select_extra_keywords(self, testdir): + p = testdir.makepyfile(test_select=""" + def test_1(): + pass + class TestClass: + def test_2(self): + pass + """) + testdir.makepyfile(conftest=""" + import py + class Class(py.test.collect.Class): + def _keywords(self): + return ['xxx', self.name] + """) + for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', + 'TestClass test_2', 'xxx TestClass test_2',): + sorter = testdir.inline_run(p.dirpath(), '-s', '-k', keyword) + print "keyword", repr(keyword) + passed, skipped, failed = sorter.listoutcomes() + assert len(passed) == 1 + assert passed[0].colitem.name == "test_2" + dlist = sorter.getnamed("deselected") + assert len(dlist) == 1 + assert dlist[0].items[0].name == 'test_1' + + def test_select_starton(self, testdir): + threepass = testdir.makepyfile(test_threepass=""" + def test_one(): assert 1 + def test_two(): assert 1 + def test_three(): assert 1 + """) + sorter = testdir.inline_run("-k", "test_two:", threepass) + passed, skipped, failed = sorter.listoutcomes() + assert len(passed) == 2 + assert not failed + dlist = sorter.getnamed("deselected") + assert len(dlist) == 1 + item = dlist[0].items[0] + assert item.name == "test_one" + Modified: py/branch/pytestplugin/py/test/testing/test_session.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_session.py (original) +++ py/branch/pytestplugin/py/test/testing/test_session.py Mon Feb 23 18:58:43 2009 @@ -1,65 +1,4 @@ import py -from py.__.test import event - -class TestKeywordSelection: - def test_select_simple(self, testdir): - file_test = testdir.makepyfile(""" - def test_one(): assert 0 - class TestClass(object): - def test_method_one(self): - assert 42 == 43 - """) - def check(keyword, name): - sorter = testdir.inline_run("-s", "-k", keyword, file_test) - passed, skipped, failed = sorter.listoutcomes() - assert len(failed) == 1 - assert failed[0].colitem.name == name - assert len(sorter.get(event.Deselected)) == 1 - - for keyword in ['test_one', 'est_on']: - #yield check, keyword, 'test_one' - check(keyword, 'test_one') - check('TestClass.test', 'test_method_one') - - def test_select_extra_keywords(self, testdir): - p = testdir.makepyfile(test_select=""" - def test_1(): - pass - class TestClass: - def test_2(self): - pass - """) - testdir.makepyfile(conftest=""" - import py - class Class(py.test.collect.Class): - def _keywords(self): - return ['xxx', self.name] - """) - for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', - 'TestClass test_2', 'xxx TestClass test_2',): - sorter = testdir.inline_run(p.dirpath(), '-s', '-k', keyword) - print "keyword", repr(keyword) - passed, skipped, failed = sorter.listoutcomes() - assert len(passed) == 1 - assert passed[0].colitem.name == "test_2" - dlist = sorter.get(event.Deselected) - assert len(dlist) == 1 - assert dlist[0].items[0].name == 'test_1' - - def test_select_starton(self, testdir): - threepass = testdir.makepyfile(test_threepass=""" - def test_one(): assert 1 - def test_two(): assert 1 - def test_three(): assert 1 - """) - sorter = testdir.inline_run("-k", "test_two:", threepass) - passed, skipped, failed = sorter.listoutcomes() - assert len(passed) == 2 - assert not failed - dlist = sorter.get(event.Deselected) - assert len(dlist) == 1 - item = dlist[0].items[0] - assert item.name == "test_one" class SessionTests: def test_basic_testitem_events(self, testdir): @@ -81,9 +20,9 @@ assert failed[0].colitem.name == "test_one_one" assert failed[1].colitem.name == "test_other" assert failed[2].colitem.name == "test_two" - itemstarted = sorter.get(event.ItemStart) + itemstarted = sorter.getnamed("itemstart") assert len(itemstarted) == 4 - colstarted = sorter.get(event.CollectionStart) + colstarted = sorter.getnamed("collectionstart") assert len(colstarted) == 1 col = colstarted[0].collector assert isinstance(col, py.test.collect.Module) @@ -186,7 +125,7 @@ def test_one(): pass """) sorter = testdir.inline_run(testdir.tmpdir) - skips = sorter.get(event.CollectionReport) + skips = sorter.getnamed("collectionreport") assert len(skips) == 1 assert skips[0].skipped @@ -257,11 +196,11 @@ ) sorter = testdir.inline_run('--collectonly', p.dirpath()) - itemstarted = sorter.get(event.ItemStart) + itemstarted = sorter.getnamed("itemstart") assert len(itemstarted) == 3 - assert not sorter.get(event.ItemTestReport) - started = sorter.get(event.CollectionStart) - finished = sorter.get(event.CollectionReport) + assert not sorter.getnamed("itemtestreport") + started = sorter.getnamed("collectionstart") + finished = sorter.getnamed("collectionreport") assert len(started) == len(finished) assert len(started) == 8 colfail = [x for x in finished if x.failed] From hpk at codespeak.net Mon Feb 23 20:10:10 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 23 Feb 2009 20:10:10 +0100 (CET) Subject: [py-svn] r62101 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090223191010.1F146168456@codespeak.net> Author: hpk Date: Mon Feb 23 20:10:09 2009 New Revision: 62101 Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: sorting through plugin tests, marking some and writing a new one marked with xfail Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Mon Feb 23 20:10:09 2009 @@ -22,6 +22,13 @@ name = name.replace(".", "_") self.tmpdir = py.test.ensuretemp(name) self._plugins = [] + self._syspathremove = [] + #self._olddir = py.std.os.getcwd() + + def finalize(self): + for p in self._syspathremove: + py.std.sys.path.remove(p) + #py.std.os.chdir(self._olddir) def _makefile(self, ext, args, kwargs): items = kwargs.items() @@ -47,6 +54,12 @@ def maketxtfile(self, *args, **kwargs): return self._makefile('.txt', args, kwargs) + def syspathinsert(self, path=None): + if path is None: + path = self.tmpdir + py.std.sys.path.insert(0, str(path)) + self._syspathremove.append(str(path)) + def mkdir(self, name): return self.tmpdir.mkdir(name) @@ -378,7 +391,9 @@ return LineMatcher def pytest_pyfuncarg_testdir(self, pyfuncitem): - return TmpTestdir(pyfuncitem) - + tmptestdir = TmpTestdir(pyfuncitem) + pyfuncitem.addfinalizer(tmptestdir.finalize) + return tmptestdir + def pytest_pyfuncarg_EventRecorder(self, pyfuncitem): return EventRecorder Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Mon Feb 23 20:10:09 2009 @@ -29,6 +29,7 @@ return self._plugins[pname.lower()] def setinitial(self, modules): + self.pyplugins.consider_env("PYTEST_PLUGINS") for module in modules: self.consider_module(module) @@ -43,20 +44,24 @@ def import_plugin(self, spec): assert isinstance(spec, str) modname, clsname = canonical_names(spec) - if clsname.lower() not in self._plugins: - mod = importplugin(modname) - plugin = registerplugin(self.pyplugins.register, mod, clsname) - self._plugins[clsname.lower()] = plugin - self.consider_module(mod) + if clsname.lower() in self._plugins: + return + mod = importplugin(modname) + plugin = registerplugin(self.pyplugins.register, mod, clsname) + self._plugins[clsname.lower()] = plugin + self.consider_module(mod) # # # API for interacting with registered and instantiated plugin objects # # - def getfirst(self, methname): - for x in self.pyplugins.listattr(methname): + def getfirst(self, attrname): + for x in self.pyplugins.listattr(attrname): return x - + + def listattr(self, attrname): + return self.pyplugins.listattr(attrname) + def call_firstresult(self, *args, **kwargs): return self.pyplugins.call_firstresult(*args, **kwargs) Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Mon Feb 23 20:10:09 2009 @@ -1,7 +1,6 @@ import py from py.__.test.pytestplugin import PytestPlugins, canonical_names from py.__.test.pytestplugin import registerplugin, importplugin -from py.__.test.config import Config as pytestConfig def test_considermodule(): pp = PytestPlugins() @@ -16,8 +15,8 @@ py.test.raises(ImportError, "pp.setinitial([mod])") class TestPytestPlugins: - def test_configure(self): - config = pytestConfig() + def test_configure(self, testdir): + config = testdir.parseconfig() l = [] events = [] class A: @@ -117,129 +116,75 @@ from py.__.test.config import Config as pytestConfig class TestInitialization: - disabled = True - def setup_method(self, method): - self.tmpdir = py.test.ensuretemp("%s.%s.%s" % - (__name__, self.__class__.__name__, method.__name__)) - - def test_import_plugin_importname(self): + def test_setinitial_env(self): plugins = PytestPlugins() - py.test.raises(ImportError, 'plugins.import_plugin("x.y")') - py.test.raises(ImportError, 'plugins.import_plugin("pytest_x.y")') - - sys.path.insert(0, str(self.tmpdir)) - try: - pluginname = "pytest_hello" - self.tmpdir.join(pluginname + ".py").write(py.code.Source(""" - class Hello: - pass - """)) - plugins.import_plugin("hello") - plugins.import_plugin("pytest_hello") - plugin = plugins.getplugin("hello") - finally: - sys.path.remove(str(self.tmpdir)) - - def test_import_plugin_defaults_to_pytestplugin(self): - from py.__.test.defaultconftest import pytest_plugins - for name in pytest_plugins: - if isinstance(name, str): - break - sys.path.insert(0, str(self.tmpdir)) + KEY = "PYTEST_PLUGINS" + old = os.environ.get(KEY, None) try: - self.tmpdir.join(name + ".py").write(py.code.Source(""" - class Terminal: - pass - class AnotherPlugin: - pass - pytest_plugins = AnotherPlugin - """)) - plugins = PytestPlugins() - plugins.import_plugin(name) - plugin = plugins.getplugin("terminal") - #del sys.modules[name] - print plugins._plugins - plugin = plugins.getplugin("anotherplugin") + os.environ[KEY] = "test_setinitial" + py.test.raises(ImportError, "plugins.setinitial([])") finally: - sys.path.remove(str(self.tmpdir)) - - def test_import_plugin_class(self): - plugins = PytestPlugins() - class SomePlugin: - pass - plugins.import_plugin(SomePlugin) - plugin = plugins.getplugin("someplugin") - assert isinstance(plugin, SomePlugin) - i = len(plugins._plugins) - plugins.import_plugin(SomePlugin) - assert len(plugins._plugins) == i - - def test_addpluginclass_post_configure(self): - plugins = PytestPlugins() - l = [] - class SomePlugin: - def pytest_configure(self, config): - l.append(config) - conf = pytestConfig() - plugins.configure(config=conf) - plugins.import_plugin(SomePlugin) - assert len(l) == 1 - assert l[0] is conf + if old is None: + del os.environ[KEY] + else: + os.environ[KEY] = old - def test_consider_module(self): + def test_import_plugin_importname(self, testdir): plugins = PytestPlugins() - sys.path.insert(0, str(self.tmpdir)) - try: - self.tmpdir.join("pytest_plug1.py").write("class Plug1: pass") - self.tmpdir.join("pytest_plug2.py").write("class Plug2: pass") - mod = py.std.new.module("temp") - mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] - plugins.consider_module(mod) - assert plugins.getplugin("plug1").__class__.__name__ == "Plug1" - assert plugins.getplugin("plug2").__class__.__name__ == "Plug2" - finally: - sys.path.remove(str(self.tmpdir)) - + py.test.raises(ImportError, 'plugins.import_plugin("x.y")') + py.test.raises(ImportError, 'plugins.import_plugin("pytest_x.y")') - def test_addpluginclass(self): - plugins = PytestPlugins() - class My: - pass - plugins.addpluginclass(My) - assert len(plugins._plugins) == 1 - plugins.addpluginclass(My) - assert len(plugins._plugins) == 1 + reset = testdir.syspathinsert() + pluginname = "pytest_hello" + testdir.makepyfile(**{pluginname: """ + class Hello: + pass + """}) + plugins.import_plugin("hello") + plugins.import_plugin("pytest_hello") + plugin = plugins.getplugin("hello") + + def test_consider_module(self, testdir): + plugins = PytestPlugins() + testdir.syspathinsert() + testdir.makepyfile(pytest_plug1="class Plug1: pass") + testdir.makepyfile(pytest_plug2="class Plug2: pass") + mod = py.std.new.module("temp") + mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] + plugins.consider_module(mod) + assert plugins.getplugin("plug1").__class__.__name__ == "Plug1" + assert plugins.getplugin("plug2").__class__.__name__ == "Plug2" + @py.test.keywords(xfail="getplugin() naming is unclear unconcept") def test_getplugin(self): plugins = PytestPlugins() assert py.test.raises(LookupError, "plugins.getplugin('_xxx')") class PluGin: pass - plugins.addpluginclass(PluGin) + plugins.register(PluGin()) myplugin1 = plugins.getplugin("plugin") myplugin2 = plugins.getplugin("Plugin") assert myplugin1 is myplugin2 class TestPluginInteraction: - disabled = True def test_getfirst(self): plugins = PytestPlugins() class My1: x = 1 assert plugins.getfirst("x") is None - plugins.addpluginclass(My1) + plugins.register(My1()) assert plugins.getfirst("x") == 1 - def test_call_plugins(self): + def test_call_each(self): plugins = PytestPlugins() class My: def method(self, arg): pass - plugins.addpluginclass(My) - py.test.raises(TypeError, 'plugins.call_plugins("method")') - py.test.raises(TypeError, 'plugins.call_plugins("method", 42)') - plugins.call_plugins("method", arg=42) - py.test.raises(TypeError, 'plugins.call_plugins("method", arg=42, s=13)') + plugins.register(My()) + py.test.raises(TypeError, 'plugins.call_each("method")') + l = plugins.call_each("method", arg=42) + assert l == [] + py.test.raises(TypeError, 'plugins.call_each("method", arg=42, s=13)') def test_call_firstresult(self): plugins = PytestPlugins() @@ -254,90 +199,41 @@ return None assert plugins.call_firstresult("method") is None assert plugins.call_firstresult("methodnotexists") is None - plugins.addpluginclass(My1) + plugins.register(My1()) assert plugins.call_firstresult("method") is None - plugins.addpluginclass(My2) + plugins.register(My2()) assert plugins.call_firstresult("method") == True - plugins.addpluginclass(My3) + plugins.register(My3()) assert plugins.call_firstresult("method") == True def test_listattr(self): plugins = PytestPlugins() class My2: x = 42 - plugins.addpluginclass(My2) + plugins.register(My2()) assert not plugins.listattr("hello") - assert plugins.listattr("x") == [('my2', 42)] - - def test_eventbus_interaction(self): - plugins = PytestPlugins() - l = [] - class My3: - def pytest_configure(self, config): - l.append("configure") - def pytest_unconfigure(self, config): - l.append("unconfigure") - plugins.addpluginclass(My3) - config = pytestConfig() - plugins.configure(config) - assert config.bus.issubscribed(plugins.forward_event) - plugins.unconfigure(config) - assert not config.bus.issubscribed(plugins.forward_event) - assert l == ['configure', 'unconfigure'] - - def test_pytest_event(self): - plugins = PytestPlugins() - l = [] - class My2: - def pytest_event(self, event): - l.append(event) - plugins.addpluginclass(My2) - ev = NOP() - plugins.forward_event(ev) - assert len(l) == 1 - assert l[0] is ev - - def test_addcmdlineoptions(self): - plugins = PytestPlugins() - class My: - pytest_cmdlineoptions = [ - py.test.config.Option("--hello", dest="dest", default=242) - ] - plugins.addpluginclass(My) - config = pytestConfig() - plugins.add_cmdlineoptions(config) - opt = config._parser.get_option("--hello") - assert opt - assert opt.default == 242 + assert plugins.listattr("x") == [42] - def test_setinitial_env(self): - plugins = PytestPlugins() - KEY = "PYTEST_PLUGINS" - old = os.environ.get(KEY, None) - try: - os.environ[KEY] = "test_setinitial" - py.test.raises(ImportError, "plugins.setinitial([])") - finally: - if old is None: - del os.environ[KEY] - else: - os.environ[KEY] = old - - def test_conftest_specifies_plugin(self, testdir): + @py.test.keywords(xfail="implement setupcall") + def test_call_setup_participants(self, testdir): testdir.makepyfile( conftest=""" import py - class MyPlugin: - pytest_cmdlineoptions = [ - py.test.config.Option("--myplugin-option", dest="myplugin", - help="myplugin option", - ) - ] - pytest_plugins = MyPlugin + def pytest_method(self, x): + return x+1 + pytest_plugin = "pytest_someplugin", """ ) - result = testdir.runpytest(testdir.tmpdir, '-h') - result.stdout.fnmatch_lines([ - "*--myplugin-option*", - ]) - + testdir.makepyfile(pytest_someplugin=""" + def pytest_method(self, x): + return x+1 + """) + modcol = testdir.getmodulecol(""" + def pytest_method(x): + return x+0 + """) + l = [] + call = modcol._config.pytestplugins.setupcall(modcol, "pytest_method", 1) + assert len(call.methods) == 3 + results = call.execute() + assert results == [1,2,2] From hpk at codespeak.net Tue Feb 24 15:11:38 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 24 Feb 2009 15:11:38 +0100 (CET) Subject: [py-svn] r62108 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090224141138.47143168442@codespeak.net> Author: hpk Date: Tue Feb 24 15:11:35 2009 New Revision: 62108 Modified: py/branch/pytestplugin/py/test/plugin/ (props changed) py/branch/pytestplugin/py/test/plugin/conftest.py py/branch/pytestplugin/py/test/plugin/pytest_apigen.py py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (contents, props changed) py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py py/branch/pytestplugin/py/test/plugin/pytest_unittest.py py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Log: * add "Plugin" to plugin class names. * a new "monkeypatch" plugin * fixeol Modified: py/branch/pytestplugin/py/test/plugin/conftest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/conftest.py (original) +++ py/branch/pytestplugin/py/test/plugin/conftest.py Tue Feb 24 15:11:35 2009 @@ -6,3 +6,10 @@ def consider_file(self, path): if path.basename.startswith("pytest_") and path.ext == ".py": return self.Module(path, parent=self) + +# XXX plugin +#class ConftestPlugin: +# def pytest_consider_file(self, path, parent): +# if path.basename.startswith("pytest_") and path.ext == ".py": +# return parent.Module(path, parent=parent) + Modified: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_apigen.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Tue Feb 24 15:11:35 2009 @@ -1,7 +1,6 @@ import py - -class Apigen: +class ApigenPlugin: pytest_cmdlineoptions = [ py.test.config.Option( '--apigen', @@ -50,7 +49,7 @@ terminalreporter.write_line("apigen build completed") def test_generic(plugintester): - plugintester.apicheck(Apigen) + plugintester.apicheck(ApigenPlugin) def test_simple(testdir): sub = testdir.tmpdir.mkdir("test_simple") Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Tue Feb 24 15:11:35 2009 @@ -1,15 +1,15 @@ import py -class Eventlog: - """ eventlog plugin for logging pytest events to a file. """ +class EventlogPlugin: + """ log pytest events to a file. """ pytest_cmdlineoptions = [ - py.test.config.Option('--eventlog', action='store', dest="eventlog", + py.test.config.Option('--eventlog', dest="eventlog", help="write all pytest events to a specific file") ] def pytest_configure(self, config): eventlog = config.option.eventlog if eventlog: - self.eventlogfile = py.path.local(eventlog).open("w") + self.eventlogfile = open(eventlog, 'w') def pytest_unconfigure(self, config): if hasattr(self, 'eventlogfile'): @@ -27,7 +27,7 @@ # =============================================================================== def test_generic(plugintester): - plugintester.apicheck(Eventlog) + plugintester.apicheck(EventlogPlugin) testdir = plugintester.testdir() testdir.makepyfile(""" Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Tue Feb 24 15:11:35 2009 @@ -3,7 +3,8 @@ """ import py -class Plugintester: +class PlugintesterPlugin: + """ test support code for testing pytest plugins. """ def pytest_pyfuncarg_plugintester(self, pyfuncitem): pt = PluginTester(pyfuncitem) pyfuncitem.addfinalizer(pt.finalize) @@ -140,4 +141,4 @@ # =============================================================================== def test_generic(plugintester): - plugintester.apicheck(Plugintester) + plugintester.apicheck(PlugintesterPlugin) Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Tue Feb 24 15:11:35 2009 @@ -8,7 +8,8 @@ xmlrpc = base + "/xmlrpc/" show = base + "/show/" -class Pocoo(object): +class PocooPlugin(object): + """ report URLs from sending test failures to the http://paste.pocoo.org service. """ pytest_cmdlineoptions = [ py.test.config.Option('--pocoo-sendfailures', action='store_true', dest="pocoo_sendfailures", @@ -36,10 +37,12 @@ terminalreporter.write_line("%s%s\n" % (url.show, id)) break + def test_apicheck(plugintester): - plugintester.apicheck(Pocoo) + plugintester.apicheck(PocooPlugin) -def test_toproxy(testdir): +pytest_plugins = 'pytest_monkeypatch', +def test_toproxy(testdir, monkeypatch): testdir.makepyfile(conftest="pytest_plugins='pytest_pocoo',") testpath = testdir.makepyfile(""" import py @@ -54,11 +57,6 @@ class MockProxy: def newPaste(self, language, code): l.append((language, code)) - - old = Pocoo.getproxy - Pocoo.getproxy = MockProxy - try: - result = testdir.inline_run(testpath, "--pocoo-sendfailures") - finally: - Pocoo.getproxy = old - assert len(l) == 1 + + monkeypatch.setattr(PocooPlugin, 'getproxy', MockProxy) + result = testdir.inline_run(testpath, "--pocoo-sendfailures") Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Tue Feb 24 15:11:35 2009 @@ -5,6 +5,24 @@ import py from py.__.test import event +class PytesterPlugin: + def pytest_pyfuncarg_linecomp(self, pyfuncitem): + return LineComp() + + def pytest_pyfuncarg_LineMatcher(self, pyfuncitem): + return LineMatcher + + def pytest_pyfuncarg_testdir(self, pyfuncitem): + tmptestdir = TmpTestdir(pyfuncitem) + pyfuncitem.addfinalizer(tmptestdir.finalize) + return tmptestdir + + def pytest_pyfuncarg_EventRecorder(self, pyfuncitem): + return EventRecorder + +def test_generic(plugintester): + plugintester.apicheck(PytesterPlugin) + class RunResult: def __init__(self, ret, outlines, errlines): self.ret = ret @@ -383,17 +401,3 @@ return extralines -class Pytester: - def pytest_pyfuncarg_linecomp(self, pyfuncitem): - return LineComp() - - def pytest_pyfuncarg_LineMatcher(self, pyfuncitem): - return LineMatcher - - def pytest_pyfuncarg_testdir(self, pyfuncitem): - tmptestdir = TmpTestdir(pyfuncitem) - pyfuncitem.addfinalizer(tmptestdir.finalize) - return tmptestdir - - def pytest_pyfuncarg_EventRecorder(self, pyfuncitem): - return EventRecorder Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Tue Feb 24 15:11:35 2009 @@ -1,6 +1,6 @@ import py -class Resultlog: +class ResultlogPlugin: """resultlog plugin for machine-readable logging of test results. Useful for buildbot integration code. """ @@ -232,7 +232,7 @@ assert 'ValueError' in entry def test_generic(plugintester, LineMatcher): - plugintester.apicheck(Resultlog) + plugintester.apicheck(ResultlogPlugin) testdir = plugintester.testdir() testdir.makepyfile(""" import py Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Tue Feb 24 15:11:35 2009 @@ -1,8 +1,8 @@ import py import sys -class Terminal(object): - """ Terminal Reporter plugin to write information about test run to terminal. """ +class TerminalPlugin(object): + """ Report a test run to a terminal. """ pytest_cmdlineoptions = [ py.test.config.Option('', '--collectonly', action="store_true", dest="collectonly", Modified: py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py Tue Feb 24 15:11:35 2009 @@ -9,9 +9,8 @@ """ import py -class Tmpdir: - """ pytest plugin for providing temporary directories - to test functions and methods. +class TmpdirPlugin: + """ provide temporary directories to test functions and methods. """ def pytest_pyfuncarg_tmpdir(self, pyfuncitem): basename = "_".join(pyfuncitem.listnames()) @@ -25,11 +24,11 @@ # =============================================================================== # def test_generic(plugintester): - plugintester.apicheck(Tmpdir) + plugintester.apicheck(TmpdirPlugin) def test_pyfuncarg(testdir): item = testdir.getitem("def test_func(tmpdir): pass") - tmpdir = Tmpdir() + tmpdir = TmpdirPlugin() p = tmpdir.pytest_pyfuncarg_tmpdir(item) assert p.check() assert p.basename.endswith("test_func") Modified: py/branch/pytestplugin/py/test/plugin/pytest_unittest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_unittest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_unittest.py Tue Feb 24 15:11:35 2009 @@ -15,8 +15,8 @@ """ import py -class Unittest: - """ Plugin for discovering and integrating traditional Python Unittest tests. +class UnittestPlugin: + """ discover and integrate traditional ``unittest.py`` tests. """ def pytest_pymodule_makeitem(self, modcol, name, obj): if py.std.inspect.isclass(obj) and issubclass(obj, py.std.unittest.TestCase): @@ -71,7 +71,7 @@ def test_generic(plugintester): - plugintester.apicheck(Unittest) + plugintester.apicheck(UnittestPlugin) def test_simple_unittest(testdir): testpath = testdir.makepyfile(""" Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Tue Feb 24 15:11:35 2009 @@ -1,5 +1,5 @@ """ -py.test xfail plugin for marking and reporting "expected to fail" tests. +for marking and reporting "expected to fail" tests. @py.test.keywords(xfail="needs refactoring") def test_hello(): ... @@ -7,7 +7,8 @@ """ import py -class Xfail(object): +class XfailPlugin(object): + """ mark and report specially about "expected to fail" tests. """ def pytest_report_teststatus(self, event): """ return shortletter and verbose word. """ if 'xfail' in event.keywords: @@ -43,7 +44,7 @@ # =============================================================================== def test_generic(plugintester): - plugintester.apicheck(Xfail) + plugintester.apicheck(XfailPlugin) def test_xfail(plugintester, linecomp): testdir = plugintester.testdir() From hpk at codespeak.net Tue Feb 24 15:22:57 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 24 Feb 2009 15:22:57 +0100 (CET) Subject: [py-svn] r62110 - in py/branch/pytestplugin/py/test: . testing Message-ID: <20090224142257.2A12C168457@codespeak.net> Author: hpk Date: Tue Feb 24 15:22:56 2009 New Revision: 62110 Modified: py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: fix tests, clean up getplugin() interface to work with import names. Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Tue Feb 24 15:22:56 2009 @@ -3,8 +3,6 @@ """ import py -# XXX rename PytestPlugins to PytestCom(...) -# class PytestPlugins(object): def __init__(self, pyplugins=None): if pyplugins is None: @@ -25,8 +23,9 @@ # API for bootstrapping # - def getplugin(self, pname): - return self._plugins[pname.lower()] + def getplugin(self, importname): + impname, clsname = canonical_names(importname) + return self._plugins[impname] def setinitial(self, modules): self.pyplugins.consider_env("PYTEST_PLUGINS") @@ -44,11 +43,11 @@ def import_plugin(self, spec): assert isinstance(spec, str) modname, clsname = canonical_names(spec) - if clsname.lower() in self._plugins: + if modname in self._plugins: return mod = importplugin(modname) plugin = registerplugin(self.pyplugins.register, mod, clsname) - self._plugins[clsname.lower()] = plugin + self._plugins[modname] = plugin self.consider_module(mod) # # @@ -102,7 +101,7 @@ modprefix = "pytest_" if not importspec.startswith(modprefix): importspec = modprefix + importspec - clsname = importspec[len(modprefix):].capitalize() + clsname = importspec[len(modprefix):].capitalize() + "Plugin" return importspec, clsname def registerplugin(registerfunc, mod, clsname): Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Tue Feb 24 15:22:56 2009 @@ -75,7 +75,7 @@ def test_consider_module_import_module(self, testdir, EventRecorder): mod = py.std.new.module("x") mod.pytest_plugins = "pytest_a" - aplugin = testdir.makepyfile(pytest_a="""class A: pass""") + aplugin = testdir.makepyfile(pytest_a="""class APlugin: pass""") plugins = PytestPlugins() evrec = EventRecorder(plugins) #syspath.prepend(aplugin.dirpath()) @@ -83,7 +83,7 @@ plugins.consider_module(mod) evlist = evrec.getnamed("plugin_registered") assert len(evlist) == 1 - assert evlist[0].__class__.__name__ == "A" + assert evlist[0].__class__.__name__ == "APlugin" # check that it is not registered twice plugins.consider_module(mod) @@ -95,7 +95,7 @@ for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz': impname, clsname = canonical_names(name) assert impname == "pytest_xyz" - assert clsname == "Xyz" + assert clsname == "XyzPlugin" def test_registerplugin(): l = [] @@ -137,23 +137,29 @@ reset = testdir.syspathinsert() pluginname = "pytest_hello" testdir.makepyfile(**{pluginname: """ - class Hello: + class HelloPlugin: pass """}) plugins.import_plugin("hello") + len1 = len(plugins.getplugins()) plugins.import_plugin("pytest_hello") - plugin = plugins.getplugin("hello") + len2 = len(plugins.getplugins()) + assert len1 == len2 + plugin1 = plugins.getplugin("pytest_hello") + assert plugin1.__class__.__name__ == 'HelloPlugin' + plugin2 = plugins.getplugin("hello") + assert plugin2 is plugin1 def test_consider_module(self, testdir): plugins = PytestPlugins() testdir.syspathinsert() - testdir.makepyfile(pytest_plug1="class Plug1: pass") - testdir.makepyfile(pytest_plug2="class Plug2: pass") + testdir.makepyfile(pytest_plug1="class Plug1Plugin: pass") + testdir.makepyfile(pytest_plug2="class Plug2Plugin: pass") mod = py.std.new.module("temp") mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] plugins.consider_module(mod) - assert plugins.getplugin("plug1").__class__.__name__ == "Plug1" - assert plugins.getplugin("plug2").__class__.__name__ == "Plug2" + assert plugins.getplugin("plug1").__class__.__name__ == "Plug1Plugin" + assert plugins.getplugin("plug2").__class__.__name__ == "Plug2Plugin" @py.test.keywords(xfail="getplugin() naming is unclear unconcept") def test_getplugin(self): From hpk at codespeak.net Tue Feb 24 15:26:24 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 24 Feb 2009 15:26:24 +0100 (CET) Subject: [py-svn] r62111 - in py/branch/pytestplugin/py/test: plugin testing Message-ID: <20090224142624.A5BEB168477@codespeak.net> Author: hpk Date: Tue Feb 24 15:26:24 2009 New Revision: 62111 Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: remove superflous test related to last checkin, and tweak xfail reporting Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Tue Feb 24 15:26:24 2009 @@ -27,7 +27,7 @@ entry = event.longrepr.reprcrash key = entry.path, entry.lineno, entry.message reason = event.longrepr.reprcrash.message - modpath = event.colitem.getmodpath(stopatmodule=False) + modpath = event.colitem.getmodpath(stopatmodule=True) #tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message)) tr._tw.line("%s %s:%d: " %(modpath, entry.path, entry.lineno)) Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Tue Feb 24 15:26:24 2009 @@ -161,17 +161,6 @@ assert plugins.getplugin("plug1").__class__.__name__ == "Plug1Plugin" assert plugins.getplugin("plug2").__class__.__name__ == "Plug2Plugin" - @py.test.keywords(xfail="getplugin() naming is unclear unconcept") - def test_getplugin(self): - plugins = PytestPlugins() - assert py.test.raises(LookupError, "plugins.getplugin('_xxx')") - - class PluGin: pass - plugins.register(PluGin()) - myplugin1 = plugins.getplugin("plugin") - myplugin2 = plugins.getplugin("Plugin") - assert myplugin1 is myplugin2 - class TestPluginInteraction: def test_getfirst(self): plugins = PytestPlugins() From hpk at codespeak.net Tue Feb 24 15:37:56 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 24 Feb 2009 15:37:56 +0100 (CET) Subject: [py-svn] r62112 - in py/branch/pytestplugin/py/test: . plugin Message-ID: <20090224143756.863B7168427@codespeak.net> Author: hpk Date: Tue Feb 24 15:37:55 2009 New Revision: 62112 Modified: py/branch/pytestplugin/py/test/defaultconftest.py py/branch/pytestplugin/py/test/plugin/pytest_xfail.py py/branch/pytestplugin/py/test/pycollect.py Log: enable monkeypatch plugin by default Modified: py/branch/pytestplugin/py/test/defaultconftest.py ============================================================================== --- py/branch/pytestplugin/py/test/defaultconftest.py (original) +++ py/branch/pytestplugin/py/test/defaultconftest.py Tue Feb 24 15:37:55 2009 @@ -10,7 +10,7 @@ conf_iocapture = "fd" # overridable from conftest.py -pytest_plugins = "terminal xfail".split() +pytest_plugins = "terminal xfail monkeypatch".split() # =================================================== # Distributed testing specific options Modified: py/branch/pytestplugin/py/test/plugin/pytest_xfail.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_xfail.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_xfail.py Tue Feb 24 15:37:55 2009 @@ -27,7 +27,7 @@ entry = event.longrepr.reprcrash key = entry.path, entry.lineno, entry.message reason = event.longrepr.reprcrash.message - modpath = event.colitem.getmodpath(stopatmodule=True) + modpath = event.colitem.getmodpath(includemodule=True) #tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message)) tr._tw.line("%s %s:%d: " %(modpath, entry.path, entry.lineno)) Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Tue Feb 24 15:37:55 2009 @@ -36,7 +36,7 @@ def _getobj(self): return getattr(self.parent.obj, self.name) - def getmodpath(self, stopatmodule=True): + def getmodpath(self, stopatmodule=True, includemodule=False): """ return python path relative to the containing module. """ chain = self.listchain() chain.reverse() @@ -46,10 +46,12 @@ continue name = node.name if isinstance(node, Module): - if stopatmodule: - break assert name.endswith(".py") name = name[:-3] + if stopatmodule: + if includemodule: + parts.append(name) + break parts.append(name) parts.reverse() s = ".".join(parts) From hpk at codespeak.net Tue Feb 24 16:09:40 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 24 Feb 2009 16:09:40 +0100 (CET) Subject: [py-svn] r62113 - in py/branch/pytestplugin/py/test: plugin testing Message-ID: <20090224150940.03AD41684C9@codespeak.net> Author: hpk Date: Tue Feb 24 16:09:39 2009 New Revision: 62113 Added: py/branch/pytestplugin/py/test/plugin/pytest_monkeypatch.py Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: * shuffle and remove duplicate pytestplugin tests * actually add the monkeypatch plugin Added: py/branch/pytestplugin/py/test/plugin/pytest_monkeypatch.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/plugin/pytest_monkeypatch.py Tue Feb 24 16:09:39 2009 @@ -0,0 +1,66 @@ +class MonkeypatchPlugin: + """ setattr-monkeypatching with automatical reversal after test. """ + def pytest_pyfuncarg_monkeypatch(self, pyfuncitem): + monkeypatch = MonkeyPatch() + pyfuncitem.addfinalizer(monkeypatch.finalize) + return monkeypatch + +notset = object() + +class MonkeyPatch: + def __init__(self): + self._setattr = [] + self._setitem = [] + + def setattr(self, obj, name, value): + self._setattr.insert(0, (obj, name, getattr(obj, name, notset))) + setattr(obj, name, value) + + def setitem(self, dictionary, name, value): + self._setitem.insert(0, (dictionary, name, dictionary.get(name, notset))) + dictionary[name] = value + + def finalize(self): + for obj, name, value in self._setattr: + if value is not notset: + setattr(obj, name, value) + for dictionary, name, value in self._setitem: + if value is notset: + del dictionary[name] + else: + dictionary[name] = value + + +def test_setattr(): + class A: + x = 1 + monkeypatch = MonkeyPatch() + monkeypatch.setattr(A, 'x', 2) + assert A.x == 2 + monkeypatch.setattr(A, 'x', 3) + assert A.x == 3 + monkeypatch.finalize() + assert A.x == 1 + +def test_setitem(): + d = {'x': 1} + monkeypatch = MonkeyPatch() + monkeypatch.setitem(d, 'x', 2) + monkeypatch.setitem(d, 'y', 1700) + assert d['x'] == 2 + assert d['y'] == 1700 + monkeypatch.setitem(d, 'x', 3) + assert d['x'] == 3 + monkeypatch.finalize() + assert d['x'] == 1 + assert 'y' not in d + +def test_monkeypatch_plugin(testdir): + sorter = testdir.inline_runsource(""" + pytest_plugins = 'pytest_monkeypatch', + def test_method(monkeypatch): + assert monkeypatch.__class__.__name__ == "MonkeyPatch" + """) + res = sorter.countoutcomes() + assert tuple(res) == (1, 0, 0), res + Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Tue Feb 24 16:09:39 2009 @@ -1,20 +1,103 @@ -import py +import py, os from py.__.test.pytestplugin import PytestPlugins, canonical_names from py.__.test.pytestplugin import registerplugin, importplugin -def test_considermodule(): - pp = PytestPlugins() - mod = py.std.new.module("hello") - mod.pytest_plugins = "helloworldmod_42" - py.test.raises(ImportError, "pp.consider_module(mod)") - -def test_setinitial(): - pp = PytestPlugins() - mod = py.std.new.module("hello") - mod.pytest_plugins = "helloworldmod_42" - py.test.raises(ImportError, "pp.setinitial([mod])") +class TestInitialization: + def test_setinitial(self): + pp = PytestPlugins() + mod = py.std.new.module("hello") + mod.pytest_plugins = "helloworldmod_42" + py.test.raises(ImportError, "pp.setinitial([mod])") -class TestPytestPlugins: + def test_setinitial_env(self, monkeypatch): + plugins = PytestPlugins() + monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'nonexistingmodule') + py.test.raises(ImportError, "plugins.setinitial([])") + + def test_import_plugin_importname(self, testdir): + plugins = PytestPlugins() + py.test.raises(ImportError, 'plugins.import_plugin("x.y")') + py.test.raises(ImportError, 'plugins.import_plugin("pytest_x.y")') + + reset = testdir.syspathinsert() + pluginname = "pytest_hello" + testdir.makepyfile(**{pluginname: """ + class HelloPlugin: + pass + """}) + plugins.import_plugin("hello") + len1 = len(plugins.getplugins()) + plugins.import_plugin("pytest_hello") + len2 = len(plugins.getplugins()) + assert len1 == len2 + plugin1 = plugins.getplugin("pytest_hello") + assert plugin1.__class__.__name__ == 'HelloPlugin' + plugin2 = plugins.getplugin("hello") + assert plugin2 is plugin1 + + def test_consider_module(self, testdir): + plugins = PytestPlugins() + testdir.syspathinsert() + testdir.makepyfile(pytest_plug1="class Plug1Plugin: pass") + testdir.makepyfile(pytest_plug2="class Plug2Plugin: pass") + mod = py.std.new.module("temp") + mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] + plugins.consider_module(mod) + assert plugins.getplugin("plug1").__class__.__name__ == "Plug1Plugin" + assert plugins.getplugin("plug2").__class__.__name__ == "Plug2Plugin" + + def test_consider_module_import_module(self, testdir, EventRecorder): + mod = py.std.new.module("x") + mod.pytest_plugins = "pytest_a" + aplugin = testdir.makepyfile(pytest_a="""class APlugin: pass""") + plugins = PytestPlugins() + evrec = EventRecorder(plugins) + #syspath.prepend(aplugin.dirpath()) + py.std.sys.path.insert(0, str(aplugin.dirpath())) + plugins.consider_module(mod) + evlist = evrec.getnamed("plugin_registered") + assert len(evlist) == 1 + assert evlist[0].__class__.__name__ == "APlugin" + + # check that it is not registered twice + plugins.consider_module(mod) + evlist = evrec.getnamed("plugin_registered") + assert len(evlist) == 1 + + def test_registry(self): + pp = PytestPlugins() + a1, a2 = object(), object() + pp.register(a1) + assert pp.isregistered(a1) + pp.register(a2) + assert pp.isregistered(a2) + assert pp.getplugins() == [a1, a2] + pp.unregister(a1) + assert not pp.isregistered(a1) + pp.unregister(a2) + assert not pp.isregistered(a2) + + def test_canonical_names(self): + for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz': + impname, clsname = canonical_names(name) + assert impname == "pytest_xyz" + assert clsname == "XyzPlugin" + + def test_registerplugin(self): + l = [] + registerfunc = l.append + registerplugin(registerfunc, py.io, "TerminalWriter") + assert len(l) == 1 + assert isinstance(l[0], py.io.TerminalWriter) + + def test_importplugin(self): + assert importplugin("py") == py + py.test.raises(ImportError, "importplugin('laksjd.qwe')") + mod = importplugin("pytest_terminal") + assert mod is py.__.test.plugin.pytest_terminal + + +class TestPytestPluginInteractions: def test_configure(self, testdir): config = testdir.parseconfig() l = [] @@ -59,107 +142,7 @@ pp = PytestPlugins() assert hasattr(pp, 'MultiCall') - def test_registry(self): - pp = PytestPlugins() - a1, a2 = object(), object() - pp.register(a1) - assert pp.isregistered(a1) - pp.register(a2) - assert pp.isregistered(a2) - assert pp.getplugins() == [a1, a2] - pp.unregister(a1) - assert not pp.isregistered(a1) - pp.unregister(a2) - assert not pp.isregistered(a2) - - def test_consider_module_import_module(self, testdir, EventRecorder): - mod = py.std.new.module("x") - mod.pytest_plugins = "pytest_a" - aplugin = testdir.makepyfile(pytest_a="""class APlugin: pass""") - plugins = PytestPlugins() - evrec = EventRecorder(plugins) - #syspath.prepend(aplugin.dirpath()) - py.std.sys.path.insert(0, str(aplugin.dirpath())) - plugins.consider_module(mod) - evlist = evrec.getnamed("plugin_registered") - assert len(evlist) == 1 - assert evlist[0].__class__.__name__ == "APlugin" - - # check that it is not registered twice - plugins.consider_module(mod) - evlist = evrec.getnamed("plugin_registered") - assert len(evlist) == 1 - - -def test_canonical_names(): - for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz': - impname, clsname = canonical_names(name) - assert impname == "pytest_xyz" - assert clsname == "XyzPlugin" - -def test_registerplugin(): - l = [] - registerfunc = l.append - registerplugin(registerfunc, py.io, "TerminalWriter") - assert len(l) == 1 - assert isinstance(l[0], py.io.TerminalWriter) - -def test_importplugin(): - assert importplugin("py") == py - py.test.raises(ImportError, "importplugin('laksjd.qwe')") - mod = importplugin("pytest_terminal") - assert mod is py.__.test.plugin.pytest_terminal - -import os, sys -import py -from py.__.test.event import NOP -from py.__.test.config import Config as pytestConfig -class TestInitialization: - def test_setinitial_env(self): - plugins = PytestPlugins() - KEY = "PYTEST_PLUGINS" - old = os.environ.get(KEY, None) - try: - os.environ[KEY] = "test_setinitial" - py.test.raises(ImportError, "plugins.setinitial([])") - finally: - if old is None: - del os.environ[KEY] - else: - os.environ[KEY] = old - - def test_import_plugin_importname(self, testdir): - plugins = PytestPlugins() - py.test.raises(ImportError, 'plugins.import_plugin("x.y")') - py.test.raises(ImportError, 'plugins.import_plugin("pytest_x.y")') - - reset = testdir.syspathinsert() - pluginname = "pytest_hello" - testdir.makepyfile(**{pluginname: """ - class HelloPlugin: - pass - """}) - plugins.import_plugin("hello") - len1 = len(plugins.getplugins()) - plugins.import_plugin("pytest_hello") - len2 = len(plugins.getplugins()) - assert len1 == len2 - plugin1 = plugins.getplugin("pytest_hello") - assert plugin1.__class__.__name__ == 'HelloPlugin' - plugin2 = plugins.getplugin("hello") - assert plugin2 is plugin1 - - def test_consider_module(self, testdir): - plugins = PytestPlugins() - testdir.syspathinsert() - testdir.makepyfile(pytest_plug1="class Plug1Plugin: pass") - testdir.makepyfile(pytest_plug2="class Plug2Plugin: pass") - mod = py.std.new.module("temp") - mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] - plugins.consider_module(mod) - assert plugins.getplugin("plug1").__class__.__name__ == "Plug1Plugin" - assert plugins.getplugin("plug2").__class__.__name__ == "Plug2Plugin" class TestPluginInteraction: def test_getfirst(self): From hpk at codespeak.net Tue Feb 24 17:06:13 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 24 Feb 2009 17:06:13 +0100 (CET) Subject: [py-svn] r62117 - in py/branch/pytestplugin/py: . misc/testing test test/testing Message-ID: <20090224160613.471A21684EE@codespeak.net> Author: hpk Date: Tue Feb 24 17:06:11 2009 New Revision: 62117 Modified: py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/misc/testing/test_com.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: PYTEST_PLUGINS environment variable is now considered for startup Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Tue Feb 24 17:06:11 2009 @@ -55,21 +55,23 @@ self.notify("importingmodule", modpath) __import__(modpath) - def consider_env(self, varname="PYLIB"): + def consider_env(self): """ consider ENV variable for loading modules. """ + for spec in self._envlist("PYLIB"): + self.import_module(spec) + + def _envlist(self, varname): val = py.std.os.environ.get(varname, None) - self._consider(val) + if val is not None: + return val.split(',') + return () def consider_module(self, mod, varname="pylib"): - speclist = getattr(mod, varname, None) - self._consider(speclist) - - def _consider(self, speclist): - if speclist is not None: - if not isinstance(speclist, (list, tuple)): - speclist = (speclist,) - for spec in speclist: - self.import_module(spec) + speclist = getattr(mod, varname, ()) + if not isinstance(speclist, (list, tuple)): + speclist = (speclist,) + for spec in speclist: + self.import_module(spec) def register(self, plugin): assert not isinstance(plugin, str) Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Tue Feb 24 17:06:11 2009 @@ -152,19 +152,11 @@ plugins.notify('hello') assert l == ["hellospecific", "helloanonymous"] - def test_consider_env(self): + def test_consider_env(self, monkeypatch): # XXX write a helper for preserving os.environ plugins = PyPlugins() - KEY = "XXPYLIB" - old = os.environ.get(KEY, None) - try: - os.environ[KEY] = "unknownconsider_env" - py.test.raises(ImportError, "plugins.consider_env(KEY)") - finally: - if old is None: - del os.environ[KEY] - else: - os.environ[KEY] = old + monkeypatch.setitem(os.environ, 'PYLIB', "unknownconsider_env") + py.test.raises(ImportError, "plugins.consider_env()") def test_consider_module(self): plugins = PyPlugins() Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Tue Feb 24 17:06:11 2009 @@ -49,6 +49,7 @@ self._initialized = True adddefaultoptions(self) self._conftest.setinitial(args) + self.pytestplugins.consider_env() #print "parse, seeing pytestplugins", self.pytestplugins.pyplugins #self.pytestplugins.setinitial(self._conftest.getconftestmodules(None)) #print "plugins", self.pytestplugins.pyplugins.list Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Tue Feb 24 17:06:11 2009 @@ -28,9 +28,12 @@ return self._plugins[impname] def setinitial(self, modules): - self.pyplugins.consider_env("PYTEST_PLUGINS") for module in modules: self.consider_module(module) + + def consider_env(self): + for spec in self.pyplugins._envlist("PYTEST_PLUGINS"): + self.import_plugin(spec) def consider_module(self, mod): attr = getattr(mod, "pytest_plugins", ()) Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Tue Feb 24 17:06:11 2009 @@ -2,17 +2,49 @@ from py.__.test.pytestplugin import PytestPlugins, canonical_names from py.__.test.pytestplugin import registerplugin, importplugin -class TestInitialization: +class TestBootstrapping: + def test_consider_env_fails_to_import(self, monkeypatch): + plugins = PytestPlugins() + monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'nonexistingmodule') + py.test.raises(ImportError, "plugins.consider_env()") + + def test_consider_env_plugin_instantiation(self, testdir, monkeypatch): + plugins = PytestPlugins() + testdir.syspathinsert() + testdir.makepyfile(pytest_xy123="class Xy123Plugin: pass") + monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'xy123') + l1 = len(plugins.getplugins()) + plugins.consider_env() + l2 = len(plugins.getplugins()) + assert l2 == l1 + 1 + assert plugins.getplugin('pytest_xy123') + plugins.consider_env() + l3 = len(plugins.getplugins()) + assert l2 == l3 + + def test_pytestplugin_environment_variable(self, testdir, monkeypatch): + testdir.makepyfile(pytest_x500="class X500Plugin: pass") + p = testdir.makepyfile(""" + import py + def test_hello(): + plugin = py.test.config.pytestplugins.getplugin('x500') + assert plugin is not None + """) + testdir.syspathinsert() + monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'pytest_x500') + result = testdir.runpytest(p) + assert result.ret == 0 + extra = result.stdout.fnmatch_lines(["*1 passed in*"]) + def test_setinitial(self): pp = PytestPlugins() mod = py.std.new.module("hello") mod.pytest_plugins = "helloworldmod_42" py.test.raises(ImportError, "pp.setinitial([mod])") - - def test_setinitial_env(self, monkeypatch): - plugins = PytestPlugins() - monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'nonexistingmodule') - py.test.raises(ImportError, "plugins.setinitial([])") + + @py.test.keywords(xfail=True) + def test_setinitial_conftest_plugins(self, testdir): + assert 0 def test_import_plugin_importname(self, testdir): plugins = PytestPlugins() From hpk at codespeak.net Tue Feb 24 17:49:48 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 24 Feb 2009 17:49:48 +0100 (CET) Subject: [py-svn] r62119 - py/branch/pytestplugin/py/execnet/testing Message-ID: <20090224164948.462151684BC@codespeak.net> Author: hpk Date: Tue Feb 24 17:49:47 2009 New Revision: 62119 Modified: py/branch/pytestplugin/py/execnet/testing/test_gateway.py Log: fix a test function that already had args - although py.test could also learn more about default values of functions. Modified: py/branch/pytestplugin/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/pytestplugin/py/execnet/testing/test_gateway.py (original) +++ py/branch/pytestplugin/py/execnet/testing/test_gateway.py Tue Feb 24 17:49:47 2009 @@ -247,7 +247,10 @@ channel.waitclose(TESTTIMEOUT) assert l == [42] - def test_channel_callback_stays_active(self, earlyfree=True): + def test_channel_callback_stays_active(self): + self.check_channel_callback_stays_active(earlyfree=True) + + def check_channel_callback_stays_active(self, earlyfree=True): # with 'earlyfree==True', this tests the "sendonly" channel state. l = [] channel = self.gw.remote_exec(source=''' @@ -278,7 +281,7 @@ return subchannel def test_channel_callback_remote_freed(self): - channel = self.test_channel_callback_stays_active(False) + channel = self.check_channel_callback_stays_active(earlyfree=False) channel.waitclose(TESTTIMEOUT) # freed automatically at the end of producer() def test_channel_endmarker_callback(self): From hpk at codespeak.net Tue Feb 24 20:07:31 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 24 Feb 2009 20:07:31 +0100 (CET) Subject: [py-svn] r62122 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090224190731.376FE1684E6@codespeak.net> Author: hpk Date: Tue Feb 24 20:07:28 2009 New Revision: 62122 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/testing/test_conftesthandle.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: refined plugin/conftest option parsing interface Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Tue Feb 24 20:07:28 2009 @@ -38,7 +38,7 @@ assert isinstance(pytestplugins, py.test._PytestPlugins) self.bus = pytestplugins.pyplugins self.pytestplugins = pytestplugins - self._conftest = Conftest(onimport=self.pytestplugins.consider_module) + self._conftest = Conftest(onimport=self.pytestplugins.consider_conftest) def parse(self, args): """ parse cmdline arguments into this config object. @@ -145,6 +145,9 @@ ) return self._addoptions(groupname, *specs) + def addoption(self, *args, **kwargs): + return self.addoptions("misc", self.Option(*args, **kwargs)) + def _addoptions(self, groupname, *specs): optgroup = optparse.OptionGroup(self._parser, groupname) optgroup.add_options(specs) Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Tue Feb 24 20:07:28 2009 @@ -2,10 +2,15 @@ class EventlogPlugin: """ log pytest events to a file. """ + def pytest_option(self, config): + config.addoption("--eventlog", dest="eventlog", + help="write all pytest events to the given file.") + pytest_cmdlineoptions = [ py.test.config.Option('--eventlog', dest="eventlog", help="write all pytest events to a specific file") ] + def pytest_configure(self, config): eventlog = config.option.eventlog if eventlog: Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Tue Feb 24 20:07:28 2009 @@ -96,6 +96,9 @@ pytest_cmdlineoptions = [] + def pytest_option(self, config): + """ called before commandline parsing. """ + def pytest_configure(self, config): """ called after command line options have been parsed. and all plugins and initial conftest files been loaded. Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Tue Feb 24 20:07:28 2009 @@ -164,7 +164,13 @@ rev = py.__pkg__.getrev() self.write_line("using py lib: %s " % ( py.path.local(py.__file__).dirpath(), rev)) - plugins = ", ".join(list(self.config.pytestplugins._plugins)) + plugins = [] + for x in self.config.pytestplugins._plugins: + if isinstance(x, str) and x.startswith("pytest_"): + plugins.append(x[7:]) + else: + plugins.append(str(x)) # XXX display conftest plugins more nicely + plugins = ", ".join(plugins) self.write_line("active plugins: %s" %(plugins,)) for i, testarg in py.builtin.enumerate(self.config.args): self.write_line("test object %d: %s" %(i+1, testarg)) Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Tue Feb 24 20:07:28 2009 @@ -27,14 +27,17 @@ impname, clsname = canonical_names(importname) return self._plugins[impname] - def setinitial(self, modules): - for module in modules: - self.consider_module(module) - def consider_env(self): for spec in self.pyplugins._envlist("PYTEST_PLUGINS"): self.import_plugin(spec) + def consider_conftest(self, conftestmodule): + cls = getattr(conftestmodule, 'ConftestPlugin', None) + if cls is not None and cls not in self._plugins: + self._plugins[cls] = True + self.register(cls()) + self.consider_module(conftestmodule) + def consider_module(self, mod): attr = getattr(mod, "pytest_plugins", ()) if attr: @@ -73,6 +76,9 @@ def notify(self, eventname, *args, **kwargs): return self.pyplugins.notify(eventname, *args, **kwargs) + def do_option(self, config): + self.pyplugins.call_each('pytest_option', config=config) + def add_cmdlineoptions(self, config): # XXX think about sorting/grouping of options from user-perspective #assert self.pyplugins.list Modified: py/branch/pytestplugin/py/test/testing/test_conftesthandle.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_conftesthandle.py (original) +++ py/branch/pytestplugin/py/test/testing/test_conftesthandle.py Tue Feb 24 20:07:28 2009 @@ -24,7 +24,6 @@ assert conftest.rget("a") == 1 assert conftest.rget("b", self.basedir.join("adir", "b")) == 2 assert len(l) == 3 - def test_immediate_initialiation_and_incremental_are_the_same(self): conftest = Conftest() Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Tue Feb 24 20:07:28 2009 @@ -22,7 +22,7 @@ l3 = len(plugins.getplugins()) assert l2 == l3 - def test_pytestplugin_environment_variable(self, testdir, monkeypatch): + def test_pytestplugin_ENV_startup(self, testdir, monkeypatch): testdir.makepyfile(pytest_x500="class X500Plugin: pass") p = testdir.makepyfile(""" import py @@ -36,16 +36,6 @@ assert result.ret == 0 extra = result.stdout.fnmatch_lines(["*1 passed in*"]) - def test_setinitial(self): - pp = PytestPlugins() - mod = py.std.new.module("hello") - mod.pytest_plugins = "helloworldmod_42" - py.test.raises(ImportError, "pp.setinitial([mod])") - - @py.test.keywords(xfail=True) - def test_setinitial_conftest_plugins(self, testdir): - assert 0 - def test_import_plugin_importname(self, testdir): plugins = PytestPlugins() py.test.raises(ImportError, 'plugins.import_plugin("x.y")') @@ -96,6 +86,27 @@ evlist = evrec.getnamed("plugin_registered") assert len(evlist) == 1 + def test_consider_conftest(self, testdir): + pp = PytestPlugins() + mod = testdir.makepyfile("class ConftestPlugin: hello = 1").pyimport() + pp.consider_conftest(mod) + l = [x for x in pp.getplugins() if isinstance(x, mod.ConftestPlugin)] + assert len(l) == 1 + assert l[0].hello == 1 + + pp.consider_conftest(mod) + l = [x for x in pp.getplugins() if isinstance(x, mod.ConftestPlugin)] + assert len(l) == 1 + + def test_config_sets_conftesthandle_onimport(self, testdir): + config = testdir.parseconfig([]) + assert config._conftest._onimport == config.pytestplugins.consider_conftest + + def test_consider_conftest_deps(self, testdir): + mod = testdir.makepyfile("pytest_plugins='xyz'").pyimport() + pp = PytestPlugins() + py.test.raises(ImportError, "pp.consider_conftest(mod)") + def test_registry(self): pp = PytestPlugins() a1, a2 = object(), object() @@ -130,6 +141,20 @@ class TestPytestPluginInteractions: + def test_do_option_conftestplugin(self, testdir): + from py.__.test.config import Config + p = testdir.makepyfile(""" + class ConftestPlugin: + def pytest_option(self, config): + config.addoption('--test123', action="store_true") + """) + config = Config() + config._conftest.importconftest(p) + print config.pytestplugins.getplugins() + config.pytestplugins.do_option(config=config) + config.parse([]) + assert not config.option.test123 + def test_configure(self, testdir): config = testdir.parseconfig() l = [] @@ -156,27 +181,12 @@ config.bus.register(A()) assert len(l) == 2 - def test_addcmdlineoptions(self): - class PseudoConfig: - opts = [] - def addoptions(self, *opts): - self.opts.append(opts) - config = PseudoConfig() - pc = PytestPlugins() - class A: - pytest_cmdlineoptions = [42] - pc.pyplugins.register(A) - pc.add_cmdlineoptions(config) - assert len(config.opts) == 1 - assert config.opts[0][1] == 42 - def test_MultiCall(self): pp = PytestPlugins() assert hasattr(pp, 'MultiCall') + # lower level API - -class TestPluginInteraction: def test_getfirst(self): plugins = PytestPlugins() class My1: From py-svn at codespeak.net Wed Feb 25 09:49:03 2009 From: py-svn at codespeak.net (py-svn at codespeak.net) Date: Wed, 25 Feb 2009 09:49:03 +0100 (CET) Subject: [py-svn] Pharmacy Message 206203 Message-ID: <20090225084903.C894E16841E@codespeak.net> An HTML attachment was scrubbed... URL: From hpk at codespeak.net Wed Feb 25 11:35:21 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 25 Feb 2009 11:35:21 +0100 (CET) Subject: [py-svn] r62133 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090225103521.CB1FE168419@codespeak.net> Author: hpk Date: Wed Feb 25 11:35:21 2009 New Revision: 62133 Modified: py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/plugin/pytest_apigen.py py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: normalizing for "pytest_addoption" hook. Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Wed Feb 25 11:35:21 2009 @@ -53,7 +53,7 @@ #print "parse, seeing pytestplugins", self.pytestplugins.pyplugins #self.pytestplugins.setinitial(self._conftest.getconftestmodules(None)) #print "plugins", self.pytestplugins.pyplugins.list - self.pytestplugins.add_cmdlineoptions(self) + self.pytestplugins.do_addoption(self) args = [str(x) for x in args] cmdlineoption, args = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) Modified: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_apigen.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Wed Feb 25 11:35:21 2009 @@ -1,13 +1,10 @@ import py class ApigenPlugin: - pytest_cmdlineoptions = [ - py.test.config.Option( - '--apigen', - action="store_true", dest="apigen", - help="generate api documentation") + def pytest_addoption(self, config): + config.addoption('--apigen', action="store_true", dest="apigen", + help="generate api documentation") # "--apigen-script" argument pointing to a script"), - ] def pytest_configure(self, config): if config.option.apigen: Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Wed Feb 25 11:35:21 2009 @@ -2,15 +2,10 @@ class EventlogPlugin: """ log pytest events to a file. """ - def pytest_option(self, config): + def pytest_addoption(self, config): config.addoption("--eventlog", dest="eventlog", help="write all pytest events to the given file.") - pytest_cmdlineoptions = [ - py.test.config.Option('--eventlog', dest="eventlog", - help="write all pytest events to a specific file") - ] - def pytest_configure(self, config): eventlog = config.option.eventlog if eventlog: Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Wed Feb 25 11:35:21 2009 @@ -94,9 +94,7 @@ def __init__(self): """ usually called only once per test process. """ - pytest_cmdlineoptions = [] - - def pytest_option(self, config): + def pytest_addoption(self, config): """ called before commandline parsing. """ def pytest_configure(self, config): Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Wed Feb 25 11:35:21 2009 @@ -10,11 +10,11 @@ class PocooPlugin(object): """ report URLs from sending test failures to the http://paste.pocoo.org service. """ - pytest_cmdlineoptions = [ - py.test.config.Option('--pocoo-sendfailures', + + def pytest_addoption(self, config): + config.addoption('--pocoo-sendfailures', action='store_true', dest="pocoo_sendfailures", help="send failures to %s" %(url.base,)) - ] def getproxy(self): return py.std.xmlrpclib.ServerProxy(url.xmlrpc).pastes Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Wed Feb 25 11:35:21 2009 @@ -4,10 +4,10 @@ """resultlog plugin for machine-readable logging of test results. Useful for buildbot integration code. """ - pytest_cmdlineoptions = [ - py.test.config.Option('--resultlog', action="store", dest="resultlog", default=None, + def pytest_addoption(self, config): + config.addoption('--resultlog', action="store", dest="resultlog", help="path for machine-readable result log") - ] + def pytest_configure(self, config): resultlog = config.option.resultlog if resultlog: Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Wed Feb 25 11:35:21 2009 @@ -3,12 +3,11 @@ class TerminalPlugin(object): """ Report a test run to a terminal. """ - pytest_cmdlineoptions = [ - py.test.config.Option('', '--collectonly', + def pytest_addoption(self, config): + config.addoption('--collectonly', action="store_true", dest="collectonly", - default=False, help="only collect tests, don't execute them."), - ] + def pytest_configure(self, config): if config.option.collectonly: self.reporter = CollectonlyReporter(config) Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Wed Feb 25 11:35:21 2009 @@ -76,16 +76,8 @@ def notify(self, eventname, *args, **kwargs): return self.pyplugins.notify(eventname, *args, **kwargs) - def do_option(self, config): - self.pyplugins.call_each('pytest_option', config=config) - - def add_cmdlineoptions(self, config): - # XXX think about sorting/grouping of options from user-perspective - #assert self.pyplugins.list - opts = [] - for options in self.pyplugins.listattr("pytest_cmdlineoptions"): - opts.extend(options) - config.addoptions("ungrouped options added by plugins", *opts) + def do_addoption(self, config): + self.pyplugins.call_each('pytest_addoption', config=config) def pyevent_plugin_registered(self, plugin): if hasattr(self, '_config'): Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Wed Feb 25 11:35:21 2009 @@ -145,13 +145,12 @@ from py.__.test.config import Config p = testdir.makepyfile(""" class ConftestPlugin: - def pytest_option(self, config): + def pytest_addoption(self, config): config.addoption('--test123', action="store_true") """) config = Config() config._conftest.importconftest(p) print config.pytestplugins.getplugins() - config.pytestplugins.do_option(config=config) config.parse([]) assert not config.option.test123 From hpk at codespeak.net Wed Feb 25 12:54:19 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 25 Feb 2009 12:54:19 +0100 (CET) Subject: [py-svn] r62134 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090225115419.C74EA168409@codespeak.net> Author: hpk Date: Wed Feb 25 12:54:15 2009 New Revision: 62134 Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Log: small adjustments Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Wed Feb 25 12:54:15 2009 @@ -7,7 +7,7 @@ help="write all pytest events to the given file.") def pytest_configure(self, config): - eventlog = config.option.eventlog + eventlog = config.getvalue("eventlog") if eventlog: self.eventlogfile = open(eventlog, 'w') Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Wed Feb 25 12:54:15 2009 @@ -9,7 +9,7 @@ show = base + "/show/" class PocooPlugin(object): - """ report URLs from sending test failures to the http://paste.pocoo.org service. """ + """ report URLs from sending test failures to the pocoo paste service. """ def pytest_addoption(self, config): config.addoption('--pocoo-sendfailures', From py-svn at codespeak.net Wed Feb 25 17:35:10 2009 From: py-svn at codespeak.net (py-svn at codespeak.net) Date: Wed, 25 Feb 2009 17:35:10 +0100 (CET) Subject: [py-svn] Great sale for today Message-ID: <20090225163510.C75DE1684EE@codespeak.net> An HTML attachment was scrubbed... URL: From hpk at codespeak.net Wed Feb 25 22:59:52 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 25 Feb 2009 22:59:52 +0100 (CET) Subject: [py-svn] r62165 - in py/branch/pytestplugin/py: . test test/plugin test/testing Message-ID: <20090225215952.59332168507@codespeak.net> Author: hpk Date: Wed Feb 25 22:59:49 2009 New Revision: 62165 Modified: py/branch/pytestplugin/py/__init__.py py/branch/pytestplugin/py/test/collect.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/defaultconftest.py py/branch/pytestplugin/py/test/plugin/conftest.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py py/branch/pytestplugin/py/test/pycollect.py py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/session.py py/branch/pytestplugin/py/test/testing/acceptance_test.py py/branch/pytestplugin/py/test/testing/test_collect.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_doctest.py py/branch/pytestplugin/py/test/testing/test_genitems.py Log: * introduce pytest_collect_file hook which may return an arbitrary collection item or None * move doctests and all their tests into their own test * various tweaks in the course, most importantly the introduction of the slightly obnoxious collect._getfsnode() helper * strike py.test.collect.DoctestFile for now Modified: py/branch/pytestplugin/py/__init__.py ============================================================================== --- py/branch/pytestplugin/py/__init__.py (original) +++ py/branch/pytestplugin/py/__init__.py Wed Feb 25 22:59:49 2009 @@ -92,7 +92,6 @@ 'test.collect.File' : ('./test/collect.py', 'File'), 'test.collect.Item' : ('./test/collect.py', 'Item'), 'test.collect.Module' : ('./test/pycollect.py', 'Module'), - 'test.collect.DoctestFile' : ('./test/pycollect.py', 'DoctestFile'), 'test.collect.Class' : ('./test/pycollect.py', 'Class'), 'test.collect.Instance' : ('./test/pycollect.py', 'Instance'), 'test.collect.Generator' : ('./test/pycollect.py', 'Generator'), Modified: py/branch/pytestplugin/py/test/collect.py ============================================================================== --- py/branch/pytestplugin/py/test/collect.py (original) +++ py/branch/pytestplugin/py/test/collect.py Wed Feb 25 22:59:49 2009 @@ -53,7 +53,6 @@ col = self.stack.pop() col.teardown() for col in needed_collectors[len(self.stack):]: - #print "setting up", col col.setup() self.stack.append(col) @@ -108,6 +107,7 @@ self._config = config self.fspath = getattr(parent, 'fspath', None) + # # note to myself: Pickling is uh. # @@ -202,6 +202,50 @@ cur = next return cur + + def _getfsnode(self, path): + # this method is usually called from + # config.getfsnode() which returns a colitem + # from filename arguments + # + # pytest's collector tree does not neccessarily + # follow the filesystem and we thus need to do + # some special matching code here because + # _getitembynames() works by colitem names, not + # basenames. + if path == self.fspath: + return self + basenames = path.relto(self.fspath).split(path.sep) + cur = self + while basenames: + basename = basenames.pop(0) + assert basename + fspath = cur.fspath.join(basename) + colitems = cur._memocollect() + l = [] + for colitem in colitems: + if colitem.fspath == fspath or colitem.name == basename: + l.append(colitem) + if not l: + msg = ("Collector %r does not provide %r colitem " + "existing colitems are: %s" % + (cur, fspath, colitems)) + raise AssertionError(msg) + if basenames: + if len(l) > 1: + msg = ("Collector %r has more than one %r colitem " + "existing colitems are: %s" % + (cur, fspath, colitems)) + raise AssertionError(msg) + cur = l[0] + else: + if len(l) > 1: + cur = l + else: + cur = l[0] + break + return cur + def readkeywords(self): return dict([(x, True) for x in self._keywords()]) @@ -302,7 +346,7 @@ """ Directory = configproperty('Directory') Module = configproperty('Module') - DoctestFile = configproperty('DoctestFile') + #DoctestFile = configproperty('DoctestFile') def collect(self): """ returns a list of children (items and collectors) @@ -413,7 +457,10 @@ for path in self.fspath.listdir(sort=True): res = self.consider(path) if res is not None: - l.append(res) + if isinstance(res, (list, tuple)): + l.extend(res) + else: + l.append(res) return l def consider(self, path): @@ -423,14 +470,14 @@ return self.consider_dir(path) def consider_file(self, path): - ext = path.ext - pb = path.purebasename - if pb.startswith("test_") or pb.endswith("_test") or \ - path in self._config.args: - if ext == ".py": - return self.Module(path, parent=self) - elif ext == ".txt": - return self.DoctestFile(path, parent=self) + res = self._config.pytestplugins.call_each( + 'pytest_collect_file', path=path, parent=self) + l = [] + # throw out identical modules + for x in res: + if x not in l: + l.append(x) + return l def consider_dir(self, path): if not self.recfilter(path): @@ -446,14 +493,6 @@ Directory = self._config.getvalue('Directory', path) return Directory(path, parent=self) - def collect_by_name(self, name): - """ get a child with the given name. """ - res = super(Directory, self).collect_by_name(name) - if res is None: - p = self.fspath.join(name) - res = self.consider(p) - return res - from py.__.test.runner import basic_run_report, forked_run_report class Item(Node): """ a basic test item. """ Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Wed Feb 25 22:59:49 2009 @@ -48,11 +48,8 @@ "can only parse cmdline args at most once per Config object") self._initialized = True adddefaultoptions(self) - self._conftest.setinitial(args) + self._conftest.setinitial(args) self.pytestplugins.consider_env() - #print "parse, seeing pytestplugins", self.pytestplugins.pyplugins - #self.pytestplugins.setinitial(self._conftest.getconftestmodules(None)) - #print "plugins", self.pytestplugins.pyplugins.list self.pytestplugins.do_addoption(self) args = [str(x) for x in args] cmdlineoption, args = self._parser.parse_args(args) @@ -113,8 +110,7 @@ pkgpath = path.check(file=1) and path.dirpath() or path Dir = self._conftest.rget("Directory", pkgpath) col = Dir(pkgpath, config=self) - names = path.relto(col.fspath).split(path.sep) - return col._getitembynames(names) + return col._getfsnode(path) def getvalue_pathlist(self, name, path=None): """ return a matching value, which needs to be sequence Modified: py/branch/pytestplugin/py/test/defaultconftest.py ============================================================================== --- py/branch/pytestplugin/py/test/defaultconftest.py (original) +++ py/branch/pytestplugin/py/test/defaultconftest.py Wed Feb 25 22:59:49 2009 @@ -1,7 +1,7 @@ import py Module = py.test.collect.Module -DoctestFile = py.test.collect.DoctestFile +#DoctestFile = py.test.collect.DoctestFile Directory = py.test.collect.Directory Class = py.test.collect.Class Generator = py.test.collect.Generator @@ -10,7 +10,10 @@ conf_iocapture = "fd" # overridable from conftest.py -pytest_plugins = "terminal xfail monkeypatch".split() +# XXX resultlog should go, but pypy's nightrun depends on this +# default, need to change the pypy buildbot master configuration + +pytest_plugins = "terminal xfail resultlog monkeypatch".split() # =================================================== # Distributed testing specific options @@ -29,6 +32,15 @@ # =================================================== +class ConftestPlugin: + def pytest_collect_file(self, path, parent): + ext = path.ext + pb = path.purebasename + if pb.startswith("test_") or pb.endswith("_test") or \ + path in parent._config.args: + if ext == ".py": + return parent.Module(path, parent=parent) + def adddefaultoptions(config): Option = config.Option config._addoptions('general options', Modified: py/branch/pytestplugin/py/test/plugin/conftest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/conftest.py (original) +++ py/branch/pytestplugin/py/test/plugin/conftest.py Wed Feb 25 22:59:49 2009 @@ -2,14 +2,9 @@ pytest_plugins = "pytester", "plugintester" -class Directory(py.test.collect.Directory): - def consider_file(self, path): +class ConftestPlugin: + def pytest_collect_file(self, path, parent): if path.basename.startswith("pytest_") and path.ext == ".py": - return self.Module(path, parent=self) - -# XXX plugin -#class ConftestPlugin: -# def pytest_consider_file(self, path, parent): -# if path.basename.startswith("pytest_") and path.ext == ".py": -# return parent.Module(path, parent=parent) + mod = parent.Module(path, parent=parent) + return mod Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Wed Feb 25 22:59:49 2009 @@ -33,7 +33,7 @@ for colitem in self.pyfuncitem.listchain(): if isinstance(colitem, py.test.collect.Module) and \ colitem.name.startswith("pytest_"): - crunner.ensureplugin(colitem.fspath.purebasename) + crunner.plugins.append(colitem.fspath.purebasename) break return crunner @@ -119,6 +119,10 @@ def pytest_pyfunc_call(self, pyfuncitem, args, kwargs): """ return True if we consumed/did the call to the python function item. """ + # collection hooks + def pytest_collect_file(self, path, parent): + """ return Collection node or None. """ + def pytest_pymodule_makeitem(self, modcol, name, obj): """ return custom item/collector or None. """ Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 25 22:59:49 2009 @@ -34,14 +34,24 @@ class TmpTestdir: def __init__(self, pyfuncitem): self.pyfuncitem = pyfuncitem - name = pyfuncitem.getmodpath(stopatmodule=False) - #name = "_".join(pyfuncitem.getmodpath()) # listnames()) - name = name.replace("()", "_") - name = name.replace(".", "_") - self.tmpdir = py.test.ensuretemp(name) - self._plugins = [] + # XXX remove duplication with tmpdir plugin + basetmp = py.test.ensuretemp("testdir") + name = pyfuncitem.name + for i in range(100): + try: + tmpdir = basetmp.mkdir(name + str(i)) + except py.error.EEXIST: + continue + break + # we need to create another subdir + # because Directory.collect() currently loads + # conftest.py from sibling directories + self.tmpdir = tmpdir.mkdir(name) + self.plugins = [] self._syspathremove = [] #self._olddir = py.std.os.getcwd() + from py.__.test.config import Config + self.Config = Config def finalize(self): for p in self._syspathremove: @@ -63,9 +73,13 @@ ret = p return ret + def makefile(self, ext, *args, **kwargs): return self._makefile(ext, args, kwargs) + def makeconftest(self, source): + return self.makepyfile(conftest=source) + def makepyfile(self, *args, **kwargs): return self._makefile('.py', args, kwargs) @@ -84,16 +98,12 @@ def chdir(self): return self.tmpdir.chdir() - def ensureplugin(self, impname): - assert isinstance(impname, str) - if not impname in self._plugins: - self._plugins.append(impname) - def genitems(self, colitems): return list(self.session.genitems(colitems)) def inline_genitems(self, *args): - config = self.parseconfig(*args) + #config = self.parseconfig(*args) + config = self.parseconfig_clean(*args) session = config.initsession() rec = EventRecorder(config.bus) colitems = [config.getfsnode(arg) for arg in config.args] @@ -130,6 +140,32 @@ config.pytestplugins.unconfigure(config) return sorter + def inline_run_with_plugins(self, *args): + config = self.parseconfig_clean(*args) + config.pytestplugins.configure(config) + session = config.initsession() + sorter = EventRecorder(config.bus) + session.main() + config.pytestplugins.unconfigure(config) + return sorter + + def config_preparse(self): + config = self.Config() + for plugin in self.plugins: + if isinstance(plugin, str): + config.pytestplugins.import_module(plugin) + else: + config.pytestplugins.register(plugin) + return config + + def parseconfig_clean(self, *args): + if not args: + args = (self.tmpdir,) + config = self.config_preparse() + config.parse(list(args)) + return config + + # XXX deprecated: def parseconfig(self, *args): if not args: args = (self.tmpdir,) @@ -164,11 +200,11 @@ def prepare(self): p = self.tmpdir.join("conftest.py") if not p.check(): - pstring = repr(self._plugins) - p.write("import py ; pytest_plugins = %s" % pstring) + plugins = [x for x in self.plugins if isinstance(x, str)] + p.write("import py ; pytest_plugins = %r" % plugins) else: - if self._plugins: - print "warning, reusing existing", p + if self.plugins: + print "warning, ignoring reusing existing con", p def popen(self, cmdargs, stdout, stderr, **kw): if not hasattr(py.std, 'subprocess'): Modified: py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py Wed Feb 25 22:59:49 2009 @@ -12,10 +12,20 @@ class TmpdirPlugin: """ provide temporary directories to test functions and methods. """ + + def pytest_configure(self, config): + # XXX make ensuretemp live on config + self.basetmp = py.test.ensuretemp("tmpdir") + def pytest_pyfuncarg_tmpdir(self, pyfuncitem): - basename = "_".join(pyfuncitem.listnames()) - # XXX make creation dependent on config - return py.test.ensuretemp(basename) + name = pyfuncitem.name + for i in range(10000): + try: + tmpdir = self.basetmp.mkdir(name + (i and str(i) or '')) + except py.error.EEXIST: + continue + break + return tmpdir # =============================================================================== # @@ -28,7 +38,8 @@ def test_pyfuncarg(testdir): item = testdir.getitem("def test_func(tmpdir): pass") - tmpdir = TmpdirPlugin() - p = tmpdir.pytest_pyfuncarg_tmpdir(item) + plugin = TmpdirPlugin() + plugin.pytest_configure(item._config) + p = plugin.pytest_pyfuncarg_tmpdir(item) assert p.check() assert p.basename.endswith("test_func") Modified: py/branch/pytestplugin/py/test/pycollect.py ============================================================================== --- py/branch/pytestplugin/py/test/pycollect.py (original) +++ py/branch/pytestplugin/py/test/pycollect.py Wed Feb 25 22:59:49 2009 @@ -385,50 +385,6 @@ def __ne__(self, other): return not self == other -class DoctestFile(py.test.collect.File): - - def collect(self): - return [DoctestFileContent(self.fspath.basename, parent=self)] - -from py.__.code.excinfo import Repr, ReprFileLocation - -class ReprFailDoctest(Repr): - def __init__(self, reprlocation, lines): - self.reprlocation = reprlocation - self.lines = lines - def toterminal(self, tw): - for line in self.lines: - tw.line(line) - self.reprlocation.toterminal(tw) - -class DoctestFileContent(py.test.collect.Item): - def repr_failure(self, excinfo, outerr): - if excinfo.errisinstance(py.compat.doctest.DocTestFailure): - doctestfailure = excinfo.value - example = doctestfailure.example - test = doctestfailure.test - filename = test.filename - lineno = example.lineno + 1 - message = excinfo.type.__name__ - reprlocation = ReprFileLocation(filename, lineno, message) - checker = py.compat.doctest.OutputChecker() - REPORT_UDIFF = py.compat.doctest.REPORT_UDIFF - filelines = py.path.local(filename).readlines(cr=0) - i = max(0, lineno - 10) - lines = [] - for line in filelines[i:lineno]: - lines.append("%03d %s" % (i+1, line)) - i += 1 - lines += checker.output_difference(example, - doctestfailure.got, REPORT_UDIFF).split("\n") - return ReprFailDoctest(reprlocation, lines) - #elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): - else: - return super(DoctestFileContent, self).repr_failure(excinfo, outerr) - - def runtest(self): - if not self._deprecated_testexecution(): - failed, tot = py.compat.doctest.testfile( - str(self.fspath), module_relative=False, - raise_on_error=True, verbose=0) +# DEPRECATED +#from py.__.test.plugin.pytest_doctest import DoctestFile Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Wed Feb 25 22:59:49 2009 @@ -71,6 +71,7 @@ return self.pyplugins.call_firstresult(*args, **kwargs) def call_each(self, *args, **kwargs): + #print "plugins.call_each", args[0], args[1:], kwargs return self.pyplugins.call_each(*args, **kwargs) def notify(self, eventname, *args, **kwargs): Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Wed Feb 25 22:59:49 2009 @@ -42,6 +42,9 @@ """ yield Items from iterating over the given colitems. """ while colitems: next = colitems.pop(0) + if isinstance(next, (tuple, list)): + colitems[:] = list(next) + colitems + continue assert self.bus is next._config.bus notify = self.bus.notify if isinstance(next, Item): Modified: py/branch/pytestplugin/py/test/testing/acceptance_test.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/acceptance_test.py (original) +++ py/branch/pytestplugin/py/test/testing/acceptance_test.py Wed Feb 25 22:59:49 2009 @@ -228,22 +228,6 @@ "y* = 'xxxxxx*" ]) - def test_doctest_simple_failing(self, testdir): - p = testdir.maketxtfile(""" - >>> i = 0 - >>> i + 1 - 2 - """) - result = testdir.runpytest(p) - result.stdout.fnmatch_lines([ - '001 >>> i = 0', - '002 >>> i + 1', - 'Expected:', - " 2", - "Got:", - " 1", - "*test_doctest_simple_failing.txt:2: DocTestFailure" - ]) def test_dist_testing(self, testdir): p1 = testdir.makepyfile(""" Modified: py/branch/pytestplugin/py/test/testing/test_collect.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_collect.py (original) +++ py/branch/pytestplugin/py/test/testing/test_collect.py Wed Feb 25 22:59:49 2009 @@ -67,12 +67,12 @@ def test_listnames_and__getitembynames(self, testdir): modcol = testdir.getmodulecol("pass", withinit=True) + print modcol._config.pytestplugins.getplugins() names = modcol.listnames() print names dircol = modcol._config.getfsnode(modcol._config.topdir) x = dircol._getitembynames(names) assert modcol.name == x.name - assert modcol.name == x.name def test_listnames_getitembynames_custom(self, testdir): hello = testdir.makefile(".xxx", hello="world") @@ -137,6 +137,22 @@ res2 = modcol.collect() assert res1 == [x.name for x in res2] +class TestCollectPluginHooks: + def test_pytest_collect_file(self, testdir): + tmpdir = testdir.tmpdir + wascalled = [] + class Plugin: + def pytest_collect_file(self, path, parent): + wascalled.append(path) + config = testdir.Config() + config.pytestplugins.register(Plugin()) + config.parse([tmpdir]) + col = config.getfsnode(tmpdir) + testdir.makefile(".abc", "xyz") + res = col.collect() + assert len(wascalled) == 1 + assert wascalled[0].ext == '.abc' + class TestCustomConftests: def test_non_python_files(self, testdir): testdir.makepyfile(conftest=""" Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Wed Feb 25 22:59:49 2009 @@ -1,6 +1,19 @@ import py class TestConfigCmdlineParsing: + @py.test.keywords(xfail="commit parser") + def test_config_addoption(self): + from py.__.test.config import Config + config = Config() + config.addoption("cat1", "--option1", action="store_true") + config.addoption("cat1", "--option2", action="store_true") + cap = py.io.StdCapture() + config.parse(["-h"]) + out, err = cap.reset() + assert out.count("cat1") == 1 + assert out.find("option1") != -1 + assert out.find("option2") != -1 + def test_config_cmdline_options(self, testdir): testdir.makepyfile(conftest=""" import py Modified: py/branch/pytestplugin/py/test/testing/test_doctest.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_doctest.py (original) +++ py/branch/pytestplugin/py/test/testing/test_doctest.py Wed Feb 25 22:59:49 2009 @@ -1,28 +1 @@ -import py -from py.__.test.outcome import Failed - -class TestDoctests: - def test_simple_docteststring(self, testdir): - txtfile = testdir.maketxtfile(test_doc=""" - >>> i = 0 - >>> i + 1 - 1 - """) - col = testdir.getfscol(path=txtfile) - testitem = col.join(txtfile.basename) - res = testitem.runtest() - assert res is None - - @py.test.keywords(xfail=True) - def test_doctest_unexpected_exception(self, testdir): - p = testdir.maketxtfile(""" - >>> i = 0 - >>> x - 2 - """) - col = testdir.getfscol(p) - testitem = col.join(p.basename) - excinfo = py.test.raises(Failed, "testitem.runtest()") - repr = testitem.repr_failure(excinfo, ("", "")) - assert repr.reprlocation Modified: py/branch/pytestplugin/py/test/testing/test_genitems.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_genitems.py (original) +++ py/branch/pytestplugin/py/test/testing/test_genitems.py Wed Feb 25 22:59:49 2009 @@ -60,21 +60,6 @@ assert s.endswith("test_example_items1.testone") print s - def test_collect_doctest_files_with_test_prefix(self, testdir): - from py.__.test.pycollect import DoctestFileContent - testdir.maketxtfile(whatever="") - checkfile = testdir.maketxtfile(test_something=""" - alskdjalsdk - >>> i = 5 - >>> i-1 - 4 - """) - for x in (testdir.tmpdir, checkfile): - #print "checking that %s returns custom items" % (x,) - items, events = testdir.inline_genitems(x) - assert len(items) == 1 - assert isinstance(items[0], DoctestFileContent) - class TestKeywordSelection: def test_select_simple(self, testdir): From hpk at codespeak.net Wed Feb 25 23:01:51 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 25 Feb 2009 23:01:51 +0100 (CET) Subject: [py-svn] r62166 - in py/branch/pytestplugin/py/test: . plugin Message-ID: <20090225220151.43AA4168447@codespeak.net> Author: hpk Date: Wed Feb 25 23:01:49 2009 New Revision: 62166 Added: py/branch/pytestplugin/py/test/plugin/pytest_doctest.py Modified: py/branch/pytestplugin/py/test/defaultconftest.py Log: *actually* add pytest_doctest plugin and enable tmpdir plugin by default Modified: py/branch/pytestplugin/py/test/defaultconftest.py ============================================================================== --- py/branch/pytestplugin/py/test/defaultconftest.py (original) +++ py/branch/pytestplugin/py/test/defaultconftest.py Wed Feb 25 23:01:49 2009 @@ -13,7 +13,7 @@ # XXX resultlog should go, but pypy's nightrun depends on this # default, need to change the pypy buildbot master configuration -pytest_plugins = "terminal xfail resultlog monkeypatch".split() +pytest_plugins = "terminal xfail tmpdir resultlog monkeypatch".split() # =================================================== # Distributed testing specific options Added: py/branch/pytestplugin/py/test/plugin/pytest_doctest.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/plugin/pytest_doctest.py Wed Feb 25 23:01:49 2009 @@ -0,0 +1,163 @@ +import py + +class DoctestPlugin: + def pytest_addoption(self, config): + config.addoption("--doctest-modules", + action="store_true", dest="doctestmodules") + + def pytest_collect_file(self, path, parent): + if path.ext == ".py": + if parent._config.getvalue("doctestmodules"): + return DoctestModule(path, parent) + if path.check(fnmatch="test_*.txt"): + return DoctestTextfile(path, parent) + +from py.__.code.excinfo import Repr, ReprFileLocation + +class ReprFailDoctest(Repr): + def __init__(self, reprlocation, lines): + self.reprlocation = reprlocation + self.lines = lines + def toterminal(self, tw): + for line in self.lines: + tw.line(line) + self.reprlocation.toterminal(tw) + +class DoctestItem(py.test.collect.Item): + def __init__(self, path, parent): + name = self.__class__.__name__ + ":" + path.basename + super(DoctestItem, self).__init__(name=name, parent=parent) + self.fspath = path + + def repr_failure(self, excinfo, outerr): + if excinfo.errisinstance(py.compat.doctest.DocTestFailure): + doctestfailure = excinfo.value + example = doctestfailure.example + test = doctestfailure.test + filename = test.filename + lineno = example.lineno + 1 + message = excinfo.type.__name__ + reprlocation = ReprFileLocation(filename, lineno, message) + checker = py.compat.doctest.OutputChecker() + REPORT_UDIFF = py.compat.doctest.REPORT_UDIFF + filelines = py.path.local(filename).readlines(cr=0) + i = max(0, lineno - 10) + lines = [] + for line in filelines[i:lineno]: + lines.append("%03d %s" % (i+1, line)) + i += 1 + lines += checker.output_difference(example, + doctestfailure.got, REPORT_UDIFF).split("\n") + return ReprFailDoctest(reprlocation, lines) + #elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): + else: + return super(DoctestTextfileContent, self).repr_failure(excinfo, outerr) + +class DoctestTextfile(DoctestItem): + def runtest(self): + if not self._deprecated_testexecution(): + failed, tot = py.compat.doctest.testfile( + str(self.fspath), module_relative=False, + raise_on_error=True, verbose=0) + +class DoctestModule(DoctestItem): + def runtest(self): + assert 0 + module = self.fspath.pyimport() + failed, tot = py.compat.doctest.testmod( + module, raise_on_error=True, verbose=0) + + +# +# Plugin tests +# +from py.__.test.outcome import Failed + +class TestDoctests: + def test_collect_testtextfile(self, testdir): + testdir.plugins.append(DoctestPlugin()) + testdir.maketxtfile(whatever="") + checkfile = testdir.maketxtfile(test_something=""" + alskdjalsdk + >>> i = 5 + >>> i-1 + 4 + """) + for x in (testdir.tmpdir, checkfile): + #print "checking that %s returns custom items" % (x,) + items, events = testdir.inline_genitems(x) + print events.events + assert len(items) == 1 + assert isinstance(items[0], DoctestTextfile) + + def test_collect_module(self, testdir): + testdir.plugins.append(DoctestPlugin()) + path = testdir.makepyfile(whatever="#") + for p in (path, testdir.tmpdir): + items, evrec = testdir.inline_genitems(p, '--doctest-modules') + print evrec.events + assert len(items) == 1 + assert isinstance(items[0], DoctestModule) + + def test_simple_doctestfile(self, testdir): + testdir.plugins.append(DoctestPlugin()) + p = testdir.maketxtfile(test_doc=""" + >>> x = 1 + >>> x == 1 + False + """) + events = testdir.inline_run_with_plugins(p) + ev, = events.getnamed("itemtestreport") + assert ev.failed + + @py.test.keywords(xfail=True) + def test_doctest_unexpected_exception(self, testdir): + p = testdir.maketxtfile(""" + >>> i = 0 + >>> x + 2 + """) + col = testdir.getfscol(p) + testitem = col.join(p.basename) + excinfo = py.test.raises(Failed, "testitem.runtest()") + repr = testitem.repr_failure(excinfo, ("", "")) + assert repr.reprlocation + + @py.test.keywords(xfail=True) + def test_doctestmodule(self, testdir): + testdir.plugins.append(DoctestPlugin()) + p = testdir.makepyfile(""" + ''' + >>> x = 1 + >>> x == 1 + False + + ''' + """) + events = testdir.inline_run_with_plugins(p, "--doctest-modules") + ev, = events.getnamed("itemtestreport") + assert ev.failed + assert isinstnace(ev.item, DoctestTextfile) + + def test_txtfile_failing(self, testdir): + testdir.plugins.append('pytest_doctest') + p = testdir.maketxtfile(""" + >>> i = 0 + >>> i + 1 + 2 + """) + result = testdir.runpytest(p) + result.stdout.fnmatch_lines([ + '001 >>> i = 0', + '002 >>> i + 1', + 'Expected:', + " 2", + "Got:", + " 1", + "*test_txtfile_failing.txt:2: DocTestFailure" + ]) + + +def test_generic(plugintester): + plugintester.apicheck(DoctestPlugin) + From hpk at codespeak.net Wed Feb 25 23:03:29 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 25 Feb 2009 23:03:29 +0100 (CET) Subject: [py-svn] r62167 - py/branch/pytestplugin/py/test/testing Message-ID: <20090225220329.5F78E16844C@codespeak.net> Author: hpk Date: Wed Feb 25 23:03:28 2009 New Revision: 62167 Removed: py/branch/pytestplugin/py/test/testing/test_doctest.py Log: remove empty file Deleted: /py/branch/pytestplugin/py/test/testing/test_doctest.py ============================================================================== --- /py/branch/pytestplugin/py/test/testing/test_doctest.py Wed Feb 25 23:03:28 2009 +++ (empty file) @@ -1 +0,0 @@ - From hpk at codespeak.net Wed Feb 25 23:23:38 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 25 Feb 2009 23:23:38 +0100 (CET) Subject: [py-svn] r62168 - in py/branch/pytestplugin/py/test: dsession/testing plugin Message-ID: <20090225222338.11FE21684C9@codespeak.net> Author: hpk Date: Wed Feb 25 23:23:37 2009 New Revision: 62168 Modified: py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Log: avoiding _reparse() mostly Modified: py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_functional_dsession.py Wed Feb 25 23:23:37 2009 @@ -21,19 +21,19 @@ import py Option = py.test.config.Option option = py.test.config.addoptions("someopt", - Option('', '--forcegen', action="store_true", dest="forcegen", default=False)) + Option('--someopt', action="store_true", dest="someopt", default=False)) """, ) p1 = testdir.makepyfile(""" def test_1(): import py, conftest - print "test_1: py.test.config.option.forcegen", py.test.config.option.forcegen + print "test_1: py.test.config.option.someopt", py.test.config.option.someopt print "test_1: conftest", conftest - print "test_1: conftest.option.forcegen", conftest.option.forcegen - assert conftest.option.forcegen + print "test_1: conftest.option.someopt", conftest.option.someopt + assert conftest.option.someopt """, __init__="#") print p1 - config = testdir.parseconfig('-n1', p1, '--forcegen') + config = py.test.config._reparse(['-n1', p1, '--someopt']) dsession = DSession(config) eq = EventQueue(config.bus) dsession.main() Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Wed Feb 25 23:23:37 2009 @@ -103,7 +103,7 @@ def inline_genitems(self, *args): #config = self.parseconfig(*args) - config = self.parseconfig_clean(*args) + config = self.parseconfig(*args) session = config.initsession() rec = EventRecorder(config.bus) colitems = [config.getfsnode(arg) for arg in config.args] @@ -141,7 +141,7 @@ return sorter def inline_run_with_plugins(self, *args): - config = self.parseconfig_clean(*args) + config = self.parseconfig(*args) config.pytestplugins.configure(config) session = config.initsession() sorter = EventRecorder(config.bus) @@ -158,19 +158,13 @@ config.pytestplugins.register(plugin) return config - def parseconfig_clean(self, *args): + def parseconfig(self, *args): if not args: args = (self.tmpdir,) config = self.config_preparse() config.parse(list(args)) return config - # XXX deprecated: - def parseconfig(self, *args): - if not args: - args = (self.tmpdir,) - return py.test.config._reparse(list(args)) - def getitem(self, source, funcname="test_func"): modcol = self.getmodulecol(source) item = modcol.join(funcname) From hpk at codespeak.net Thu Feb 26 00:42:20 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 00:42:20 +0100 (CET) Subject: [py-svn] r62170 - in py/branch/pytestplugin/py/test: . plugin Message-ID: <20090225234220.A1D7616841C@codespeak.net> Author: hpk Date: Thu Feb 26 00:42:18 2009 New Revision: 62170 Modified: py/branch/pytestplugin/py/test/plugin/pytest_doctest.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/session.py Log: extend plugin api testinging fix a bit doctests Modified: py/branch/pytestplugin/py/test/plugin/pytest_doctest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_doctest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_doctest.py Thu Feb 26 00:42:18 2009 @@ -49,9 +49,10 @@ lines += checker.output_difference(example, doctestfailure.got, REPORT_UDIFF).split("\n") return ReprFailDoctest(reprlocation, lines) - #elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): + elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): + return # XXX else: - return super(DoctestTextfileContent, self).repr_failure(excinfo, outerr) + return super(DoctestItem, self).repr_failure(excinfo, outerr) class DoctestTextfile(DoctestItem): def runtest(self): @@ -62,7 +63,6 @@ class DoctestModule(DoctestItem): def runtest(self): - assert 0 module = self.fspath.pyimport() failed, tot = py.compat.doctest.testmod( module, raise_on_error=True, verbose=0) @@ -71,7 +71,6 @@ # # Plugin tests # -from py.__.test.outcome import Failed class TestDoctests: def test_collect_testtextfile(self, testdir): @@ -112,18 +111,27 @@ @py.test.keywords(xfail=True) def test_doctest_unexpected_exception(self, testdir): + from py.__.test.outcome import Failed + + testdir.plugins.append(DoctestPlugin()) p = testdir.maketxtfile(""" >>> i = 0 + >>> i = 1 >>> x 2 """) - col = testdir.getfscol(p) - testitem = col.join(p.basename) - excinfo = py.test.raises(Failed, "testitem.runtest()") - repr = testitem.repr_failure(excinfo, ("", "")) - assert repr.reprlocation + sorter = testdir.inline_run(p) + events = sorter.getnamed("itemtestreport") + assert len(events) == 1 + ev, = events + assert ev.failed + assert ev.longrepr + # XXX + #testitem, = items + #excinfo = py.test.raises(Failed, "testitem.runtest()") + #repr = testitem.repr_failure(excinfo, ("", "")) + #assert repr.reprlocation - @py.test.keywords(xfail=True) def test_doctestmodule(self, testdir): testdir.plugins.append(DoctestPlugin()) p = testdir.makepyfile(""" @@ -137,7 +145,6 @@ events = testdir.inline_run_with_plugins(p, "--doctest-modules") ev, = events.getnamed("itemtestreport") assert ev.failed - assert isinstnace(ev.item, DoctestTextfile) def test_txtfile_failing(self, testdir): testdir.plugins.append('pytest_doctest') Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Thu Feb 26 00:42:18 2009 @@ -37,23 +37,22 @@ break return crunner - def apicheck(self, impclass): - print "loading and checking", impclass + def apicheck(self, pluginclass): + print "loading and checking", pluginclass fail = False pm = py.test._PytestPlugins() - plugin = impclass() - methods = collectattr(plugin.__class__) + methods = collectattr(pluginclass) hooks = collectattr(PytestPluginHooks) getargs = py.std.inspect.getargs def isgenerichook(name): - return name.startswith("pyevent_") or name.startswith("pytest_pyfuncarg") + return name.startswith("pytest_pyfuncarg_") while methods: name, method = methods.popitem() if isgenerichook(name): - continue # XXX also do some checks - if name not in hooks and not isgenerichook(name): + continue + if name not in hooks: print "found unknown hook: %s" % name fail = True else: @@ -65,8 +64,8 @@ for arg, hookarg in zip(method_args[0], hookargs[0]): if arg != hookarg: print "argument mismatch:" - print "required:", formatdef(method) - print "actual :", formatdef(hook) + print "actual : %s.%s" %(pluginclass.__name__, formatdef(method)) + print "required:", formatdef(hook) fail = True break if not fail: @@ -74,11 +73,12 @@ if fail: py.test.fail("Plugin API error") -def collectattr(obj, prefix="pytest_"): +def collectattr(obj, prefixes=("pytest_", "pyevent_")): methods = {} for apiname in vars(obj): - if apiname.startswith(prefix): - methods[apiname] = getattr(obj, apiname) + for prefix in prefixes: + if apiname.startswith(prefix): + methods[apiname] = getattr(obj, apiname) return methods def formatdef(func): @@ -133,13 +133,52 @@ def pytest_terminal_summary(self, terminalreporter): """ add additional section in terminal summary reporting. """ - #def pytest_termreport_result(self, event): - # """ return (category, short, verbose) information about the given result event. - # ``category`` will be used for counting tests and - # pytest_termreport_summary will be called for each category. - # ``short`` will be used for printing progress info like "...F.." - # ``verbose`` is used for printing verbose information. - # """ + # events + def pyevent(self, eventname, *args, **kwargs): + """ called for each testing event. """ + + def pyevent_internalerror(self, event): + """ called for internal errors. """ + + def pyevent_itemstart(self, event): + """ test item gets collected. """ + + def pyevent_itemtestreport(self, event): + """ test has been run. """ + + def pyevent_deselected(self, event): + """ item has been dselected. """ + + def pyevent_collectionstart(self, event): + """ collector starts collecting. """ + + def pyevent_collectionreport(self, event): + """ collector finished collecting. """ + + def pyevent_testrunstart(self, event): + """ whole test run starts. """ + + def pyevent_testrunfinish(self, event): + """ whole test run starts. """ + + def pyevent_hostup(self, event): + """ Host is up. """ + + def pyevent_hostgatewayready(self, event): + """ Connection to Host is ready. """ + + def pyevent_hostdown(self, event): + """ Host is down. """ + + def pyevent_rescheduleitems(self, event): + """ Items from a host that went down. """ + + def pyevent_looponfailinginfo(self, event): + """ info for repeating failing tests. """ + + def pyevent_plugin_registered(self, plugin): + """ a new py lib plugin got registered. """ + # =============================================================================== # plugin tests Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Thu Feb 26 00:42:18 2009 @@ -85,13 +85,13 @@ else: return self._tw.markup("???", red=True) - def pyevent_internalexception(self, ev): - for line in str(ev.repr).split("\n"): + def pyevent_internalerror(self, event): + for line in str(event.repr).split("\n"): self.write_line("InternalException: " + line) - def pyevent_hostgatewayready(self, ev): + def pyevent_hostgatewayready(self, event): if self.config.option.verbose: - self.write_line("HostGatewayReady: %s" %(ev.host,)) + self.write_line("HostGatewayReady: %s" %(event.host,)) def pyevent_plugin_registered(self, plugin): if self.config.option.traceconfig: @@ -101,63 +101,63 @@ # which garbles our output if we use self.write_line self.write_line(msg) - def pyevent_hostup(self, ev): - d = ev.platinfo.copy() - d['hostid'] = ev.host.hostid + def pyevent_hostup(self, event): + d = event.platinfo.copy() + d['hostid'] = event.host.hostid d['version'] = repr_pythonversion(d['sys.version_info']) self.write_line("HOSTUP: %(hostid)s %(sys.platform)s " "%(sys.executable)s - Python %(version)s" % d) - def pyevent_hostdown(self, ev): - host = ev.host - error = ev.error + def pyevent_hostdown(self, event): + host = event.host + error = event.error if error: self.write_line("HostDown %s: %s" %(host.hostid, error)) - def pyevent_itemstart(self, ev): + def pyevent_itemstart(self, event): if self.config.option.verbose: - info = ev.item.repr_metainfo() + info = event.item.repr_metainfo() line = info.verboseline(basedir=self.curdir) + " " extra = "" - if ev.host: - extra = "-> " + ev.host.hostid + if event.host: + extra = "-> " + event.host.hostid self.write_ensure_prefix(line, extra) else: # ensure that the path is printed before the 1st test of # a module starts running - fspath = ev.item.fspath + fspath = event.item.fspath self.write_fspath_result(fspath, "") - def pyevent_rescheduleitems(self, ev): + def pyevent_rescheduleitems(self, event): if self.config.option.debug: - self.write_sep("!", "RESCHEDULING %s " %(ev.items,)) + self.write_sep("!", "RESCHEDULING %s " %(event.items,)) - def pyevent_deselected(self, ev): - self.stats.setdefault('deselected', []).append(ev) + def pyevent_deselected(self, event): + self.stats.setdefault('deselected', []).append(event) - def pyevent_itemtestreport(self, ev): - fspath = ev.colitem.fspath - cat, letter, word = self.getcategoryletterword(ev) - self.stats.setdefault(cat, []).append(ev) + def pyevent_itemtestreport(self, event): + fspath = event.colitem.fspath + cat, letter, word = self.getcategoryletterword(event) + self.stats.setdefault(cat, []).append(event) if not self.config.option.verbose: self.write_fspath_result(fspath, letter) else: - info = ev.colitem.repr_metainfo() + info = event.colitem.repr_metainfo() line = info.verboseline(basedir=self.curdir) + " " self.write_ensure_prefix(line, word) - def pyevent_collectionreport(self, ev): - if not ev.passed: - if ev.failed: - self.stats.setdefault("failed", []).append(ev) - msg = ev.longrepr.reprcrash.message - self.write_fspath_result(ev.colitem.fspath, "F") - elif ev.skipped: - self.stats.setdefault("skipped", []).append(ev) - self.write_fspath_result(ev.colitem.fspath, "S") + def pyevent_collectionreport(self, event): + if not event.passed: + if event.failed: + self.stats.setdefault("failed", []).append(event) + msg = event.longrepr.reprcrash.message + self.write_fspath_result(event.colitem.fspath, "F") + elif event.skipped: + self.stats.setdefault("skipped", []).append(event) + self.write_fspath_result(event.colitem.fspath, "S") - def pyevent_testrunstart(self, ev): + def pyevent_testrunstart(self, event): self.write_sep("=", "test session starts", bold=True) self._sessionstarttime = py.std.time.time() rev = py.__pkg__.getrev() @@ -174,30 +174,30 @@ for i, testarg in py.builtin.enumerate(self.config.args): self.write_line("test object %d: %s" %(i+1, testarg)) - def pyevent_testrunfinish(self, ev): + def pyevent_testrunfinish(self, event): self._tw.line("") - if ev.exitstatus in (0, 1, 2): + if event.exitstatus in (0, 1, 2): self.summary_failures() self.summary_skips() self.config.pytestplugins.call_each("pytest_terminal_summary", terminalreporter=self) - if ev.excrepr is not None: - self.summary_final_exc(ev.excrepr) - if ev.exitstatus == 2: + if event.excrepr is not None: + self.summary_final_exc(event.excrepr) + if event.exitstatus == 2: self.write_sep("!", "KEYBOARD INTERRUPT") self.summary_deselected() self.summary_stats() - def pyevent_looponfailinginfo(self, ev): - if ev.failreports: + def pyevent_looponfailinginfo(self, event): + if event.failreports: self.write_sep("#", "LOOPONFAILING", red=True) - for report in ev.failreports: + for report in event.failreports: try: loc = report.longrepr.reprcrash except AttributeError: loc = str(report.longrepr)[:50] self.write_line(loc, red=True) self.write_sep("#", "waiting for changes") - for rootdir in ev.rootdirs: + for rootdir in event.rootdirs: self.write_line("### Watching: %s" %(rootdir,), bold=True) # @@ -265,24 +265,24 @@ def outindent(self, line): self.out.line(self.indent + str(line)) - def pyevent_collectionstart(self, ev): - self.outindent(ev.collector) + def pyevent_collectionstart(self, event): + self.outindent(event.collector) self.indent += self.INDENT def pyevent_itemstart(self, event): self.outindent(event.item) - def pyevent_collectionreport(self, ev): - if not ev.passed: - self.outindent("!!! %s !!!" % ev.longrepr.reprcrash.message) - self._failed.append(ev) + def pyevent_collectionreport(self, event): + if not event.passed: + self.outindent("!!! %s !!!" % event.longrepr.reprcrash.message) + self._failed.append(event) self.indent = self.indent[:-len(self.INDENT)] - def pyevent_testrunfinish(self, session): + def pyevent_testrunfinish(self, event): if self._failed: self.out.sep("!", "collection failures") - for ev in self._failed: - ev.toterminal(self.out) + for event in self._failed: + event.toterminal(self.out) def folded_skips(skipped): d = {} @@ -399,11 +399,11 @@ "E ImportError: No module named xyz" ]) - def test_internal_exception(self, testdir, linecomp): + def test_internalerror(self, testdir, linecomp): modcol = testdir.getmodulecol("def test_one(): pass") rep = TerminalReporter(modcol._config, file=linecomp.stringio) excinfo = py.test.raises(ValueError, "raise ValueError('hello')") - rep.pyevent_internalexception(event.InternalException(excinfo)) + rep.pyevent_internalerror(event.InternalException(excinfo)) linecomp.assert_contains_lines([ "InternalException: >*raise ValueError*" ]) @@ -621,3 +621,8 @@ assert repr_pythonversion() == str(x) finally: py.magic.revert(sys, 'version_info') + +def test_generic(plugintester): + plugintester.apicheck(TerminalPlugin) + plugintester.apicheck(TerminalReporter) + plugintester.apicheck(CollectonlyReporter) Modified: py/branch/pytestplugin/py/test/session.py ============================================================================== --- py/branch/pytestplugin/py/test/session.py (original) +++ py/branch/pytestplugin/py/test/session.py Thu Feb 26 00:42:18 2009 @@ -123,12 +123,14 @@ break if not self.config.option.collectonly: self.runtest(item) + py.test.collect.Item._setupstate.teardown_all() except KeyboardInterrupt: captured_excinfo = py.code.ExceptionInfo() exitstatus = outcome.EXIT_INTERRUPTED except: - self.bus.notify("internalerror", event.InternalException()) + captured_excinfo = py.code.ExceptionInfo() + self.bus.notify("internalerror", event.InternalException(captured_excinfo)) exitstatus = outcome.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: exitstatus = outcome.EXIT_TESTSFAILED From hpk at codespeak.net Thu Feb 26 01:02:59 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 01:02:59 +0100 (CET) Subject: [py-svn] r62171 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090226000259.9B18A1684FD@codespeak.net> Author: hpk Date: Thu Feb 26 01:02:58 2009 New Revision: 62171 Added: py/branch/pytestplugin/py/test/parseopt.py py/branch/pytestplugin/py/test/testing/test_parseopt.py Modified: py/branch/pytestplugin/py/test/plugin/pytest_doctest.py Log: beginning a lean option parser as a small wrapper around optparse Added: py/branch/pytestplugin/py/test/parseopt.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/parseopt.py Thu Feb 26 01:02:58 2009 @@ -0,0 +1,85 @@ +""" +thin wrapper around Python's optparse.py +adding some extra checks and ways to systematically +have Environment variables provide default values +for options. basic usage: + + >>> parser = Parser() + >>> parser.addoption("--hello", action="store_true", dest="hello") + >>> option, args = parser.parse(['--hello']) + >>> option.hello + True + >>> args + [] + +""" +import py +from py.compat import optparse + +class Parser: + """ Parser for command line arguments. """ + + def __init__(self): + self._anonymous = OptionGroup("misc") + self._groups = [self._anonymous] + + def addgroup(self, name, description=""): + for group in self._groups: + if group.name == name: + raise ValueError("group %r already exists" % name) + group = OptionGroup(name, description) + self._groups.append(group) + return group + + def getgroup(self, name): + for group in self._groups: + if group.name == name: + return group + raise ValueError("group %r not found" %(name,)) + + def addoption(self, *opts, **attrs): + """ add an optparse-style option. """ + self._anonymous.addoption(*opts, **attrs) + + def parse(self, args): + optparser = optparse.OptionParser() + for group in self._groups: + if group.options: + optgroup = optparse.OptionGroup(optparser, group.name) + optgroup.add_options(group.options) + optparser.add_option_group(optgroup) + return optparser.parse_args(args) + + def addoptions(self, *specs): + """ add a named group of options to the current testing session. + This function gets invoked during testing session initialization. + """ + for spec in specs: + for shortopt in spec._short_opts: + if not shortopt.isupper(): + raise ValueError( + "custom options must be capital letter " + "got %r" %(spec,) + ) + return self._addoptions(groupname, *specs) + + + def _addoptions(self, groupname, *specs): + optgroup = optparse.OptionGroup(self._parser, groupname) + optgroup.add_options(specs) + self._parser.add_option_group(optgroup) + for opt in specs: + if hasattr(opt, 'default') and opt.dest: + if not hasattr(self.option, opt.dest): + setattr(self.option, opt.dest, opt.default) + return self.option + +class OptionGroup: + def __init__(self, name, description=""): + self.name = name + self.description = description + self.options = [] + + def addoption(self, *optnames, **attrs): + option = py.compat.optparse.Option(*optnames, **attrs) + self.options.append(option) Modified: py/branch/pytestplugin/py/test/plugin/pytest_doctest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_doctest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_doctest.py Thu Feb 26 01:02:58 2009 @@ -50,7 +50,8 @@ doctestfailure.got, REPORT_UDIFF).split("\n") return ReprFailDoctest(reprlocation, lines) elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): - return # XXX + excinfo = py.code.ExceptionInfo(excinfo.value.exc_info) + return super(DoctestItem, self).repr_failure(excinfo, outerr) else: return super(DoctestItem, self).repr_failure(excinfo, outerr) @@ -109,7 +110,6 @@ ev, = events.getnamed("itemtestreport") assert ev.failed - @py.test.keywords(xfail=True) def test_doctest_unexpected_exception(self, testdir): from py.__.test.outcome import Failed Added: py/branch/pytestplugin/py/test/testing/test_parseopt.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/testing/test_parseopt.py Thu Feb 26 01:02:58 2009 @@ -0,0 +1,34 @@ +import py +from py.__.test import parseopt + +class TestParser: + def test_group_add_and_get(self): + parser = parseopt.Parser() + group = parser.addgroup("hello", description="desc") + assert group.name == "hello" + assert group.description == "desc" + py.test.raises(ValueError, parser.addgroup, "hello") + group2 = parser.getgroup("hello") + assert group2 is group + py.test.raises(ValueError, parser.getgroup, 'something') + + def test_group_addoption(self): + group = parseopt.OptionGroup("hello") + group.addoption("--option1", action="store_true") + assert len(group.options) == 1 + assert isinstance(group.options[0], py.compat.optparse.Option) + + def test_parser_addoption(self): + parser = parseopt.Parser() + group = parser.getgroup("misc") + assert len(group.options) == 0 + group.addoption("--option1", action="store_true") + assert len(group.options) == 1 + + def test_parse(self): + parser = parseopt.Parser() + parser.addoption("--hello", dest="hello", action="store") + option, args = parser.parse(['--hello', 'world']) + assert option.hello == "world" + assert not args + From hpk at codespeak.net Thu Feb 26 09:09:13 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 09:09:13 +0100 (CET) Subject: [py-svn] r62172 - in py/branch/pytestplugin/py/test: . testing Message-ID: <20090226080913.3603F168505@codespeak.net> Author: hpk Date: Thu Feb 26 09:09:11 2009 New Revision: 62172 Modified: py/branch/pytestplugin/py/test/parseopt.py py/branch/pytestplugin/py/test/testing/test_parseopt.py Log: some more meat for the optionparser Modified: py/branch/pytestplugin/py/test/parseopt.py ============================================================================== --- py/branch/pytestplugin/py/test/parseopt.py (original) +++ py/branch/pytestplugin/py/test/parseopt.py Thu Feb 26 09:09:11 2009 @@ -18,16 +18,23 @@ class Parser: """ Parser for command line arguments. """ + _shortoptrestrict = True - def __init__(self): - self._anonymous = OptionGroup("misc") + def __init__(self, defaultget=None): + self._anonymous = OptionGroup("misc", parser=self) self._groups = [self._anonymous] + self._defaultget = defaultget + + def processoption(self, option): + if self._defaultget: + if option.dest: + option.default = self._defaultget(option) def addgroup(self, name, description=""): for group in self._groups: if group.name == name: raise ValueError("group %r already exists" % name) - group = OptionGroup(name, description) + group = OptionGroup(name, description, parser=self) self._groups.append(group) return group @@ -50,20 +57,6 @@ optparser.add_option_group(optgroup) return optparser.parse_args(args) - def addoptions(self, *specs): - """ add a named group of options to the current testing session. - This function gets invoked during testing session initialization. - """ - for spec in specs: - for shortopt in spec._short_opts: - if not shortopt.isupper(): - raise ValueError( - "custom options must be capital letter " - "got %r" %(spec,) - ) - return self._addoptions(groupname, *specs) - - def _addoptions(self, groupname, *specs): optgroup = optparse.OptionGroup(self._parser, groupname) optgroup.add_options(specs) @@ -75,11 +68,19 @@ return self.option class OptionGroup: - def __init__(self, name, description=""): + def __init__(self, name, description="", parser=None): self.name = name self.description = description self.options = [] + self.parser = parser def addoption(self, *optnames, **attrs): + """ add an option to this group. """ + if getattr(self.parser, '_shortoptrestrict', False): + for opt in optnames: + if opt[0] == '-' and opt[1].islower(): + raise ValueError("lowercase shortoptions reserved") option = py.compat.optparse.Option(*optnames, **attrs) + if self.parser: + self.parser.processoption(option) self.options.append(option) Modified: py/branch/pytestplugin/py/test/testing/test_parseopt.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_parseopt.py (original) +++ py/branch/pytestplugin/py/test/testing/test_parseopt.py Thu Feb 26 09:09:11 2009 @@ -18,6 +18,17 @@ assert len(group.options) == 1 assert isinstance(group.options[0], py.compat.optparse.Option) + def test_group_shortopt_lowercase(self): + parser = parseopt.Parser() + group = parser.addgroup("hello") + py.test.raises(ValueError, """ + group.addoption("-x", action="store_true") + """) + assert len(group.options) == 0 + parser._shortoptrestrict = False + group.addoption("-x", action="store_true") + assert len(group.options) == 1 + def test_parser_addoption(self): parser = parseopt.Parser() group = parser.getgroup("misc") @@ -31,4 +42,17 @@ option, args = parser.parse(['--hello', 'world']) assert option.hello == "world" assert not args - + + def test_parse_defaultgetter(self): + def defaultget(option): + if option.type == "int": + return 42 + elif option.type == "string": + return "world" + parser = parseopt.Parser(defaultget=defaultget) + parser.addoption("--this", dest="this", type="int", action="store") + parser.addoption("--hello", dest="hello", type="string", action="store") + parser.addoption("--no", dest="no", action="store_true") + option, args = parser.parse([]) + assert option.hello == "world" + assert option.this == 42 From hpk at codespeak.net Thu Feb 26 09:46:58 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 09:46:58 +0100 (CET) Subject: [py-svn] r62173 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090226084658.AE88816847B@codespeak.net> Author: hpk Date: Thu Feb 26 09:46:58 2009 New Revision: 62173 Added: py/branch/pytestplugin/py/test/plugin/pytest_iocapture.py Log: another plugin to capture stdout/stderr Added: py/branch/pytestplugin/py/test/plugin/pytest_iocapture.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/plugin/pytest_iocapture.py Thu Feb 26 09:46:58 2009 @@ -0,0 +1,54 @@ +import py + +class IocapturePlugin: + """ capture sys.stdout/sys.stderr / fd1/fd2. """ + def pytest_pyfuncarg_stdcapture(self, pyfuncitem): + capture = Capture(py.io.StdCapture) + pyfuncitem.addfinalizer(capture.finalize) + return capture + + def pytest_pyfuncarg_stdcapturefd(self, pyfuncitem): + capture = Capture(py.io.StdCaptureFD) + pyfuncitem.addfinalizer(capture.finalize) + return capture + +class Capture: + def __init__(self, captureclass): + self._captureclass = captureclass + self._capture = self._captureclass() + + def finalize(self): + self._capture.reset() + + def reset(self): + res = self._capture.reset() + self._capture = self._captureclass() + return res + +def test_generic(plugintester): + plugintester.apicheck(IocapturePlugin) + +class TestCapture: + def test_std_functional(self, testdir): + testdir.plugins.append(IocapturePlugin()) + evrec = testdir.inline_runsource(""" + def test_hello(stdcapture): + print 42 + out, err = stdcapture.reset() + assert out.startswith("42") + """) + ev, = evrec.getnamed("itemtestreport") + assert ev.passed + + def test_stdfd_functional(self, testdir): + testdir.plugins.append(IocapturePlugin()) + evrec = testdir.inline_runsource(""" + def test_hello(stdcapturefd): + import os + os.write(1, "42") + out, err = stdcapturefd.reset() + assert out.startswith("42") + """) + ev, = evrec.getnamed("itemtestreport") + assert ev.passed + From hpk at codespeak.net Thu Feb 26 11:31:44 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 11:31:44 +0100 (CET) Subject: [py-svn] r62176 - in py/branch/pytestplugin/py: . test test/dsession/testing test/plugin test/testing Message-ID: <20090226103144.A0180168504@codespeak.net> Author: hpk Date: Thu Feb 26 11:31:43 2009 New Revision: 62176 Modified: py/branch/pytestplugin/py/conftest.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/defaultconftest.py py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py py/branch/pytestplugin/py/test/parseopt.py py/branch/pytestplugin/py/test/plugin/pytest_apigen.py py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/testing/test_config.py py/branch/pytestplugin/py/test/testing/test_parseopt.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: port to use new parser Modified: py/branch/pytestplugin/py/conftest.py ============================================================================== --- py/branch/pytestplugin/py/conftest.py (original) +++ py/branch/pytestplugin/py/conftest.py Thu Feb 26 11:31:43 2009 @@ -19,21 +19,21 @@ import py Option = py.test.config.Option -option = py.test.config.addoptions("py lib options", - Option('-S', '', +py.test.config.addoptions("py lib testing options", + Option('--sshtarget', action="store", dest="sshtarget", default=None, help=("target to run tests requiring ssh, e.g. " "user at codespeak.net")), - Option('', '--apigenpath', + Option('--apigenpath', action="store", dest="apigenpath", default="../apigen", type="string", help="relative path to apigen doc output location (relative from py/)"), - Option('', '--docpath', + Option('--docpath', action='store', dest='docpath', default="doc", type='string', help="relative path to doc output location (relative from py/)"), - Option('', '--runslowtests', + Option('--runslowtests', action="store_true", dest="runslowtests", default=False, help="run slow tests)"), ) Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Thu Feb 26 11:31:43 2009 @@ -4,7 +4,8 @@ from conftesthandle import Conftest from py.__.test.defaultconftest import adddefaultoptions -optparse = py.compat.optparse +from py.__.test import parseopt +from py.__.misc.warn import APIWARN # XXX move to Config class basetemp = None @@ -26,13 +27,15 @@ class Config(object): """ central bus for dealing with configuration/initialization data. """ - Option = optparse.Option + Option = py.compat.optparse.Option # deprecated _initialized = False def __init__(self, pytestplugins=None): self.option = CmdOptions() - self._parser = optparse.OptionParser( - usage="usage: %prog [options] [query] [filenames of tests]") + self._parser = parseopt.Parser( + usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]", + processopt=self._processopt, + ) if pytestplugins is None: pytestplugins = py.test._PytestPlugins() assert isinstance(pytestplugins, py.test._PytestPlugins) @@ -40,6 +43,11 @@ self.pytestplugins = pytestplugins self._conftest = Conftest(onimport=self.pytestplugins.consider_conftest) + def _processopt(self, opt): + if hasattr(opt, 'default') and opt.dest: + if not hasattr(self.option, opt.dest): + setattr(self.option, opt.dest, opt.default) + def parse(self, args): """ parse cmdline arguments into this config object. Note that this can only be called once per testing process. @@ -52,8 +60,7 @@ self.pytestplugins.consider_env() self.pytestplugins.do_addoption(self) args = [str(x) for x in args] - cmdlineoption, args = self._parser.parse_args(args) - self.option.__dict__.update(vars(cmdlineoption)) + args = self._parser.parse_setoption(args, self.option) if not args: args.append(py.std.os.getcwd()) self.topdir = gettopdir(args) @@ -132,27 +139,14 @@ """ add a named group of options to the current testing session. This function gets invoked during testing session initialization. """ - for spec in specs: - for shortopt in spec._short_opts: - if not shortopt.isupper(): - raise ValueError( - "custom options must be capital letter " - "got %r" %(spec,) - ) - return self._addoptions(groupname, *specs) - - def addoption(self, *args, **kwargs): - return self.addoptions("misc", self.Option(*args, **kwargs)) - - def _addoptions(self, groupname, *specs): - optgroup = optparse.OptionGroup(self._parser, groupname) - optgroup.add_options(specs) - self._parser.add_option_group(optgroup) - for opt in specs: - if hasattr(opt, 'default') and opt.dest: - if not hasattr(self.option, opt.dest): - setattr(self.option, opt.dest, opt.default) - return self.option + APIWARN("1.0", "define plugins to add options", stacklevel=2) + group = self._parser.addgroup(groupname) + for opt in specs: + group._addoption_instance(opt) + return self.option + + def addoption(self, *optnames, **attrs): + return self._parser.addoption(*optnames, **attrs) def getvalue(self, name, path=None): """ return 'name' value looked up from the 'options' Modified: py/branch/pytestplugin/py/test/defaultconftest.py ============================================================================== --- py/branch/pytestplugin/py/test/defaultconftest.py (original) +++ py/branch/pytestplugin/py/test/defaultconftest.py Thu Feb 26 11:31:43 2009 @@ -43,75 +43,72 @@ def adddefaultoptions(config): Option = config.Option - config._addoptions('general options', - Option('-v', '--verbose', - action="count", dest="verbose", default=0, - help="increase verbosity."), - Option('-x', '--exitfirst', + group = config._parser.addgroup("general", "general options") + group._addoption('-v', '--verbose', action="count", + dest="verbose", default=0, help="increase verbosity."), + group._addoption('-x', '--exitfirst', action="store_true", dest="exitfirst", default=False, help="exit instantly on first error or failed test."), - Option('-s', '--nocapture', + group._addoption('-s', '--nocapture', action="store_true", dest="nocapture", default=False, help="disable catching of sys.stdout/stderr output."), - Option('-k', + group._addoption('-k', action="store", dest="keyword", default='', help="only run test items matching the given " "keyword expression."), - Option('-l', '--showlocals', + group._addoption('-l', '--showlocals', action="store_true", dest="showlocals", default=False, help="show locals in tracebacks (disabled by default)."), - Option('--showskipsummary', + group._addoption('--showskipsummary', action="store_true", dest="showskipsummary", default=False, help="always show summary of skipped tests"), - Option('', '--pdb', + group._addoption('', '--pdb', action="store_true", dest="usepdb", default=False, help="start pdb (the Python debugger) on errors."), - Option('', '--tb', + group._addoption('', '--tb', action="store", dest="tbstyle", default='long', type="choice", choices=['long', 'short', 'no'], help="traceback verboseness (long/short/no)."), - Option('', '--fulltrace', + group._addoption('', '--fulltrace', action="store_true", dest="fulltrace", default=False, help="don't cut any tracebacks (default is to cut)."), - Option('', '--nomagic', + group._addoption('', '--nomagic', action="store_true", dest="nomagic", default=False, help="refrain from using magic as much as possible."), - Option('', '--traceconfig', + group._addoption('', '--traceconfig', action="store_true", dest="traceconfig", default=False, help="trace considerations of conftest.py files."), - Option('-f', '--looponfailing', + group._addoption('-f', '--looponfailing', action="store_true", dest="looponfailing", default=False, help="loop on failing test set."), - Option('', '--exec', + group._addoption('', '--exec', action="store", dest="executable", default=None, help="python executable to run the tests with."), - Option('-n', '--numprocesses', dest="numprocesses", default=0, + group._addoption('-n', '--numprocesses', dest="numprocesses", default=0, action="store", type="int", help="number of local test processes."), - Option('', '--debug', + group._addoption('', '--debug', action="store_true", dest="debug", default=False, help="turn on debugging information."), - ) - config._addoptions('EXPERIMENTAL options', - Option('-d', '--dist', + group = config._parser.addgroup("experimental", "experimental options") + group._addoption('-d', '--dist', action="store_true", dest="dist", default=False, help="ad-hoc distribute tests across machines (requires conftest settings)"), - Option('-w', '--startserver', + group._addoption('-w', '--startserver', action="store_true", dest="startserver", default=False, help="starts local web server for displaying test progress.", ), - Option('-r', '--runbrowser', + group._addoption('-r', '--runbrowser', action="store_true", dest="runbrowser", default=False, help="run browser (implies --startserver)." ), - Option('', '--boxed', + group._addoption('', '--boxed', action="store_true", dest="boxed", default=False, help="box each test run in a separate process"), - Option('', '--rest', + group._addoption('', '--rest', action='store_true', dest="restreport", default=False, help="restructured text output reporting."), - Option('', '--session', + group._addoption('', '--session', action="store", dest="session", default=None, help="lookup given sessioname in conftest.py files and use it."), - ) Modified: py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py Thu Feb 26 11:31:43 2009 @@ -96,8 +96,7 @@ assert l[0] == host.python def test_initgateway_ssh_and_remotepath(self): - from py.__.conftest import option - if not option.sshtarget: + if not py.test.config.option.sshtarget: py.test.skip("no known ssh target, use -S to set one") host = Host("%s" % (option.sshtarget, )) # this test should be careful to not write/rsync anything Modified: py/branch/pytestplugin/py/test/parseopt.py ============================================================================== --- py/branch/pytestplugin/py/test/parseopt.py (original) +++ py/branch/pytestplugin/py/test/parseopt.py Thu Feb 26 11:31:43 2009 @@ -18,17 +18,17 @@ class Parser: """ Parser for command line arguments. """ - _shortoptrestrict = True - def __init__(self, defaultget=None): + def __init__(self, usage=None, processopt=None): self._anonymous = OptionGroup("misc", parser=self) self._groups = [self._anonymous] - self._defaultget = defaultget + self._processopt = processopt + self._usage = usage def processoption(self, option): - if self._defaultget: + if self._processopt: if option.dest: - option.default = self._defaultget(option) + self._processopt(option) def addgroup(self, name, description=""): for group in self._groups: @@ -49,7 +49,7 @@ self._anonymous.addoption(*opts, **attrs) def parse(self, args): - optparser = optparse.OptionParser() + optparser = optparse.OptionParser(usage=self._usage) for group in self._groups: if group.options: optgroup = optparse.OptionGroup(optparser, group.name) @@ -57,15 +57,12 @@ optparser.add_option_group(optgroup) return optparser.parse_args(args) - def _addoptions(self, groupname, *specs): - optgroup = optparse.OptionGroup(self._parser, groupname) - optgroup.add_options(specs) - self._parser.add_option_group(optgroup) - for opt in specs: - if hasattr(opt, 'default') and opt.dest: - if not hasattr(self.option, opt.dest): - setattr(self.option, opt.dest, opt.default) - return self.option + def parse_setoption(self, args, option): + parsedoption, args = self.parse(args) + for name, value in parsedoption.__dict__.items(): + setattr(option, name, value) + return args + class OptionGroup: def __init__(self, name, description="", parser=None): @@ -76,11 +73,20 @@ def addoption(self, *optnames, **attrs): """ add an option to this group. """ - if getattr(self.parser, '_shortoptrestrict', False): - for opt in optnames: + option = py.compat.optparse.Option(*optnames, **attrs) + self._addoption_instance(option, shortupper=False) + + def _addoption(self, *optnames, **attrs): + option = py.compat.optparse.Option(*optnames, **attrs) + self._addoption_instance(option, shortupper=True) + + def _addoption_instance(self, option, shortupper=False): + if not shortupper: + for opt in option._short_opts: if opt[0] == '-' and opt[1].islower(): raise ValueError("lowercase shortoptions reserved") - option = py.compat.optparse.Option(*optnames, **attrs) if self.parser: self.parser.processoption(option) self.options.append(option) + + Modified: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_apigen.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Thu Feb 26 11:31:43 2009 @@ -35,6 +35,9 @@ tr = terminalreporter from py.__.apigen.tracer.docstorage import DocStorageAccessor terminalreporter.write_sep("=", "apigen: building documentation") + from py.__.doc.conftest import option + assert terminalreporter.config.option is option + assert hasattr(option, 'apigenpath'), option capture = py.io.StdCaptureFD() try: self.apigenscript.build( @@ -48,7 +51,7 @@ def test_generic(plugintester): plugintester.apicheck(ApigenPlugin) -def test_simple(testdir): +def test_functional_simple(testdir): sub = testdir.tmpdir.mkdir("test_simple") sub.join("__init__.py").write(py.code.Source(""" from py import initpkg Modified: py/branch/pytestplugin/py/test/plugin/pytest_pytester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pytester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pytester.py Thu Feb 26 11:31:43 2009 @@ -49,14 +49,19 @@ self.tmpdir = tmpdir.mkdir(name) self.plugins = [] self._syspathremove = [] - #self._olddir = py.std.os.getcwd() from py.__.test.config import Config self.Config = Config def finalize(self): for p in self._syspathremove: py.std.sys.path.remove(p) - #py.std.os.chdir(self._olddir) + if hasattr(self, '_olddir'): + self._olddir.chdir() + + def chdir(self): + old = self.testdir.chdir() + if not hasattr(self, '_olddir'): + self._olddir = old def _makefile(self, ext, args, kwargs): items = kwargs.items() @@ -153,7 +158,7 @@ config = self.Config() for plugin in self.plugins: if isinstance(plugin, str): - config.pytestplugins.import_module(plugin) + config.pytestplugins.import_plugin(plugin) else: config.pytestplugins.register(plugin) return config Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Thu Feb 26 11:31:43 2009 @@ -82,6 +82,7 @@ def pyevent_plugin_registered(self, plugin): if hasattr(self, '_config'): + self.pyplugins.call_plugin(plugin, "pytest_addoption", config=self._config._parser) self.pyplugins.call_plugin(plugin, "pytest_configure", config=self._config) def configure(self, config): Modified: py/branch/pytestplugin/py/test/testing/test_config.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_config.py (original) +++ py/branch/pytestplugin/py/test/testing/test_config.py Thu Feb 26 11:31:43 2009 @@ -1,15 +1,16 @@ import py +pytest_plugins = 'pytest_iocapture' + class TestConfigCmdlineParsing: @py.test.keywords(xfail="commit parser") - def test_config_addoption(self): + def test_config_addoption(self, stdcapture): from py.__.test.config import Config config = Config() config.addoption("cat1", "--option1", action="store_true") config.addoption("cat1", "--option2", action="store_true") - cap = py.io.StdCapture() config.parse(["-h"]) - out, err = cap.reset() + out, err = stdcapture.reset() assert out.count("cat1") == 1 assert out.find("option1") != -1 assert out.find("option2") != -1 @@ -28,11 +29,8 @@ help='t value'), ) """) - old = testdir.chdir() - try: - config = py.test.config._reparse(['-G', '17']) - finally: - old.chdir() + testdir.chdir() + config = py.test.config._reparse(['-G', '17']) assert config.option.gdest == 17 def test_config_cmdline_options_only_lowercase(self, testdir): Modified: py/branch/pytestplugin/py/test/testing/test_parseopt.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_parseopt.py (original) +++ py/branch/pytestplugin/py/test/testing/test_parseopt.py Thu Feb 26 11:31:43 2009 @@ -1,7 +1,15 @@ import py from py.__.test import parseopt +pytest_plugins = 'pytest_iocapture' + class TestParser: + def test_init(self, stdcapture): + parser = parseopt.Parser(usage="xyz") + py.test.raises(SystemExit, 'parser.parse(["-h"])') + out, err = stdcapture.reset() + assert out.find("xyz") != -1 + def test_group_add_and_get(self): parser = parseopt.Parser() group = parser.addgroup("hello", description="desc") @@ -25,8 +33,7 @@ group.addoption("-x", action="store_true") """) assert len(group.options) == 0 - parser._shortoptrestrict = False - group.addoption("-x", action="store_true") + group._addoption("-x", action="store_true") assert len(group.options) == 1 def test_parser_addoption(self): @@ -43,13 +50,33 @@ assert option.hello == "world" assert not args + def test_parse_will_set_default(self): + parser = parseopt.Parser() + parser.addoption("--hello", dest="hello", default="x", action="store") + option, args = parser.parse([]) + assert option.hello == "x" + del option.hello + args = parser.parse_setoption([], option) + assert option.hello == "x" + + def test_parse_setoption(self): + parser = parseopt.Parser() + parser.addoption("--hello", dest="hello", action="store") + parser.addoption("--world", dest="world", default=42) + class A: pass + option = A() + args = parser.parse_setoption(['--hello', 'world'], option) + assert option.hello == "world" + assert option.world == 42 + assert not args + def test_parse_defaultgetter(self): def defaultget(option): if option.type == "int": - return 42 + option.default = 42 elif option.type == "string": - return "world" - parser = parseopt.Parser(defaultget=defaultget) + option.default = "world" + parser = parseopt.Parser(processopt=defaultget) parser.addoption("--this", dest="this", type="int", action="store") parser.addoption("--hello", dest="hello", type="string", action="store") parser.addoption("--no", dest="no", action="store_true") Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Thu Feb 26 11:31:43 2009 @@ -154,6 +154,21 @@ config.parse([]) assert not config.option.test123 + def test_do_option_postinitialize(self, testdir): + from py.__.test.config import Config + config = Config() + config.parse([]) + config.pytestplugins.configure(config=config) + assert not hasattr(config.option, 'test123') + p = testdir.makepyfile(""" + class ConftestPlugin: + def pytest_addoption(self, config): + config.addoption('--test123', action="store_true", + default=True) + """) + config._conftest.importconftest(p) + assert config.option.test123 + def test_configure(self, testdir): config = testdir.parseconfig() l = [] From hpk at codespeak.net Thu Feb 26 12:46:53 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 12:46:53 +0100 (CET) Subject: [py-svn] r62177 - in py/branch/pytestplugin/py: . apigen cmdline/testing doc doc/example/pytest execnet/testing misc/testing path/svn/testing test test/plugin test/testing Message-ID: <20090226114653.59CA816851D@codespeak.net> Author: hpk Date: Thu Feb 26 12:46:52 2009 New Revision: 62177 Added: py/branch/pytestplugin/py/doc/pytest-plugins.txt Removed: py/branch/pytestplugin/py/doc/plugin.txt Modified: py/branch/pytestplugin/py/_com.py py/branch/pytestplugin/py/apigen/apigen.py py/branch/pytestplugin/py/cmdline/testing/test_cmdline.py py/branch/pytestplugin/py/conftest.py py/branch/pytestplugin/py/doc/conftest.py py/branch/pytestplugin/py/doc/example/pytest/test_failures.py py/branch/pytestplugin/py/doc/impl-test.txt py/branch/pytestplugin/py/doc/test_conftest.py py/branch/pytestplugin/py/execnet/testing/test_gateway.py py/branch/pytestplugin/py/misc/testing/test_com.py py/branch/pytestplugin/py/path/svn/testing/test_auth.py py/branch/pytestplugin/py/path/svn/testing/test_wccommand.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/plugin/pytest_apigen.py py/branch/pytestplugin/py/test/plugin/pytest_doctest.py py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/branch/pytestplugin/py/test/pytestplugin.py py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Log: * port py/conftest.py py/doc/conftest contents to to plugins * added and tweaked tests and access to test options everywhere Modified: py/branch/pytestplugin/py/_com.py ============================================================================== --- py/branch/pytestplugin/py/_com.py (original) +++ py/branch/pytestplugin/py/_com.py Thu Feb 26 12:46:52 2009 @@ -1,3 +1,23 @@ +""" +py lib plugins and events. + +you can write plugins that extend the py lib API. +currently this is mostly used by py.test + +registering a plugin +++++++++++++++++++++++++++++++++++ + +:: + >>> class MyPlugin: + ... def pyevent_plugin_registered(self, plugin): + ... print "registering", plugin.__class__.__name__ + ... + >>> import py + >>> py._com.pyplugins.register(MyPlugin()) + registering MyPlugin + +""" + import py class MultiCall: @@ -18,11 +38,17 @@ while self.methods: self.currentmethod = self.methods.pop() # provide call introspection if "__call__" is the first positional argument - try: + if hasattr(self.currentmethod, 'im_self'): varnames = self.currentmethod.im_func.func_code.co_varnames - except AttributeError: - varnames = () - if varnames[:2] == ('self', '__call__'): + needscall = varnames[1:2] == ('__call__',) + else: + try: + varnames = self.currentmethod.func_code.co_varnames + except AttributeError: + # builtin function + varnames = () + needscall = varnames[:1] == ('__call__',) + if needscall: res = self.currentmethod(self, *self.args, **self.kwargs) else: res = self.currentmethod(*self.args, **self.kwargs) @@ -35,7 +61,7 @@ if not firstresult: return self.results if self.results: - return self.results[0] + return self.results[-1] class PyPlugins: """ Modified: py/branch/pytestplugin/py/apigen/apigen.py ============================================================================== --- py/branch/pytestplugin/py/apigen/apigen.py (original) +++ py/branch/pytestplugin/py/apigen/apigen.py Thu Feb 26 12:46:52 2009 @@ -44,7 +44,6 @@ proj = project.Project() # output dir - from py.__.conftest import option targetdir = get_apigenpath() targetdir.ensure(dir=True) Modified: py/branch/pytestplugin/py/cmdline/testing/test_cmdline.py ============================================================================== --- py/branch/pytestplugin/py/cmdline/testing/test_cmdline.py (original) +++ py/branch/pytestplugin/py/cmdline/testing/test_cmdline.py Thu Feb 26 12:46:52 2009 @@ -2,16 +2,16 @@ pytest_plugins = "pytest_pytester" class TestPyLookup: - def test_basic(self, fstester): - p = fstester.makepyfile(hello="def x(): pass") - result = fstester.runpybin("py.lookup", "pass") + def test_basic(self, testdir): + p = testdir.makepyfile(hello="def x(): pass") + result = testdir.runpybin("py.lookup", "pass") result.stdout.fnmatch_lines( ['%s:*def x(): pass' %(p.basename)] ) - def test_search_in_filename(self, fstester): - p = fstester.makepyfile(hello="def x(): pass") - result = fstester.runpybin("py.lookup", "hello") + def test_search_in_filename(self, testdir): + p = testdir.makepyfile(hello="def x(): pass") + result = testdir.runpybin("py.lookup", "hello") result.stdout.fnmatch_lines( ['*%s:*' %(p.basename)] ) Modified: py/branch/pytestplugin/py/conftest.py ============================================================================== --- py/branch/pytestplugin/py/conftest.py (original) +++ py/branch/pytestplugin/py/conftest.py Thu Feb 26 12:46:52 2009 @@ -1,41 +1,35 @@ -#pythonexecutables = ('python2.2', 'python2.3',) -#pythonexecutable = 'python2.2' +# default values for cmdline options +pytestconfig_verbose = 0 +pytestconfig_nocapture = False +pytestconfig_collectonly = False +pytestconfig_exitfirst = False +pytestconfig_fulltrace = False +pytestconfig_showlocals = False +pytestconfig_nomagic = False -# in the future we want to be able to say here: -#def setup_module(extpy): -# mod = extpy.resolve() -# mod.module = 23 -# directory = pypath.root.dirpath() +dist_rsync_roots = ['.'] # XXX -# default values for options (modified from cmdline) -verbose = 0 -nocapture = False -collectonly = False -exitfirst = False -fulltrace = False -showlocals = False -nomagic = False +pytest_plugins = 'pytest_doctest', 'pytest_pytester' import py -Option = py.test.config.Option - -py.test.config.addoptions("py lib testing options", - Option('--sshtarget', +class PylibTestPlugin: + def pytest_addoption(self, parser): + group = parser.addgroup("pylib", "py lib testing options") + group.addoption('--sshtarget', action="store", dest="sshtarget", default=None, help=("target to run tests requiring ssh, e.g. " - "user at codespeak.net")), - Option('--apigenpath', + "user at codespeak.net")) + group.addoption('--apigenpath', action="store", dest="apigenpath", default="../apigen", type="string", - help="relative path to apigen doc output location (relative from py/)"), - Option('--docpath', + help="relative path to apigen doc output location (relative from py/)") + group.addoption('--docpath', action='store', dest='docpath', default="doc", type='string', - help="relative path to doc output location (relative from py/)"), - Option('--runslowtests', + help="relative path to doc output location (relative from py/)") + group.addoption('--runslowtests', action="store_true", dest="runslowtests", default=False, - help="run slow tests)"), - ) + help="run slow tests)") +ConftestPlugin = PylibTestPlugin -dist_rsync_roots = ['.'] Modified: py/branch/pytestplugin/py/doc/conftest.py ============================================================================== --- py/branch/pytestplugin/py/doc/conftest.py (original) +++ py/branch/pytestplugin/py/doc/conftest.py Thu Feb 26 12:46:52 2009 @@ -10,30 +10,30 @@ TIMEOUT_URLOPEN = 5.0 -Option = py.test.config.Option -option = py.test.config.addoptions("documentation check options", - Option('-R', '--checkremote', +class RestDocPlugin: + def pytest_addoption(self, parser): + parser.addgroup("ReST", "ReST documentation check options") + parser.addoption('-R', '--checkremote', action="store_true", dest="checkremote", default=False, help="urlopen() remote links found in ReST text files.", - ), - Option('', '--forcegen', + ) + parser.addoption('--forcegen', action="store_true", dest="forcegen", default=False, help="force generation of html files even if they appear up-to-date" - ), -) + ) + +ConftestPlugin = RestDocPlugin def get_apigenpath(): - from py.__.conftest import option path = os.environ.get('APIGENPATH') if path is None: - path = option.apigenpath + path = py.test.config.option.apigenpath return pypkgdir.join(path, abs=True) def get_docpath(): - from py.__.conftest import option path = os.environ.get('DOCPATH') if path is None: - path = option.docpath + path = py.test.config.option.docpath return pypkgdir.join(path, abs=True) def get_apigen_relpath(): @@ -96,7 +96,7 @@ py.test.fail("docutils processing failed, see captured stderr") def _checkskip(lpath, htmlpath=None): - if not option.forcegen: + if not py.test.config.option.forcegen: lpath = py.path.local(lpath) if htmlpath is not None: htmlpath = py.path.local(htmlpath) @@ -199,7 +199,7 @@ continue tryfn = l[1].strip() if tryfn.startswith('http:') or tryfn.startswith('https'): - if option.checkremote: + if py.test.config.option.checkremote: yield urlcheck, tryfn, path, lineno elif tryfn.startswith('webcal:'): continue Modified: py/branch/pytestplugin/py/doc/example/pytest/test_failures.py ============================================================================== --- py/branch/pytestplugin/py/doc/example/pytest/test_failures.py (original) +++ py/branch/pytestplugin/py/doc/example/pytest/test_failures.py Thu Feb 26 12:46:52 2009 @@ -2,14 +2,13 @@ import py failure_demo = py.magic.autopath().dirpath('failure_demo.py') -from py.__.test.testing import suptest -from py.__.test import event +pytest_plugins = "pytest_pytester" -def test_failure_demo_fails_properly(): - sorter = suptest.events_from_cmdline([failure_demo]) +def test_failure_demo_fails_properly(testdir): + sorter = testdir.inline_run(failure_demo) passed, skipped, failed = sorter.countoutcomes() assert passed == 0 assert failed == 20, failed - colreports = sorter.get(event.CollectionReport) + colreports = sorter.getnamed("collectionreport") failed = len([x.failed for x in colreports]) assert failed == 5 Modified: py/branch/pytestplugin/py/doc/impl-test.txt ============================================================================== --- py/branch/pytestplugin/py/doc/impl-test.txt (original) +++ py/branch/pytestplugin/py/doc/impl-test.txt Thu Feb 26 12:46:52 2009 @@ -272,69 +272,3 @@ .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev -pytest plugins -================== - - -specifying plugins for directories or test modules ---------------------------------------------------------- - -py.test loads and configures plugins at tool startup and whenever -it encounters new confest or test modules which -contain a ``pytest_plugins`` definition. At tool -startup the ``PYTEST_PLUGINS`` environment variable -is considered as well. - -Example -++++++++++ -If you create a ``conftest.py file with the following content:: - - pytest_plugins = "pytest_plugin1", MyLocalPluginClass - -then test execution within that directory can make use -of the according instantiated plugins: - -* the module ``pytest_plugin1`` will be imported and - and its contained `Plugin1`` class instantiated. - A plugin module can put its dependencies into - a "pytest_plugins" attribute at module level as well. - -* the ``MyLocalPluginClass`` will be instantiated - and added to the pluginmanager. - - -Plugin methods ----------------------------------- - -A Plugin class may implement the following attributes and methods: - -* pytest_cmdlineoptions: a list of optparse-style py.test.config.Option objects -* pytest_configure(self, config): called after command line options have been parsed -* pytest_unconfigure(self, config): called before the test process quits -* pytest_event(self, event): called for each `pytest event`_ - -XXX reference APIcheck'ed full documentation - -_`pytest event`: - -Pytest Events -------------------- - -XXX Various reporting events. - -Example plugins ------------------------ - -XXX here are a few existing plugins: - -* adding reporting facilities, e.g. - pytest_terminal: default reporter for writing info to terminals - pytest_resultlog: log test results in machine-readable form to a file - pytest_eventlog: log all internal pytest events to a file - pytest_xfail: "expected to fail" test marker - pytest_tmpdir: provide temporary directories to test functions - pytest_plugintester: generic apichecks, support for functional plugin tests - pytest_pytester: support for testing py.test runs - -* extending test execution, e.g. - pytest_apigen: tracing values of function/method calls when running tests Deleted: /py/branch/pytestplugin/py/doc/plugin.txt ============================================================================== --- /py/branch/pytestplugin/py/doc/plugin.txt Thu Feb 26 12:46:52 2009 +++ (empty file) @@ -1,56 +0,0 @@ -py lib plugins ----------------- - -you can write plugins that extend the py lib API -or implement certain hooks. - -registering a plugin -++++++++++++++++++++++++++++++++++ - -:: - >>> class MyPlugin: - ... def pyevent_pluginregistered(self, plugin): - ... print "registering", plugin.__class__.__name__ - ... - >>> import py - >>> py.plugin.register(MyPlugin()) - registering MyPlugin - -logging all events caused by py lib -++++++++++++++++++++++++++++++++++++++++ - -you can log all py lib generated events by setting -an environment variable, for example on linux:: - - export PYEVENTLOG="/tmp/mylog" - -automatically importing plugins/modules -+++++++++++++++++++++++++++++++++++++++++ - -XXX - -you can set the environment variable ``PY_AUTOIMPORT=mymodule`` -to ensure that a module gets imported automatically at -py lib import time. - - -py lib events ----------------- - -sending events -+++++++++++++++++ - -example sending events:: - - class myobj: - pass - py.bus.notify(myeventname=myobj) - -subscribing to all events -+++++++++++++++++++++++++++++ - -example subscribing to all send objects:: - - l = [] - py.bus.subscribe(l.append) # anonymous subscription - py.bus.subscribe(myeventname=l.append) # named subscription Added: py/branch/pytestplugin/py/doc/pytest-plugins.txt ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/doc/pytest-plugins.txt Thu Feb 26 12:46:52 2009 @@ -0,0 +1,67 @@ + +pytest plugins +================== + +specifying plugins for directories or test modules +--------------------------------------------------------- + +py.test loads and configures plugins at tool startup and whenever +it encounters new confest or test modules which +contain a ``pytest_plugins`` definition. At tool +startup the ``PYTEST_PLUGINS`` environment variable +is considered as well. + +Example +++++++++++ + +If you create a ``conftest.py`` file with the following content:: + + pytest_plugins = "pytest_plugin1", MyLocalPluginClass + +then test execution within that directory can make use +of the according instantiated plugins: + +* the module ``pytest_plugin1`` will be imported and + and its contained `Plugin1`` class instantiated. + A plugin module can put its dependencies into + a "pytest_plugins" attribute at module level as well. + +* the ``MyLocalPluginClass`` will be instantiated + and added to the pluginmanager. + + +Plugin methods +---------------------------------- + +A Plugin class may implement the following attributes and methods: + +* pytest_cmdlineoptions: a list of optparse-style py.test.config.Option objects +* pytest_configure(self, config): called after command line options have been parsed +* pytest_unconfigure(self, config): called before the test process quits +* pytest_event(self, event): called for each `pytest event`_ + +XXX reference APIcheck'ed full documentation + +_`pytest event`: + +Pytest Events +------------------- + +XXX Various reporting events. + +Example plugins +----------------------- + +XXX here are a few existing plugins: + +* adding reporting facilities, e.g. + pytest_terminal: default reporter for writing info to terminals + pytest_resultlog: log test results in machine-readable form to a file + pytest_eventlog: log all internal pytest events to a file + pytest_xfail: "expected to fail" test marker + pytest_tmpdir: provide temporary directories to test functions + pytest_plugintester: generic apichecks, support for functional plugin tests + pytest_pytester: support for testing py.test runs + +* extending test execution, e.g. + pytest_apigen: tracing values of function/method calls when running tests Modified: py/branch/pytestplugin/py/doc/test_conftest.py ============================================================================== --- py/branch/pytestplugin/py/doc/test_conftest.py (original) +++ py/branch/pytestplugin/py/doc/test_conftest.py Thu Feb 26 12:46:52 2009 @@ -1,30 +1,31 @@ import py from py.__.test import event -from py.__.test.testing import suptest from py.__.doc import conftest as doc_conftest +pytest_plugins = "pytest_pytester" -class TestDoctest(suptest.InlineCollection): - def setup_method(self, method): - super(TestDoctest, self).setup_method(method) - p = py.path.local(doc_conftest.__file__) - if p.ext == ".pyc": - p = p.new(ext=".py") - p.copy(self.tmpdir.join("conftest.py")) +def pytest_pyfuncarg_testdir(__call__, pyfuncitem): + testdir = __call__.execute(firstresult=True) + p = py.path.local(doc_conftest.__file__) + if p.ext == ".pyc": + p = p.new(ext=".py") + p.copy(testdir.tmpdir.join("conftest.py")) + return testdir - def test_doctest_extra_exec(self): - xtxt = self.maketxtfile(x=""" +class TestDoctest: + def test_doctest_extra_exec(self, testdir): + xtxt = testdir.maketxtfile(x=""" hello:: .. >>> raise ValueError >>> None """) - sorter = suptest.events_from_cmdline([xtxt]) + sorter = testdir.inline_run(xtxt) passed, skipped, failed = sorter.countoutcomes() assert failed == 1 - def test_doctest_basic(self): - xtxt = self.maketxtfile(x=""" + def test_doctest_basic(self, testdir): + xtxt = testdir.maketxtfile(x=""" .. >>> from os.path import abspath @@ -42,33 +43,33 @@ end """) - sorter = suptest.events_from_cmdline([xtxt]) + sorter = testdir.inline_run(xtxt) passed, skipped, failed = sorter.countoutcomes() assert failed == 0 assert passed + skipped == 2 - def test_doctest_eol(self): - ytxt = self.maketxtfile(y=".. >>> 1 + 1\r\n 2\r\n\r\n") - sorter = suptest.events_from_cmdline([ytxt]) + def test_doctest_eol(self, testdir): + ytxt = testdir.maketxtfile(y=".. >>> 1 + 1\r\n 2\r\n\r\n") + sorter = testdir.inline_run(ytxt) passed, skipped, failed = sorter.countoutcomes() assert failed == 0 assert passed + skipped == 2 - def test_doctest_indentation(self): - footxt = self.maketxtfile(foo= + def test_doctest_indentation(self, testdir): + footxt = testdir.maketxtfile(foo= '..\n >>> print "foo\\n bar"\n foo\n bar\n') - sorter = suptest.events_from_cmdline([footxt]) + sorter = testdir.inline_run(footxt) passed, skipped, failed = sorter.countoutcomes() assert failed == 0 assert skipped + passed == 2 - def test_js_ignore(self): - xtxt = self.maketxtfile(xtxt=""" + def test_js_ignore(self, testdir): + xtxt = testdir.maketxtfile(xtxt=""" `blah`_ .. _`blah`: javascript:some_function() """) - sorter = suptest.events_from_cmdline([xtxt]) + sorter = testdir.inline_run(xtxt) passed, skipped, failed = sorter.countoutcomes() assert failed == 0 assert skipped + passed == 3 Modified: py/branch/pytestplugin/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/pytestplugin/py/execnet/testing/test_gateway.py (original) +++ py/branch/pytestplugin/py/execnet/testing/test_gateway.py Thu Feb 26 12:46:52 2009 @@ -2,7 +2,6 @@ import os, sys, time, signal import py from py.__.execnet import gateway -from py.__.conftest import option mypath = py.magic.autopath() from StringIO import StringIO @@ -571,16 +570,16 @@ class TestSshGateway(BasicRemoteExecution): def setup_class(cls): - if option.sshtarget is None: - py.test.skip("no known ssh target, use -S to set one") - cls.gw = py.execnet.SshGateway(option.sshtarget) + if py.test.config.option.sshtarget is None: + py.test.skip("no known ssh target, use --sshtarget to set one") + cls.gw = py.execnet.SshGateway(py.test.config.option.sshtarget) def test_sshconfig_functional(self): tmpdir = py.test.ensuretemp("test_sshconfig") ssh_config = tmpdir.join("ssh_config") ssh_config.write( "Host alias123\n" - " HostName %s\n" % (option.sshtarget,)) + " HostName %s\n" % (py.test.config.option.sshtarget,)) gw = py.execnet.SshGateway("alias123", ssh_config=ssh_config) assert gw._cmd.find("-F") != -1 assert gw._cmd.find(str(ssh_config)) != -1 @@ -588,7 +587,7 @@ gw.exit() def test_sshaddress(self): - assert self.gw.remoteaddress == option.sshtarget + assert self.gw.remoteaddress == py.test.config.option.sshtarget def test_connexion_failes_on_non_existing_hosts(self): py.test.raises(IOError, Modified: py/branch/pytestplugin/py/misc/testing/test_com.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_com.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_com.py Thu Feb 26 12:46:52 2009 @@ -38,6 +38,18 @@ assert call.execute() == [23] assert call.execute(firstresult=True) == 23 + def test_call_subexecute(self): + def m(__call__): + subresult = __call__.execute(firstresult=True) + return subresult + 1 + + def n(): + return 1 + + call = MultiCall([n, m]) + res = call.execute(firstresult=True) + assert res == 2 + class TestPyPlugins: def test_MultiCall(self): plugins = PyPlugins() Modified: py/branch/pytestplugin/py/path/svn/testing/test_auth.py ============================================================================== --- py/branch/pytestplugin/py/path/svn/testing/test_auth.py (original) +++ py/branch/pytestplugin/py/path/svn/testing/test_auth.py Thu Feb 26 12:46:52 2009 @@ -3,7 +3,6 @@ import svntestbase from threading import Thread import time -from py.__.conftest import option def make_repo_auth(repo, userdata): """ write config to repo @@ -251,7 +250,7 @@ class SvnAuthFunctionalTestBase(object): def setup_class(cls): svntestbase.getsvnbin() - if not option.runslowtests: + if not py.test.config.option.runslowtests: py.test.skip('skipping slow functional tests - use --runslowtests ' 'to override') Modified: py/branch/pytestplugin/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/branch/pytestplugin/py/path/svn/testing/test_wccommand.py (original) +++ py/branch/pytestplugin/py/path/svn/testing/test_wccommand.py Thu Feb 26 12:46:52 2009 @@ -4,7 +4,6 @@ from py.__.path.svn.wccommand import InfoSvnWCCommand, XMLWCStatus from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn import svncommon -from py.__.conftest import option if sys.platform != 'win32': def normpath(p): @@ -157,7 +156,7 @@ self.root.revert(rec=1) def test_status_conflict(self): - if not option.runslowtests: + if not py.test.config.option.runslowtests: py.test.skip('skipping slow unit tests - use --runslowtests ' 'to override') wc = self.root @@ -177,7 +176,7 @@ assert [x.basename for x in s.conflict] == ['conflictsamplefile'] def test_status_external(self): - if not option.runslowtests: + if not py.test.config.option.runslowtests: py.test.skip('skipping slow unit tests - use --runslowtests ' 'to override') otherrepo, otherwc = getrepowc('externalrepo', 'externalwc') Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Thu Feb 26 12:46:52 2009 @@ -58,7 +58,7 @@ adddefaultoptions(self) self._conftest.setinitial(args) self.pytestplugins.consider_env() - self.pytestplugins.do_addoption(self) + self.pytestplugins.do_addoption(self._parser) args = [str(x) for x in args] args = self._parser.parse_setoption(args, self.option) if not args: Modified: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_apigen.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Thu Feb 26 12:46:52 2009 @@ -1,8 +1,8 @@ import py class ApigenPlugin: - def pytest_addoption(self, config): - config.addoption('--apigen', action="store_true", dest="apigen", + def pytest_addoption(self, parser): + parser.addoption('--apigen', action="store_true", dest="apigen", help="generate api documentation") # "--apigen-script" argument pointing to a script"), @@ -35,9 +35,6 @@ tr = terminalreporter from py.__.apigen.tracer.docstorage import DocStorageAccessor terminalreporter.write_sep("=", "apigen: building documentation") - from py.__.doc.conftest import option - assert terminalreporter.config.option is option - assert hasattr(option, 'apigenpath'), option capture = py.io.StdCaptureFD() try: self.apigenscript.build( Modified: py/branch/pytestplugin/py/test/plugin/pytest_doctest.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_doctest.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_doctest.py Thu Feb 26 12:46:52 2009 @@ -1,8 +1,8 @@ import py class DoctestPlugin: - def pytest_addoption(self, config): - config.addoption("--doctest-modules", + def pytest_addoption(self, parser): + parser.addoption("--doctest-modules", action="store_true", dest="doctestmodules") def pytest_collect_file(self, path, parent): Modified: py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py Thu Feb 26 12:46:52 2009 @@ -2,8 +2,8 @@ class EventlogPlugin: """ log pytest events to a file. """ - def pytest_addoption(self, config): - config.addoption("--eventlog", dest="eventlog", + def pytest_addoption(self, parser): + parser.addoption("--eventlog", dest="eventlog", help="write all pytest events to the given file.") def pytest_configure(self, config): Modified: py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py Thu Feb 26 12:46:52 2009 @@ -94,7 +94,7 @@ def __init__(self): """ usually called only once per test process. """ - def pytest_addoption(self, config): + def pytest_addoption(self, parser): """ called before commandline parsing. """ def pytest_configure(self, config): Modified: py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py Thu Feb 26 12:46:52 2009 @@ -11,8 +11,8 @@ class PocooPlugin(object): """ report URLs from sending test failures to the pocoo paste service. """ - def pytest_addoption(self, config): - config.addoption('--pocoo-sendfailures', + def pytest_addoption(self, parser): + parser.addoption('--pocoo-sendfailures', action='store_true', dest="pocoo_sendfailures", help="send failures to %s" %(url.base,)) Modified: py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py Thu Feb 26 12:46:52 2009 @@ -4,8 +4,8 @@ """resultlog plugin for machine-readable logging of test results. Useful for buildbot integration code. """ - def pytest_addoption(self, config): - config.addoption('--resultlog', action="store", dest="resultlog", + def pytest_addoption(self, parser): + parser.addoption('--resultlog', action="store", dest="resultlog", help="path for machine-readable result log") def pytest_configure(self, config): Modified: py/branch/pytestplugin/py/test/plugin/pytest_terminal.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_terminal.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_terminal.py Thu Feb 26 12:46:52 2009 @@ -3,8 +3,8 @@ class TerminalPlugin(object): """ Report a test run to a terminal. """ - def pytest_addoption(self, config): - config.addoption('--collectonly', + def pytest_addoption(self, parser): + parser.addoption('--collectonly', action="store_true", dest="collectonly", help="only collect tests, don't execute them."), Modified: py/branch/pytestplugin/py/test/pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/pytestplugin.py Thu Feb 26 12:46:52 2009 @@ -77,12 +77,12 @@ def notify(self, eventname, *args, **kwargs): return self.pyplugins.notify(eventname, *args, **kwargs) - def do_addoption(self, config): - self.pyplugins.call_each('pytest_addoption', config=config) + def do_addoption(self, parser): + self.pyplugins.call_each('pytest_addoption', parser=parser) def pyevent_plugin_registered(self, plugin): if hasattr(self, '_config'): - self.pyplugins.call_plugin(plugin, "pytest_addoption", config=self._config._parser) + self.pyplugins.call_plugin(plugin, "pytest_addoption", parser=self._config._parser) self.pyplugins.call_plugin(plugin, "pytest_configure", config=self._config) def configure(self, config): Modified: py/branch/pytestplugin/py/test/testing/test_pytestplugin.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_pytestplugin.py (original) +++ py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Thu Feb 26 12:46:52 2009 @@ -145,8 +145,8 @@ from py.__.test.config import Config p = testdir.makepyfile(""" class ConftestPlugin: - def pytest_addoption(self, config): - config.addoption('--test123', action="store_true") + def pytest_addoption(self, parser): + parser.addoption('--test123', action="store_true") """) config = Config() config._conftest.importconftest(p) @@ -162,8 +162,8 @@ assert not hasattr(config.option, 'test123') p = testdir.makepyfile(""" class ConftestPlugin: - def pytest_addoption(self, config): - config.addoption('--test123', action="store_true", + def pytest_addoption(self, parser): + parser.addoption('--test123', action="store_true", default=True) """) config._conftest.importconftest(p) From hpk at codespeak.net Thu Feb 26 14:23:29 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 14:23:29 +0100 (CET) Subject: [py-svn] r62187 - in py/branch/pytestplugin/py: . apigen doc execnet/testing test/dsession/testing test/plugin Message-ID: <20090226132329.B16861684D2@codespeak.net> Author: hpk Date: Thu Feb 26 14:23:29 2009 New Revision: 62187 Modified: py/branch/pytestplugin/py/apigen/apigen.py py/branch/pytestplugin/py/conftest.py py/branch/pytestplugin/py/doc/confrest.py py/branch/pytestplugin/py/doc/conftest.py py/branch/pytestplugin/py/doc/contact.txt py/branch/pytestplugin/py/doc/future.txt py/branch/pytestplugin/py/doc/path.txt py/branch/pytestplugin/py/doc/xml.txt py/branch/pytestplugin/py/execnet/testing/test_gateway.py py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Log: * shuffling py/doc/conftest and confrest to become a bit more sane. * renaming options Modified: py/branch/pytestplugin/py/apigen/apigen.py ============================================================================== --- py/branch/pytestplugin/py/apigen/apigen.py (original) +++ py/branch/pytestplugin/py/apigen/apigen.py Thu Feb 26 14:23:29 2009 @@ -36,7 +36,7 @@ not p.basename.startswith('.') and str(p).find('c-extension%sgreenlet%sbuild' % (p.sep, p.sep)) == -1) -def build(pkgdir, dsa, capture): +def build(config, pkgdir, dsa, capture): # create a linker (link database) for cross-linking l = linker.TempLinker() @@ -44,7 +44,7 @@ proj = project.Project() # output dir - targetdir = get_apigenpath() + targetdir = get_apigenpath(config=config) targetdir.ensure(dir=True) # find out what to build Modified: py/branch/pytestplugin/py/conftest.py ============================================================================== --- py/branch/pytestplugin/py/conftest.py (original) +++ py/branch/pytestplugin/py/conftest.py Thu Feb 26 14:23:29 2009 @@ -15,19 +15,10 @@ class PylibTestPlugin: def pytest_addoption(self, parser): group = parser.addgroup("pylib", "py lib testing options") - group.addoption('--sshtarget', - action="store", dest="sshtarget", default=None, + group.addoption('--sshhost', + action="store", dest="sshhost", default=None, help=("target to run tests requiring ssh, e.g. " "user at codespeak.net")) - group.addoption('--apigenpath', - action="store", dest="apigenpath", - default="../apigen", - type="string", - help="relative path to apigen doc output location (relative from py/)") - group.addoption('--docpath', - action='store', dest='docpath', - default="doc", type='string', - help="relative path to doc output location (relative from py/)") group.addoption('--runslowtests', action="store_true", dest="runslowtests", default=False, help="run slow tests)") Modified: py/branch/pytestplugin/py/doc/confrest.py ============================================================================== --- py/branch/pytestplugin/py/doc/confrest.py (original) +++ py/branch/pytestplugin/py/doc/confrest.py Thu Feb 26 14:23:29 2009 @@ -1,9 +1,10 @@ import py from py.__.misc.rest import convert_rest_html, strip_html_header from py.__.misc.difftime import worded_time -from py.__.doc.conftest import get_apigenpath, get_docpath from py.__.apigen.linker import relpath +mydir = py.magic.autopath().dirpath() + html = py.xml.html class Page(object): @@ -25,13 +26,13 @@ self.fill() def a_docref(self, name, relhtmlpath): - docpath = self.project.get_docpath() + docpath = self.project.docpath return html.a(name, class_="menu", href=relpath(self.targetpath.strpath, docpath.join(relhtmlpath).strpath)) def a_apigenref(self, name, relhtmlpath): - apipath = get_apigenpath() + apipath = self.project.apigenpath return html.a(name, class_="menu", href=relpath(self.targetpath.strpath, apipath.join(relhtmlpath).strpath)) @@ -106,9 +107,6 @@ class Project: - mydir = py.magic.autopath().dirpath() - # string for url, path for local file - stylesheet = mydir.join('style.css') title = "py lib" prefix_title = "" # we have a logo already containing "py lib" encoding = 'latin1' @@ -119,31 +117,49 @@ href="http://codespeak.net")) Page = PyPage + def __init__(self, sourcepath=None): + if sourcepath is None: + sourcepath = mydir + self.setpath(sourcepath) + + def setpath(self, sourcepath, docpath=None, + apigenpath=None, stylesheet=None): + self.sourcepath = sourcepath + if docpath is None: + docpath = sourcepath + self.docpath = docpath + if apigenpath is None: + apigenpath = docpath + self.apigenpath = apigenpath + if stylesheet is None: + p = sourcepath.join("style.css") + if p.check(): + self.stylesheet = p + else: + p = py.path.local(stylesheet) + if p.check(): + stylesheet = p + self.stylesheet = stylesheet def get_content(self, txtpath, encoding): return unicode(txtpath.read(), encoding) - def get_docpath(self): - return get_docpath() - def get_htmloutputpath(self, txtpath): - docpath = self.get_docpath() - reloutputpath = txtpath.new(ext='.html').relto(self.mydir) - return docpath.join(reloutputpath) + reloutputpath = txtpath.new(ext='.html').relto(self.sourcepath) + return self.docpath.join(reloutputpath) def process(self, txtpath): encoding = self.encoding content = self.get_content(txtpath, encoding) - docpath = self.get_docpath() outputpath = self.get_htmloutputpath(txtpath) stylesheet = self.stylesheet - if isinstance(self.stylesheet, py.path.local): - if not docpath.join(stylesheet.basename).check(): + if isinstance(stylesheet, py.path.local): + if not self.docpath.join(stylesheet.basename).check(): docpath.ensure(dir=True) stylesheet.copy(docpath) stylesheet = relpath(outputpath.strpath, - docpath.join(stylesheet.basename).strpath) + self.docpath.join(stylesheet.basename).strpath) content = convert_rest_html(content, txtpath, stylesheet=stylesheet, encoding=encoding) Modified: py/branch/pytestplugin/py/doc/conftest.py ============================================================================== --- py/branch/pytestplugin/py/doc/conftest.py (original) +++ py/branch/pytestplugin/py/doc/conftest.py Thu Feb 26 14:23:29 2009 @@ -5,36 +5,40 @@ import os pypkgdir = py.path.local(py.__file__).dirpath() - -mypath = py.magic.autopath().dirpath() - -TIMEOUT_URLOPEN = 5.0 +mydir = py.magic.autopath().dirpath() class RestDocPlugin: def pytest_addoption(self, parser): - parser.addgroup("ReST", "ReST documentation check options") - parser.addoption('-R', '--checkremote', - action="store_true", dest="checkremote", default=False, - help="urlopen() remote links found in ReST text files.", - ) - parser.addoption('--forcegen', + group = parser.addgroup("ReST", "ReST documentation check options") + group.addoption('-R', '--urlcheck', + action="store_true", dest="urlcheck", default=False, + help="urlopen() remote links found in ReST text files.") + group.addoption('--urlcheck-timeout', action="store", + type="int", dest="urlcheck_timeout", default=5, + help="timeout in seconds for urlcheck") + group.addoption('--forcegen', action="store_true", dest="forcegen", default=False, - help="force generation of html files even if they appear up-to-date" - ) + help="force generation of html files even if they appear up-to-date") ConftestPlugin = RestDocPlugin def get_apigenpath(): + path = os.environ.get('DOCPATH') + if path is None: + return mydir + else: + return py.path.local().join(path, abs=True) path = os.environ.get('APIGENPATH') if path is None: - path = py.test.config.option.apigenpath + path = "apigen" return pypkgdir.join(path, abs=True) def get_docpath(): path = os.environ.get('DOCPATH') if path is None: - path = py.test.config.option.docpath - return pypkgdir.join(path, abs=True) + return mydir + else: + return py.path.local().join(path, abs=True) def get_apigen_relpath(): return relpath(get_docpath().strpath + '/', @@ -68,48 +72,47 @@ directive.register_linkrole('source', resolve_linkrole) _initialized = True -def restcheck(path): - localpath = path - if hasattr(path, 'localpath'): - localpath = path.localpath - checkdocutils() - import docutils.utils - - try: - cur = localpath - for x in cur.parts(reverse=True): - confrest = x.dirpath('confrest.py') - if confrest.check(file=1): - confrest = confrest.pyimport() - project = confrest.Project() - _checkskip(path, project.get_htmloutputpath(path)) - project.process(path) - break - else: - # defer to default processor - _checkskip(path) - rest.process(path) - except KeyboardInterrupt: - raise - except docutils.utils.SystemMessage: - # we assume docutils printed info on stdout - py.test.fail("docutils processing failed, see captured stderr") - -def _checkskip(lpath, htmlpath=None): - if not py.test.config.option.forcegen: - lpath = py.path.local(lpath) - if htmlpath is not None: - htmlpath = py.path.local(htmlpath) - if lpath.ext == '.txt': - htmlpath = htmlpath or lpath.new(ext='.html') - if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime(): - py.test.skip("html file is up to date, use --forcegen to regenerate") - #return [] # no need to rebuild class ReSTSyntaxTest(py.test.collect.Item): def runtest(self): - mypath = self.fspath - restcheck(py.path.svnwc(mypath)) + self.restcheck(py.path.svnwc(self.fspath)) + + def restcheck(self, path): + localpath = path + if hasattr(path, 'localpath'): + localpath = path.localpath + checkdocutils() + import docutils.utils + try: + cur = localpath + for x in cur.parts(reverse=True): + confrest = x.dirpath('confrest.py') + if confrest.check(file=1): + confrest = confrest.pyimport() + project = confrest.Project() + self._checkskip(path, project.get_htmloutputpath(path)) + project.process(path) + break + else: + # defer to default processor + self._checkskip(path) + rest.process(path) + except KeyboardInterrupt: + raise + except docutils.utils.SystemMessage: + # we assume docutils printed info on stdout + py.test.fail("docutils processing failed, see captured stderr") + + def _checkskip(self, lpath, htmlpath=None): + if not self._config.getvalue("forcegen"): + lpath = py.path.local(lpath) + if htmlpath is not None: + htmlpath = py.path.local(htmlpath) + if lpath.ext == '.txt': + htmlpath = htmlpath or lpath.new(ext='.html') + if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime(): + py.test.skip("html file is up to date, use --forcegen to regenerate") + #return [] # no need to rebuild class DoctestText(py.test.collect.Item): def runtest(self): @@ -156,13 +159,39 @@ class LinkCheckerMaker(py.test.collect.Collector): def collect(self): - l = [] - for call, tryfn, path, lineno in genlinkchecks(self.fspath): - name = "%s:%d" %(tryfn, lineno) - l.append( - CheckLink(name, parent=self, args=(tryfn, path, lineno), callobj=call) - ) - return l + return list(self.genlinkchecks()) + + def genlinkchecks(self): + path = self.fspath + # generating functions + args as single tests + timeout = self._config.getvalue("urlcheck_timeout") + for lineno, line in py.builtin.enumerate(path.readlines()): + line = line.strip() + if line.startswith('.. _'): + if line.startswith('.. _`'): + delim = '`:' + else: + delim = ':' + l = line.split(delim, 1) + if len(l) != 2: + continue + tryfn = l[1].strip() + name = "%s:%d" %(tryfn, lineno) + if tryfn.startswith('http:') or tryfn.startswith('https'): + if self._config.getvalue("urlcheck"): + yield CheckLink(name, parent=self, + args=(tryfn, path, lineno, timeout), callobj=urlcheck) + elif tryfn.startswith('webcal:'): + continue + else: + i = tryfn.find('#') + if i != -1: + checkfn = tryfn[:i] + else: + checkfn = tryfn + if checkfn.strip() and (1 or checkfn.endswith('.html')): + yield CheckLink(name, parent=self, + args=(tryfn, path, lineno), callobj=localrefcheck) class CheckLink(py.test.collect.Function): def repr_metainfo(self): @@ -185,34 +214,8 @@ self.DoctestText("doctest", self), ] -# generating functions + args as single tests -def genlinkchecks(path): - for lineno, line in py.builtin.enumerate(path.readlines()): - line = line.strip() - if line.startswith('.. _'): - if line.startswith('.. _`'): - delim = '`:' - else: - delim = ':' - l = line.split(delim, 1) - if len(l) != 2: - continue - tryfn = l[1].strip() - if tryfn.startswith('http:') or tryfn.startswith('https'): - if py.test.config.option.checkremote: - yield urlcheck, tryfn, path, lineno - elif tryfn.startswith('webcal:'): - continue - else: - i = tryfn.find('#') - if i != -1: - checkfn = tryfn[:i] - else: - checkfn = tryfn - if checkfn.strip() and (1 or checkfn.endswith('.html')): - yield localrefcheck, tryfn, path, lineno -def urlcheck(tryfn, path, lineno): +def urlcheck(tryfn, path, lineno, TIMEOUT_URLOPEN): old = py.std.socket.getdefaulttimeout() py.std.socket.setdefaulttimeout(TIMEOUT_URLOPEN) try: @@ -222,7 +225,7 @@ finally: py.std.socket.setdefaulttimeout(old) except (py.std.urllib2.URLError, py.std.urllib2.HTTPError), e: - if e.code in (401, 403): # authorization required, forbidden + if getattr(e, 'code', None) in (401, 403): # authorization required, forbidden py.test.skip("%s: %s" %(tryfn, str(e))) else: py.test.fail("remote reference error %r in %s:%d\n%s" %( Modified: py/branch/pytestplugin/py/doc/contact.txt ============================================================================== --- py/branch/pytestplugin/py/doc/contact.txt (original) +++ py/branch/pytestplugin/py/doc/contact.txt Thu Feb 26 14:23:29 2009 @@ -16,8 +16,6 @@ .. _`merlinux.eu`: http://merlinux.eu -.. _`zope3`: http://zope3.zwiki.org/ -.. _twisted: http://www.twistedmatrix.org .. _future: future.html .. _`get an account`: Modified: py/branch/pytestplugin/py/doc/future.txt ============================================================================== --- py/branch/pytestplugin/py/doc/future.txt (original) +++ py/branch/pytestplugin/py/doc/future.txt Thu Feb 26 14:23:29 2009 @@ -105,10 +105,7 @@ In other words, implementing a `memoryfs`_ or a `dictfs`_ would give you two things for free: a filesystem mountable at kernel level as well as a uniform "path" object allowing you to access your -filesystem in convenient ways. (At some point it might -even become interesting to think about interfacing to -`reiserfs v4 features`_ at the Filesystem level but that -is a can of subsequent worms). +filesystem in convenient ways. Also interesting to check out is Will McGugan's work on his `fs package`_. @@ -128,7 +125,6 @@ .. _`dictfs`: http://codespeak.net/pipermail/py-dev/2005-January/000191.html .. _`pylufs`: http://codespeak.net/svn/user/arigo/hack/pylufs/ .. _`pyfuse`: http://codespeak.net/svn/user/arigo/hack/pyfuse/ -.. _`reiserfs v4 features`: http://www.namesys.com/v4/v4.html Integrate interactive completion Modified: py/branch/pytestplugin/py/doc/path.txt ============================================================================== --- py/branch/pytestplugin/py/doc/path.txt (original) +++ py/branch/pytestplugin/py/doc/path.txt Thu Feb 26 14:23:29 2009 @@ -52,7 +52,7 @@ Some example usage of :api:`py.path.svnurl`:: .. >>> import py - .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') + .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> url = py.path.svnurl('http://codespeak.net/svn/py') >>> info = url.info() >>> info.kind @@ -64,7 +64,7 @@ Example usage of :api:`py.path.svnwc`:: - .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') + .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> temp = py.test.ensuretemp('py.path_documentation') >>> wc = py.path.svnwc(temp.join('svnwc')) >>> wc.checkout('http://codespeak.net/svn/py/dist/py/path/local') @@ -182,7 +182,7 @@ As an example of 'uncommon' methods, we'll show how to read and write properties in an :api:`py.path.svnwc` instance:: - .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') + .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> wc.propget('foo') '' >>> wc.propset('foo', 'bar') @@ -200,7 +200,7 @@ Some uncommon functionality can also be provided as extensions, such as SVN authentication:: - .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') + .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> auth = py.path.SvnAuth('anonymous', 'user', cache_auth=False, ... interactive=False) >>> wc.auth = auth Modified: py/branch/pytestplugin/py/doc/xml.txt ============================================================================== --- py/branch/pytestplugin/py/doc/xml.txt (original) +++ py/branch/pytestplugin/py/doc/xml.txt Thu Feb 26 14:23:29 2009 @@ -168,5 +168,4 @@ your Tags. Hum, it's probably harder to explain this than to actually code it :-) -.. _Nevow: http://www.divmod.org/projects/nevow .. _`py.test`: test.html Modified: py/branch/pytestplugin/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/pytestplugin/py/execnet/testing/test_gateway.py (original) +++ py/branch/pytestplugin/py/execnet/testing/test_gateway.py Thu Feb 26 14:23:29 2009 @@ -570,16 +570,16 @@ class TestSshGateway(BasicRemoteExecution): def setup_class(cls): - if py.test.config.option.sshtarget is None: - py.test.skip("no known ssh target, use --sshtarget to set one") - cls.gw = py.execnet.SshGateway(py.test.config.option.sshtarget) + if py.test.config.option.sshhost is None: + py.test.skip("no known ssh target, use --sshhost to set one") + cls.gw = py.execnet.SshGateway(py.test.config.option.sshhost) def test_sshconfig_functional(self): tmpdir = py.test.ensuretemp("test_sshconfig") ssh_config = tmpdir.join("ssh_config") ssh_config.write( "Host alias123\n" - " HostName %s\n" % (py.test.config.option.sshtarget,)) + " HostName %s\n" % (py.test.config.option.sshhost,)) gw = py.execnet.SshGateway("alias123", ssh_config=ssh_config) assert gw._cmd.find("-F") != -1 assert gw._cmd.find(str(ssh_config)) != -1 @@ -587,7 +587,7 @@ gw.exit() def test_sshaddress(self): - assert self.gw.remoteaddress == py.test.config.option.sshtarget + assert self.gw.remoteaddress == py.test.config.option.sshhost def test_connexion_failes_on_non_existing_hosts(self): py.test.raises(IOError, Modified: py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py (original) +++ py/branch/pytestplugin/py/test/dsession/testing/test_hostmanage.py Thu Feb 26 14:23:29 2009 @@ -96,9 +96,10 @@ assert l[0] == host.python def test_initgateway_ssh_and_remotepath(self): - if not py.test.config.option.sshtarget: + hostspec = py.test.config.option.sshhost + if not hostspec: py.test.skip("no known ssh target, use -S to set one") - host = Host("%s" % (option.sshtarget, )) + host = Host("%s" % (hostspec)) # this test should be careful to not write/rsync anything # as the remotepath is the default location # and may be used in the real world @@ -292,10 +293,10 @@ def XXXtest_ssh_rsync_samehost_twice(self): #XXX we have no easy way to have a temp directory remotely! option = py.test.config.option - if option.sshtarget is None: + if option.sshhost is None: py.test.skip("no known ssh target, use -S to set one") - host1 = Host("%s" % (option.sshtarget, )) - host2 = Host("%s" % (option.sshtarget, )) + host1 = Host("%s" % (option.sshhost, )) + host2 = Host("%s" % (option.sshhost, )) hm = HostManager(config, hosts=[host1, host2]) events = [] hm.init_rsync(events.append) Modified: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_apigen.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Thu Feb 26 14:23:29 2009 @@ -2,9 +2,18 @@ class ApigenPlugin: def pytest_addoption(self, parser): - parser.addoption('--apigen', action="store_true", dest="apigen", - help="generate api documentation") - # "--apigen-script" argument pointing to a script"), + group = parser.addgroup("apigen options") + group.addoption('--apigen', action="store_true", dest="apigen", + help="generate api documentation") + #group.addoption('--apigenpath', + # action="store", dest="apigenpath", + # default="../apigen", + # type="string", + # help="relative path to apigen doc output location (relative from py/)") + #group.addoption('--docpath', + # action='store', dest='docpath', + # default="doc", type='string', + # help="relative path to doc output location (relative from py/)") def pytest_configure(self, config): if config.option.apigen: @@ -35,9 +44,11 @@ tr = terminalreporter from py.__.apigen.tracer.docstorage import DocStorageAccessor terminalreporter.write_sep("=", "apigen: building documentation") + assert hasattr(tr.config.option, 'apigenpath') capture = py.io.StdCaptureFD() try: self.apigenscript.build( + tr.config, self.pkgdir, DocStorageAccessor(self.docstorage), capture) From hpk at codespeak.net Thu Feb 26 15:18:26 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 15:18:26 +0100 (CET) Subject: [py-svn] r62188 - py/branch/pytestplugin/py/doc Message-ID: <20090226141826.6931A168516@codespeak.net> Author: hpk Date: Thu Feb 26 15:18:24 2009 New Revision: 62188 Modified: py/branch/pytestplugin/py/doc/confrest.py py/branch/pytestplugin/py/doc/conftest.py Log: adjustments before factoring out ReST as a plugin Modified: py/branch/pytestplugin/py/doc/confrest.py ============================================================================== --- py/branch/pytestplugin/py/doc/confrest.py (original) +++ py/branch/pytestplugin/py/doc/confrest.py Thu Feb 26 15:18:24 2009 @@ -3,7 +3,6 @@ from py.__.misc.difftime import worded_time from py.__.apigen.linker import relpath -mydir = py.magic.autopath().dirpath() html = py.xml.html @@ -107,6 +106,7 @@ class Project: + mydir = py.magic.autopath().dirpath() title = "py lib" prefix_title = "" # we have a logo already containing "py lib" encoding = 'latin1' @@ -119,7 +119,7 @@ def __init__(self, sourcepath=None): if sourcepath is None: - sourcepath = mydir + sourcepath = self.mydir self.setpath(sourcepath) def setpath(self, sourcepath, docpath=None, Modified: py/branch/pytestplugin/py/doc/conftest.py ============================================================================== --- py/branch/pytestplugin/py/doc/conftest.py (original) +++ py/branch/pytestplugin/py/doc/conftest.py Thu Feb 26 15:18:24 2009 @@ -23,15 +23,10 @@ ConftestPlugin = RestDocPlugin def get_apigenpath(): - path = os.environ.get('DOCPATH') - if path is None: - return mydir - else: - return py.path.local().join(path, abs=True) path = os.environ.get('APIGENPATH') if path is None: - path = "apigen" - return pypkgdir.join(path, abs=True) + path = get_docpath().join("apigen") + return py.path.local(path) def get_docpath(): path = os.environ.get('DOCPATH') From hpk at codespeak.net Thu Feb 26 16:34:54 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 16:34:54 +0100 (CET) Subject: [py-svn] r62192 - in py/branch/pytestplugin/py: . apigen apigen/testing doc misc Message-ID: <20090226153454.716031684C3@codespeak.net> Author: hpk Date: Thu Feb 26 16:34:52 2009 New Revision: 62192 Removed: py/branch/pytestplugin/py/doc/conftest.py py/branch/pytestplugin/py/doc/test_conftest.py Modified: py/branch/pytestplugin/py/apigen/conftest.py py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py py/branch/pytestplugin/py/conftest.py py/branch/pytestplugin/py/doc/confrest.py py/branch/pytestplugin/py/misc/rest.py Log: pytest_restdoc.py: another conftest bites the dust, another plugin is born, Modified: py/branch/pytestplugin/py/apigen/conftest.py ============================================================================== --- py/branch/pytestplugin/py/apigen/conftest.py (original) +++ py/branch/pytestplugin/py/apigen/conftest.py Thu Feb 26 16:34:52 2009 @@ -1,10 +1,9 @@ import py -Option = py.test.config.Option -option = py.test.config.addoptions("apigen test options", - Option('', '--webcheck', +class ConftestPlugin: + def addoption(self, parser): + parser.addoption('--webcheck', action="store_true", dest="webcheck", default=False, help="run XHTML validation tests" - ), -) + ) Modified: py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py (original) +++ py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py Thu Feb 26 16:34:52 2009 @@ -82,7 +82,7 @@ if isinstance(htmlstring, unicode): htmlstring = htmlstring.encode('UTF-8', 'replace') assert isinstance(htmlstring, str) - if option.webcheck: + if py.test.config.option.webcheck: webcheck.check_html(htmlstring) else: py.test.skip("pass --webcheck to validate html produced in tests " Modified: py/branch/pytestplugin/py/conftest.py ============================================================================== --- py/branch/pytestplugin/py/conftest.py (original) +++ py/branch/pytestplugin/py/conftest.py Thu Feb 26 16:34:52 2009 @@ -9,7 +9,7 @@ dist_rsync_roots = ['.'] # XXX -pytest_plugins = 'pytest_doctest', 'pytest_pytester' +pytest_plugins = 'pytest_doctest', 'pytest_pytester', 'pytest_restdoc' import py class PylibTestPlugin: Modified: py/branch/pytestplugin/py/doc/confrest.py ============================================================================== --- py/branch/pytestplugin/py/doc/confrest.py (original) +++ py/branch/pytestplugin/py/doc/confrest.py Thu Feb 26 16:34:52 2009 @@ -3,7 +3,6 @@ from py.__.misc.difftime import worded_time from py.__.apigen.linker import relpath - html = py.xml.html class Page(object): Deleted: /py/branch/pytestplugin/py/doc/conftest.py ============================================================================== --- /py/branch/pytestplugin/py/doc/conftest.py Thu Feb 26 16:34:52 2009 +++ (empty file) @@ -1,320 +0,0 @@ -from __future__ import generators -import py -from py.__.misc import rest -from py.__.apigen.linker import relpath -import os - -pypkgdir = py.path.local(py.__file__).dirpath() -mydir = py.magic.autopath().dirpath() - -class RestDocPlugin: - def pytest_addoption(self, parser): - group = parser.addgroup("ReST", "ReST documentation check options") - group.addoption('-R', '--urlcheck', - action="store_true", dest="urlcheck", default=False, - help="urlopen() remote links found in ReST text files.") - group.addoption('--urlcheck-timeout', action="store", - type="int", dest="urlcheck_timeout", default=5, - help="timeout in seconds for urlcheck") - group.addoption('--forcegen', - action="store_true", dest="forcegen", default=False, - help="force generation of html files even if they appear up-to-date") - -ConftestPlugin = RestDocPlugin - -def get_apigenpath(): - path = os.environ.get('APIGENPATH') - if path is None: - path = get_docpath().join("apigen") - return py.path.local(path) - -def get_docpath(): - path = os.environ.get('DOCPATH') - if path is None: - return mydir - else: - return py.path.local().join(path, abs=True) - -def get_apigen_relpath(): - return relpath(get_docpath().strpath + '/', - get_apigenpath().strpath + '/') - -def deindent(s, sep='\n'): - leastspaces = -1 - lines = s.split(sep) - for line in lines: - if not line.strip(): - continue - spaces = len(line) - len(line.lstrip()) - if leastspaces == -1 or spaces < leastspaces: - leastspaces = spaces - if leastspaces == -1: - return s - for i, line in py.builtin.enumerate(lines): - if not line.strip(): - lines[i] = '' - else: - lines[i] = line[leastspaces:] - return sep.join(lines) - -_initialized = False -def checkdocutils(): - global _initialized - py.test.importorskip("docutils") - if not _initialized: - from py.__.rest import directive - directive.register_linkrole('api', resolve_linkrole) - directive.register_linkrole('source', resolve_linkrole) - _initialized = True - - -class ReSTSyntaxTest(py.test.collect.Item): - def runtest(self): - self.restcheck(py.path.svnwc(self.fspath)) - - def restcheck(self, path): - localpath = path - if hasattr(path, 'localpath'): - localpath = path.localpath - checkdocutils() - import docutils.utils - try: - cur = localpath - for x in cur.parts(reverse=True): - confrest = x.dirpath('confrest.py') - if confrest.check(file=1): - confrest = confrest.pyimport() - project = confrest.Project() - self._checkskip(path, project.get_htmloutputpath(path)) - project.process(path) - break - else: - # defer to default processor - self._checkskip(path) - rest.process(path) - except KeyboardInterrupt: - raise - except docutils.utils.SystemMessage: - # we assume docutils printed info on stdout - py.test.fail("docutils processing failed, see captured stderr") - - def _checkskip(self, lpath, htmlpath=None): - if not self._config.getvalue("forcegen"): - lpath = py.path.local(lpath) - if htmlpath is not None: - htmlpath = py.path.local(htmlpath) - if lpath.ext == '.txt': - htmlpath = htmlpath or lpath.new(ext='.html') - if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime(): - py.test.skip("html file is up to date, use --forcegen to regenerate") - #return [] # no need to rebuild - -class DoctestText(py.test.collect.Item): - def runtest(self): - s = self._normalize_linesep() - l = [] - prefix = '.. >>> ' - mod = py.std.types.ModuleType(self.fspath.purebasename) - skipchunk = False - for line in deindent(s).split('\n'): - stripped = line.strip() - if skipchunk and line.startswith(skipchunk): - print "skipping", line - continue - skipchunk = False - if stripped.startswith(prefix): - try: - exec py.code.Source(stripped[len(prefix):]).compile() in \ - mod.__dict__ - except ValueError, e: - if e.args and e.args[0] == "skipchunk": - skipchunk = " " * (len(line) - len(line.lstrip())) - else: - raise - else: - l.append(line) - docstring = "\n".join(l) - mod.__doc__ = docstring - failed, tot = py.compat.doctest.testmod(mod, verbose=1) - if failed: - py.test.fail("doctest %s: %s failed out of %s" %( - self.fspath, failed, tot)) - - def _normalize_linesep(self): - # XXX quite nasty... but it works (fixes win32 issues) - s = self.fspath.read() - linesep = '\n' - if '\r' in s: - if '\n' not in s: - linesep = '\r' - else: - linesep = '\r\n' - s = s.replace(linesep, '\n') - return s - -class LinkCheckerMaker(py.test.collect.Collector): - def collect(self): - return list(self.genlinkchecks()) - - def genlinkchecks(self): - path = self.fspath - # generating functions + args as single tests - timeout = self._config.getvalue("urlcheck_timeout") - for lineno, line in py.builtin.enumerate(path.readlines()): - line = line.strip() - if line.startswith('.. _'): - if line.startswith('.. _`'): - delim = '`:' - else: - delim = ':' - l = line.split(delim, 1) - if len(l) != 2: - continue - tryfn = l[1].strip() - name = "%s:%d" %(tryfn, lineno) - if tryfn.startswith('http:') or tryfn.startswith('https'): - if self._config.getvalue("urlcheck"): - yield CheckLink(name, parent=self, - args=(tryfn, path, lineno, timeout), callobj=urlcheck) - elif tryfn.startswith('webcal:'): - continue - else: - i = tryfn.find('#') - if i != -1: - checkfn = tryfn[:i] - else: - checkfn = tryfn - if checkfn.strip() and (1 or checkfn.endswith('.html')): - yield CheckLink(name, parent=self, - args=(tryfn, path, lineno), callobj=localrefcheck) - -class CheckLink(py.test.collect.Function): - def repr_metainfo(self): - return self.ReprMetaInfo(fspath=self.fspath, lineno=self._args[2], - modpath="checklink: %s" % (self._args[0],)) - def setup(self): - pass - def teardown(self): - pass - -class DocfileTests(py.test.collect.File): - DoctestText = DoctestText - ReSTSyntaxTest = ReSTSyntaxTest - LinkCheckerMaker = LinkCheckerMaker - - def collect(self): - return [ - self.ReSTSyntaxTest(self.fspath.basename, parent=self), - self.LinkCheckerMaker("checklinks", self), - self.DoctestText("doctest", self), - ] - - -def urlcheck(tryfn, path, lineno, TIMEOUT_URLOPEN): - old = py.std.socket.getdefaulttimeout() - py.std.socket.setdefaulttimeout(TIMEOUT_URLOPEN) - try: - try: - print "trying remote", tryfn - py.std.urllib2.urlopen(tryfn) - finally: - py.std.socket.setdefaulttimeout(old) - except (py.std.urllib2.URLError, py.std.urllib2.HTTPError), e: - if getattr(e, 'code', None) in (401, 403): # authorization required, forbidden - py.test.skip("%s: %s" %(tryfn, str(e))) - else: - py.test.fail("remote reference error %r in %s:%d\n%s" %( - tryfn, path.basename, lineno+1, e)) - -def localrefcheck(tryfn, path, lineno): - # assume it should be a file - i = tryfn.find('#') - if tryfn.startswith('javascript:'): - return # don't check JS refs - if i != -1: - anchor = tryfn[i+1:] - tryfn = tryfn[:i] - else: - anchor = '' - fn = path.dirpath(tryfn) - ishtml = fn.ext == '.html' - fn = ishtml and fn.new(ext='.txt') or fn - print "filename is", fn - if not fn.check(): # not ishtml or not fn.check(): - if not py.path.local(tryfn).check(): # the html could be there - py.test.fail("reference error %r in %s:%d" %( - tryfn, path.basename, lineno+1)) - if anchor: - source = unicode(fn.read(), 'latin1') - source = source.lower().replace('-', ' ') # aehem - - anchor = anchor.replace('-', ' ') - match2 = ".. _`%s`:" % anchor - match3 = ".. _%s:" % anchor - candidates = (anchor, match2, match3) - print "candidates", repr(candidates) - for line in source.split('\n'): - line = line.strip() - if line in candidates: - break - else: - py.test.fail("anchor reference error %s#%s in %s:%d" %( - tryfn, anchor, path.basename, lineno+1)) - - -# ___________________________________________________________ -# -# hooking into py.test Directory collector's chain ... - -class DocDirectory(py.test.collect.Directory): - DocfileTests = DocfileTests - def collect(self): - results = super(DocDirectory, self).collect() - for x in self.fspath.listdir('*.txt', sort=True): - results.append(self.DocfileTests(x, parent=self)) - return results - -Directory = DocDirectory - -def resolve_linkrole(name, text, check=True): - apigen_relpath = get_apigen_relpath() - if name == 'api': - if text == 'py': - return ('py', apigen_relpath + 'api/index.html') - else: - assert text.startswith('py.'), ( - 'api link "%s" does not point to the py package') % (text,) - dotted_name = text - if dotted_name.find('(') > -1: - dotted_name = dotted_name[:text.find('(')] - # remove pkg root - path = dotted_name.split('.')[1:] - dotted_name = '.'.join(path) - obj = py - if check: - for chunk in path: - try: - obj = getattr(obj, chunk) - except AttributeError: - raise AssertionError( - 'problem with linkrole :api:`%s`: can not resolve ' - 'dotted name %s' % (text, dotted_name,)) - return (text, apigen_relpath + 'api/%s.html' % (dotted_name,)) - elif name == 'source': - assert text.startswith('py/'), ('source link "%s" does not point ' - 'to the py package') % (text,) - relpath = '/'.join(text.split('/')[1:]) - if check: - pkgroot = py.__pkg__.getpath() - abspath = pkgroot.join(relpath) - assert pkgroot.join(relpath).check(), ( - 'problem with linkrole :source:`%s`: ' - 'path %s does not exist' % (text, relpath)) - if relpath.endswith('/') or not relpath: - relpath += 'index.html' - else: - relpath += '.html' - return (text, apigen_relpath + 'source/%s' % (relpath,)) - -# legacy -ReSTChecker = DocfileTests Deleted: /py/branch/pytestplugin/py/doc/test_conftest.py ============================================================================== --- /py/branch/pytestplugin/py/doc/test_conftest.py Thu Feb 26 16:34:52 2009 +++ (empty file) @@ -1,116 +0,0 @@ - -import py -from py.__.test import event -from py.__.doc import conftest as doc_conftest - -pytest_plugins = "pytest_pytester" - -def pytest_pyfuncarg_testdir(__call__, pyfuncitem): - testdir = __call__.execute(firstresult=True) - p = py.path.local(doc_conftest.__file__) - if p.ext == ".pyc": - p = p.new(ext=".py") - p.copy(testdir.tmpdir.join("conftest.py")) - return testdir - -class TestDoctest: - def test_doctest_extra_exec(self, testdir): - xtxt = testdir.maketxtfile(x=""" - hello:: - .. >>> raise ValueError - >>> None - """) - sorter = testdir.inline_run(xtxt) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 1 - - def test_doctest_basic(self, testdir): - xtxt = testdir.maketxtfile(x=""" - .. - >>> from os.path import abspath - - hello world - - >>> assert abspath - >>> i=3 - >>> print i - 3 - - yes yes - - >>> i - 3 - - end - """) - sorter = testdir.inline_run(xtxt) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 0 - assert passed + skipped == 2 - - def test_doctest_eol(self, testdir): - ytxt = testdir.maketxtfile(y=".. >>> 1 + 1\r\n 2\r\n\r\n") - sorter = testdir.inline_run(ytxt) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 0 - assert passed + skipped == 2 - - def test_doctest_indentation(self, testdir): - footxt = testdir.maketxtfile(foo= - '..\n >>> print "foo\\n bar"\n foo\n bar\n') - sorter = testdir.inline_run(footxt) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 0 - assert skipped + passed == 2 - - def test_js_ignore(self, testdir): - xtxt = testdir.maketxtfile(xtxt=""" - `blah`_ - - .. _`blah`: javascript:some_function() - """) - sorter = testdir.inline_run(xtxt) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 0 - assert skipped + passed == 3 - -def test_deindent(): - deindent = doc_conftest.deindent - assert deindent('foo') == 'foo' - assert deindent('foo\n bar') == 'foo\n bar' - assert deindent(' foo\n bar\n') == 'foo\nbar\n' - assert deindent(' foo\n\n bar\n') == 'foo\n\nbar\n' - assert deindent(' foo\n bar\n') == 'foo\n bar\n' - assert deindent(' foo\n bar\n') == ' foo\nbar\n' - - -def test_resolve_linkrole(): - from py.__.doc.conftest import get_apigen_relpath - apigen_relpath = get_apigen_relpath() - from py.__.doc.conftest import resolve_linkrole - assert resolve_linkrole('api', 'py.foo.bar', False) == ( - 'py.foo.bar', apigen_relpath + 'api/foo.bar.html') - assert resolve_linkrole('api', 'py.foo.bar()', False) == ( - 'py.foo.bar()', apigen_relpath + 'api/foo.bar.html') - assert resolve_linkrole('api', 'py', False) == ( - 'py', apigen_relpath + 'api/index.html') - py.test.raises(AssertionError, 'resolve_linkrole("api", "foo.bar")') - assert resolve_linkrole('source', 'py/foo/bar.py', False) == ( - 'py/foo/bar.py', apigen_relpath + 'source/foo/bar.py.html') - assert resolve_linkrole('source', 'py/foo/', False) == ( - 'py/foo/', apigen_relpath + 'source/foo/index.html') - assert resolve_linkrole('source', 'py/', False) == ( - 'py/', apigen_relpath + 'source/index.html') - py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")') - -def test_resolve_linkrole_check_api(): - from py.__.doc.conftest import resolve_linkrole - assert resolve_linkrole('api', 'py.test.ensuretemp') - py.test.raises(AssertionError, "resolve_linkrole('api', 'py.foo.baz')") - -def test_resolve_linkrole_check_source(): - from py.__.doc.conftest import resolve_linkrole - assert resolve_linkrole('source', 'py/path/common.py') - py.test.raises(AssertionError, - "resolve_linkrole('source', 'py/foo/bar.py')") - Modified: py/branch/pytestplugin/py/misc/rest.py ============================================================================== --- py/branch/pytestplugin/py/misc/rest.py (original) +++ py/branch/pytestplugin/py/misc/rest.py Thu Feb 26 16:34:52 2009 @@ -9,6 +9,9 @@ def log(msg): pass +def get_htmloutputpath(path): + return path.new(ext=".html") + def convert_rest_html(source, source_path, stylesheet=None, encoding='latin1'): from py.__.rest import directive """ return html latin1-encoded document for the given input. From hpk at codespeak.net Thu Feb 26 16:38:12 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 16:38:12 +0100 (CET) Subject: [py-svn] r62193 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090226153812.F19711684C3@codespeak.net> Author: hpk Date: Thu Feb 26 16:38:12 2009 New Revision: 62193 Added: py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py Log: actually add the plugin :) Added: py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py Thu Feb 26 16:38:12 2009 @@ -0,0 +1,422 @@ +import py + +class RestdocPlugin: + def pytest_addoption(self, parser): + group = parser.addgroup("ReST", "ReST documentation check options") + group.addoption('-R', '--urlcheck', + action="store_true", dest="urlcheck", default=False, + help="urlopen() remote links found in ReST text files.") + group.addoption('--urlcheck-timeout', action="store", + type="int", dest="urlcheck_timeout", default=5, + help="timeout in seconds for urlcheck") + group.addoption('--forcegen', + action="store_true", dest="forcegen", default=False, + help="force generation of html files even if they appear up-to-date") + + def pytest_collect_file(self, path, parent): + if path.ext == ".txt": + project = self._getproject(path) + if project is not None: + return ReSTFile(project, path, parent=parent) + + def _getproject(self, path): + for parent in path.parts(reverse=True): + confrest = parent.join("confrest.py") + if confrest.check(): + Project = confrest.pyimport().Project + return Project(parent.dirpath()) + +class ReSTFile(py.test.collect.File): + def __init__(self, project, fspath, parent): + super(ReSTFile, self).__init__(fspath=fspath, parent=parent) + self.project = project + + def collect(self): + return [ + ReSTSyntaxTest(self.project, "ReSTSyntax", parent=self), + LinkCheckerMaker("checklinks", parent=self), + DoctestText("doctest", parent=self), + ] + +def deindent(s, sep='\n'): + leastspaces = -1 + lines = s.split(sep) + for line in lines: + if not line.strip(): + continue + spaces = len(line) - len(line.lstrip()) + if leastspaces == -1 or spaces < leastspaces: + leastspaces = spaces + if leastspaces == -1: + return s + for i, line in py.builtin.enumerate(lines): + if not line.strip(): + lines[i] = '' + else: + lines[i] = line[leastspaces:] + return sep.join(lines) + +class ReSTSyntaxTest(py.test.collect.Item): + def __init__(self, project, *args, **kwargs): + super(ReSTSyntaxTest, self).__init__(*args, **kwargs) + self.project = project + + def runtest(self): + self.restcheck(py.path.svnwc(self.fspath)) + + def restcheck(self, path): + checkdocutils() + from docutils.utils import SystemMessage + try: + self._checkskip(path, self.project.get_htmloutputpath(path)) + self.project.process(path) + except KeyboardInterrupt: + raise + except SystemMessage: + # we assume docutils printed info on stdout + py.test.fail("docutils processing failed, see captured stderr") + + def _checkskip(self, lpath, htmlpath=None): + if not self._config.getvalue("forcegen"): + lpath = py.path.local(lpath) + if htmlpath is not None: + htmlpath = py.path.local(htmlpath) + if lpath.ext == '.txt': + htmlpath = htmlpath or lpath.new(ext='.html') + if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime(): + py.test.skip("html file is up to date, use --forcegen to regenerate") + #return [] # no need to rebuild + +class DoctestText(py.test.collect.Item): + def runtest(self): + s = self._normalize_linesep() + l = [] + prefix = '.. >>> ' + mod = py.std.types.ModuleType(self.fspath.purebasename) + skipchunk = False + for line in deindent(s).split('\n'): + stripped = line.strip() + if skipchunk and line.startswith(skipchunk): + print "skipping", line + continue + skipchunk = False + if stripped.startswith(prefix): + try: + exec py.code.Source(stripped[len(prefix):]).compile() in \ + mod.__dict__ + except ValueError, e: + if e.args and e.args[0] == "skipchunk": + skipchunk = " " * (len(line) - len(line.lstrip())) + else: + raise + else: + l.append(line) + docstring = "\n".join(l) + mod.__doc__ = docstring + failed, tot = py.compat.doctest.testmod(mod, verbose=1) + if failed: + py.test.fail("doctest %s: %s failed out of %s" %( + self.fspath, failed, tot)) + + def _normalize_linesep(self): + # XXX quite nasty... but it works (fixes win32 issues) + s = self.fspath.read() + linesep = '\n' + if '\r' in s: + if '\n' not in s: + linesep = '\r' + else: + linesep = '\r\n' + s = s.replace(linesep, '\n') + return s + +class LinkCheckerMaker(py.test.collect.Collector): + def collect(self): + return list(self.genlinkchecks()) + + def genlinkchecks(self): + path = self.fspath + # generating functions + args as single tests + timeout = self._config.getvalue("urlcheck_timeout") + for lineno, line in py.builtin.enumerate(path.readlines()): + line = line.strip() + if line.startswith('.. _'): + if line.startswith('.. _`'): + delim = '`:' + else: + delim = ':' + l = line.split(delim, 1) + if len(l) != 2: + continue + tryfn = l[1].strip() + name = "%s:%d" %(tryfn, lineno) + if tryfn.startswith('http:') or tryfn.startswith('https'): + if self._config.getvalue("urlcheck"): + yield CheckLink(name, parent=self, + args=(tryfn, path, lineno, timeout), callobj=urlcheck) + elif tryfn.startswith('webcal:'): + continue + else: + i = tryfn.find('#') + if i != -1: + checkfn = tryfn[:i] + else: + checkfn = tryfn + if checkfn.strip() and (1 or checkfn.endswith('.html')): + yield CheckLink(name, parent=self, + args=(tryfn, path, lineno), callobj=localrefcheck) + +class CheckLink(py.test.collect.Function): + def repr_metainfo(self): + return self.ReprMetaInfo(fspath=self.fspath, lineno=self._args[2], + modpath="checklink: %s" % (self._args[0],)) + def setup(self): + pass + def teardown(self): + pass + +def urlcheck(tryfn, path, lineno, TIMEOUT_URLOPEN): + old = py.std.socket.getdefaulttimeout() + py.std.socket.setdefaulttimeout(TIMEOUT_URLOPEN) + try: + try: + print "trying remote", tryfn + py.std.urllib2.urlopen(tryfn) + finally: + py.std.socket.setdefaulttimeout(old) + except (py.std.urllib2.URLError, py.std.urllib2.HTTPError), e: + if getattr(e, 'code', None) in (401, 403): # authorization required, forbidden + py.test.skip("%s: %s" %(tryfn, str(e))) + else: + py.test.fail("remote reference error %r in %s:%d\n%s" %( + tryfn, path.basename, lineno+1, e)) + +def localrefcheck(tryfn, path, lineno): + # assume it should be a file + i = tryfn.find('#') + if tryfn.startswith('javascript:'): + return # don't check JS refs + if i != -1: + anchor = tryfn[i+1:] + tryfn = tryfn[:i] + else: + anchor = '' + fn = path.dirpath(tryfn) + ishtml = fn.ext == '.html' + fn = ishtml and fn.new(ext='.txt') or fn + print "filename is", fn + if not fn.check(): # not ishtml or not fn.check(): + if not py.path.local(tryfn).check(): # the html could be there + py.test.fail("reference error %r in %s:%d" %( + tryfn, path.basename, lineno+1)) + if anchor: + source = unicode(fn.read(), 'latin1') + source = source.lower().replace('-', ' ') # aehem + + anchor = anchor.replace('-', ' ') + match2 = ".. _`%s`:" % anchor + match3 = ".. _%s:" % anchor + candidates = (anchor, match2, match3) + print "candidates", repr(candidates) + for line in source.split('\n'): + line = line.strip() + if line in candidates: + break + else: + py.test.fail("anchor reference error %s#%s in %s:%d" %( + tryfn, anchor, path.basename, lineno+1)) + + + +_initialized = False +def checkdocutils(): + global _initialized + py.test.importorskip("docutils") + if not _initialized: + from py.__.rest import directive + directive.register_linkrole('api', resolve_linkrole) + directive.register_linkrole('source', resolve_linkrole) + _initialized = True + +def get_apigenpath(): + path = os.environ.get('APIGENPATH') + if path is None: + path = get_docpath().join("apigen") + return py.path.local(path) + +def get_docpath(): + path = os.environ.get('DOCPATH') + if path is None: + return mydir + else: + return py.path.local().join(path, abs=True) + +def get_apigen_relpath(): + return relpath(get_docpath().strpath + '/', + get_apigenpath().strpath + '/') + + +def resolve_linkrole(name, text, check=True): + apigen_relpath = get_apigen_relpath() + if name == 'api': + if text == 'py': + return ('py', apigen_relpath + 'api/index.html') + else: + assert text.startswith('py.'), ( + 'api link "%s" does not point to the py package') % (text,) + dotted_name = text + if dotted_name.find('(') > -1: + dotted_name = dotted_name[:text.find('(')] + # remove pkg root + path = dotted_name.split('.')[1:] + dotted_name = '.'.join(path) + obj = py + if check: + for chunk in path: + try: + obj = getattr(obj, chunk) + except AttributeError: + raise AssertionError( + 'problem with linkrole :api:`%s`: can not resolve ' + 'dotted name %s' % (text, dotted_name,)) + return (text, apigen_relpath + 'api/%s.html' % (dotted_name,)) + elif name == 'source': + assert text.startswith('py/'), ('source link "%s" does not point ' + 'to the py package') % (text,) + relpath = '/'.join(text.split('/')[1:]) + if check: + pkgroot = py.__pkg__.getpath() + abspath = pkgroot.join(relpath) + assert pkgroot.join(relpath).check(), ( + 'problem with linkrole :source:`%s`: ' + 'path %s does not exist' % (text, relpath)) + if relpath.endswith('/') or not relpath: + relpath += 'index.html' + else: + relpath += '.html' + return (text, apigen_relpath + 'source/%s' % (relpath,)) + + + +# +# PLUGIN tests +# +def test_generic(plugintester): + plugintester.apicheck(RestdocPlugin) + +def test_deindent(): + assert deindent('foo') == 'foo' + assert deindent('foo\n bar') == 'foo\n bar' + assert deindent(' foo\n bar\n') == 'foo\nbar\n' + assert deindent(' foo\n\n bar\n') == 'foo\n\nbar\n' + assert deindent(' foo\n bar\n') == 'foo\n bar\n' + assert deindent(' foo\n bar\n') == ' foo\nbar\n' + +class TestApigenLinkRole: + disabled = True + # these tests are moved here from the former py/doc/conftest.py + def test_resolve_linkrole(self): + from py.__.doc.conftest import get_apigen_relpath + apigen_relpath = get_apigen_relpath() + + assert resolve_linkrole('api', 'py.foo.bar', False) == ( + 'py.foo.bar', apigen_relpath + 'api/foo.bar.html') + assert resolve_linkrole('api', 'py.foo.bar()', False) == ( + 'py.foo.bar()', apigen_relpath + 'api/foo.bar.html') + assert resolve_linkrole('api', 'py', False) == ( + 'py', apigen_relpath + 'api/index.html') + py.test.raises(AssertionError, 'resolve_linkrole("api", "foo.bar")') + assert resolve_linkrole('source', 'py/foo/bar.py', False) == ( + 'py/foo/bar.py', apigen_relpath + 'source/foo/bar.py.html') + assert resolve_linkrole('source', 'py/foo/', False) == ( + 'py/foo/', apigen_relpath + 'source/foo/index.html') + assert resolve_linkrole('source', 'py/', False) == ( + 'py/', apigen_relpath + 'source/index.html') + py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")') + + def test_resolve_linkrole_check_api(self): + assert resolve_linkrole('api', 'py.test.ensuretemp') + py.test.raises(AssertionError, "resolve_linkrole('api', 'py.foo.baz')") + + def test_resolve_linkrole_check_source(self): + assert resolve_linkrole('source', 'py/path/common.py') + py.test.raises(AssertionError, + "resolve_linkrole('source', 'py/foo/bar.py')") + + +def pytest_pyfuncarg_testdir(__call__, pyfuncitem): + testdir = __call__.execute(firstresult=True) + testdir.makepyfile(confrest=""" + from py.__.misc import rest + class Project: + def __init__(self, sourcepath): + self.sourcepath = sourcepath + def process(self, path): + return rest.process(path) + def get_htmloutputpath(self, path): + return path.new(ext='html') + """) + testdir.plugins.append(RestdocPlugin()) + return testdir + +class TestDoctest: + def test_doctest_extra_exec(self, testdir): + xtxt = testdir.maketxtfile(x=""" + hello:: + .. >>> raise ValueError + >>> None + """) + sorter = testdir.inline_run(xtxt) + passed, skipped, failed = sorter.countoutcomes() + assert failed == 1 + + def test_doctest_basic(self, testdir): + xtxt = testdir.maketxtfile(x=""" + .. + >>> from os.path import abspath + + hello world + + >>> assert abspath + >>> i=3 + >>> print i + 3 + + yes yes + + >>> i + 3 + + end + """) + sorter = testdir.inline_run(xtxt) + passed, skipped, failed = sorter.countoutcomes() + assert failed == 0 + assert passed + skipped == 2 + + def test_doctest_eol(self, testdir): + ytxt = testdir.maketxtfile(y=".. >>> 1 + 1\r\n 2\r\n\r\n") + sorter = testdir.inline_run(ytxt) + passed, skipped, failed = sorter.countoutcomes() + assert failed == 0 + assert passed + skipped == 2 + + def test_doctest_indentation(self, testdir): + footxt = testdir.maketxtfile(foo= + '..\n >>> print "foo\\n bar"\n foo\n bar\n') + sorter = testdir.inline_run(footxt) + passed, skipped, failed = sorter.countoutcomes() + assert failed == 0 + assert skipped + passed == 2 + + def test_js_ignore(self, testdir): + xtxt = testdir.maketxtfile(xtxt=""" + `blah`_ + + .. _`blah`: javascript:some_function() + """) + sorter = testdir.inline_run(xtxt) + passed, skipped, failed = sorter.countoutcomes() + assert failed == 0 + assert skipped + passed == 3 + From hpk at codespeak.net Thu Feb 26 17:10:04 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 17:10:04 +0100 (CET) Subject: [py-svn] r62194 - py/branch/pytestplugin/py/test/plugin Message-ID: <20090226161004.F09771684FA@codespeak.net> Author: hpk Date: Thu Feb 26 17:10:02 2009 New Revision: 62194 Modified: py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py Log: introduce pytest_doctest_prepare_content hook Modified: py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py Thu Feb 26 17:10:02 2009 @@ -89,7 +89,12 @@ class DoctestText(py.test.collect.Item): def runtest(self): - s = self._normalize_linesep() + content = self._normalize_linesep() + newcontent = self._config.pytestplugins.call_firstresult( + 'pytest_doctest_prepare_content', content=content) + if newcontent is not None: + content = newcontent + s = content l = [] prefix = '.. >>> ' mod = py.std.types.ModuleType(self.fspath.purebasename) @@ -420,3 +425,24 @@ assert failed == 0 assert skipped + passed == 3 + def test_pytest_doctest_prepare_content(self, testdir): + l = [] + class MyPlugin: + def pytest_doctest_prepare_content(self, content): + l.append(content) + return content.replace("False", "True") + + testdir.plugins.append(MyPlugin()) + + xtxt = testdir.maketxtfile(x=""" + hello: + + >>> 2 == 2 + False + + """) + sorter = testdir.inline_run(xtxt) + assert len(l) == 1 + passed, skipped, failed = sorter.countoutcomes() + assert not failed and not skipped + assert passed >= 1 From hpk at codespeak.net Thu Feb 26 18:16:05 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 26 Feb 2009 18:16:05 +0100 (CET) Subject: [py-svn] r62196 - in py/branch/pytestplugin/py: apigen apigen/rest/testing apigen/testing doc misc rest/testing test/looponfail/testing test/plugin Message-ID: <20090226171605.C9A4B168417@codespeak.net> Author: hpk Date: Thu Feb 26 18:16:03 2009 New Revision: 62196 Modified: py/branch/pytestplugin/py/apigen/apigen.py py/branch/pytestplugin/py/apigen/conftest.py py/branch/pytestplugin/py/apigen/layout.py py/branch/pytestplugin/py/apigen/project.py py/branch/pytestplugin/py/apigen/rest/testing/test_rest.py py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py py/branch/pytestplugin/py/doc/confrest.py py/branch/pytestplugin/py/misc/rest.py py/branch/pytestplugin/py/rest/testing/test_rst.py py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py py/branch/pytestplugin/py/test/plugin/pytest_apigen.py py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py Log: getting apigen tests mostly to pass, although i am not sure it's overall producing the right results. Modified: py/branch/pytestplugin/py/apigen/apigen.py ============================================================================== --- py/branch/pytestplugin/py/apigen/apigen.py (original) +++ py/branch/pytestplugin/py/apigen/apigen.py Thu Feb 26 18:16:03 2009 @@ -8,7 +8,6 @@ from py.__.apigen import linker from py.__.apigen import project from py.__.apigen.tracer.docstorage import pkg_to_dict -from py.__.doc.conftest import get_apigenpath from layout import LayoutPage @@ -44,7 +43,7 @@ proj = project.Project() # output dir - targetdir = get_apigenpath(config=config) + targetdir = proj.apigenpath targetdir.ensure(dir=True) # find out what to build Modified: py/branch/pytestplugin/py/apigen/conftest.py ============================================================================== --- py/branch/pytestplugin/py/apigen/conftest.py (original) +++ py/branch/pytestplugin/py/apigen/conftest.py Thu Feb 26 18:16:03 2009 @@ -1,9 +1,8 @@ import py class ConftestPlugin: - def addoption(self, parser): + def pytest_addoption(self, parser): parser.addoption('--webcheck', action="store_true", dest="webcheck", default=False, help="run XHTML validation tests" ) - Modified: py/branch/pytestplugin/py/apigen/layout.py ============================================================================== --- py/branch/pytestplugin/py/apigen/layout.py (original) +++ py/branch/pytestplugin/py/apigen/layout.py Thu Feb 26 18:16:03 2009 @@ -6,7 +6,6 @@ import py from py.__.doc import confrest from py.__.apigen import linker -from py.__.doc.conftest import get_apigenpath, get_docpath here = py.magic.autopath().dirpath() @@ -25,7 +24,7 @@ def get_relpath(self): return linker.relpath(self.targetpath.strpath, - get_apigenpath().strpath) + '/' + self.project.apigenpath.strpath) + '/' def set_content(self, contentel): self.contentspace.append(contentel) Modified: py/branch/pytestplugin/py/apigen/project.py ============================================================================== --- py/branch/pytestplugin/py/apigen/project.py (original) +++ py/branch/pytestplugin/py/apigen/project.py Thu Feb 26 18:16:03 2009 @@ -8,14 +8,18 @@ import py from layout import LayoutPage -class Project(py.__.doc.confrest.Project): +# XXX don't import from an internal py lib class +from py.__.doc import confrest + +class Project(confrest.Project): """ a full project this takes care of storing information on the first pass, and building pages + indexes on the second """ - def __init__(self): + def __init__(self, *args, **kwargs): + confrest.Project.__init__(self, *args, **kwargs) self.content_items = {} def add_item(self, path, content): Modified: py/branch/pytestplugin/py/apigen/rest/testing/test_rest.py ============================================================================== --- py/branch/pytestplugin/py/apigen/rest/testing/test_rest.py (original) +++ py/branch/pytestplugin/py/apigen/rest/testing/test_rest.py Thu Feb 26 18:16:03 2009 @@ -15,7 +15,6 @@ import pickle from py.__.apigen.tracer.testing.runtest import cut_pyc -from py.__.doc.conftest import genlinkchecks from py.__.rest.rst import Rest, Paragraph from py.__.rest.transform import HTMLHandler # XXX: UUuuuuuuuuuuuuuuuuuuuuuuu, dangerous import @@ -186,9 +185,11 @@ py.test.skip('skipping rest generation because docutils is ' 'not installed (this is a partial skip, the rest ' 'of the test was successful)') - for path in tempdir.listdir('*.txt'): - for item, arg1, arg2, arg3 in genlinkchecks(path): - item(arg1, arg2, arg3) + py.test.skip("partial skip: find a nice way to re-use pytest_restdoc's genlinkchecks") + # XXX find a nice way check pytest_restdoc's genlinkchecks() + #for path in tempdir.listdir('*.txt'): + # for item, arg1, arg2, arg3 in genlinkchecks(path): + # item(arg1, arg2, arg3) def test_generation_simple_api(self): ds = self.get_filled_docstorage() Modified: py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py (original) +++ py/branch/pytestplugin/py/apigen/testing/test_apigen_example.py Thu Feb 26 18:16:03 2009 @@ -8,7 +8,6 @@ from py.__.apigen.layout import LayoutPage from py.__.apigen.project import Project from py.__.test.web import webcheck -from py.__.apigen.conftest import option from py.__.path.svn.testing.svntestbase import make_test_repo def run_string_sequence_test(data, seq): Modified: py/branch/pytestplugin/py/doc/confrest.py ============================================================================== --- py/branch/pytestplugin/py/doc/confrest.py (original) +++ py/branch/pytestplugin/py/doc/confrest.py Thu Feb 26 18:16:03 2009 @@ -134,11 +134,15 @@ p = sourcepath.join("style.css") if p.check(): self.stylesheet = p + else: + self.stylesheet = None else: p = py.path.local(stylesheet) if p.check(): stylesheet = p self.stylesheet = stylesheet + self.apigen_relpath = relpath( + self.docpath.strpath + '/', self.apigenpath.strpath + '/') def get_content(self, txtpath, encoding): return unicode(txtpath.read(), encoding) Modified: py/branch/pytestplugin/py/misc/rest.py ============================================================================== --- py/branch/pytestplugin/py/misc/rest.py (original) +++ py/branch/pytestplugin/py/misc/rest.py Thu Feb 26 18:16:03 2009 @@ -9,9 +9,6 @@ def log(msg): pass -def get_htmloutputpath(path): - return path.new(ext=".html") - def convert_rest_html(source, source_path, stylesheet=None, encoding='latin1'): from py.__.rest import directive """ return html latin1-encoded document for the given input. @@ -74,3 +71,11 @@ break uni = match.group(1) return uni + +class Project: # used for confrest.py files + def __init__(self, sourcepath): + self.sourcepath = sourcepath + def process(self, path): + return process(path) + def get_htmloutputpath(self, path): + return path.new(ext='html') Modified: py/branch/pytestplugin/py/rest/testing/test_rst.py ============================================================================== --- py/branch/pytestplugin/py/rest/testing/test_rst.py (original) +++ py/branch/pytestplugin/py/rest/testing/test_rst.py Thu Feb 26 18:16:03 2009 @@ -3,7 +3,7 @@ """ from py.__.rest.rst import * -from py.__.doc.conftest import restcheck +from py.__.misc.rest import process as restcheck import traceback tempdir = py.test.ensuretemp('rest') Modified: py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py ============================================================================== --- py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py (original) +++ py/branch/pytestplugin/py/test/looponfail/testing/test_remote.py Thu Feb 26 18:16:03 2009 @@ -9,7 +9,7 @@ failures = control.runsession() assert not failures - def test_failures(self, testdir): + def test_failures_somewhere(self, testdir): item = testdir.getitem("def test_func(): assert 0\n") control = RemoteControl(item._config) control.setup() Modified: py/branch/pytestplugin/py/test/plugin/pytest_apigen.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_apigen.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_apigen.py Thu Feb 26 18:16:03 2009 @@ -44,7 +44,7 @@ tr = terminalreporter from py.__.apigen.tracer.docstorage import DocStorageAccessor terminalreporter.write_sep("=", "apigen: building documentation") - assert hasattr(tr.config.option, 'apigenpath') + #assert hasattr(tr.config.option, 'apigenpath') capture = py.io.StdCaptureFD() try: self.apigenscript.build( Modified: py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py Thu Feb 26 18:16:03 2009 @@ -26,6 +26,7 @@ Project = confrest.pyimport().Project return Project(parent.dirpath()) + class ReSTFile(py.test.collect.File): def __init__(self, project, fspath, parent): super(ReSTFile, self).__init__(fspath=fspath, parent=parent) @@ -65,7 +66,8 @@ self.restcheck(py.path.svnwc(self.fspath)) def restcheck(self, path): - checkdocutils() + py.test.importorskip("docutils") + self.register_linkrole() from docutils.utils import SystemMessage try: self._checkskip(path, self.project.get_htmloutputpath(path)) @@ -76,6 +78,52 @@ # we assume docutils printed info on stdout py.test.fail("docutils processing failed, see captured stderr") + def register_linkrole(self): + from py.__.rest import directive + directive.register_linkrole('api', self.resolve_linkrole) + directive.register_linkrole('source', self.resolve_linkrole) + + def resolve_linkrole(self, name, text, check=True): + apigen_relpath = self.project.apigen_relpath + + if name == 'api': + if text == 'py': + return ('py', apigen_relpath + 'api/index.html') + else: + assert text.startswith('py.'), ( + 'api link "%s" does not point to the py package') % (text,) + dotted_name = text + if dotted_name.find('(') > -1: + dotted_name = dotted_name[:text.find('(')] + # remove pkg root + path = dotted_name.split('.')[1:] + dotted_name = '.'.join(path) + obj = py + if check: + for chunk in path: + try: + obj = getattr(obj, chunk) + except AttributeError: + raise AssertionError( + 'problem with linkrole :api:`%s`: can not resolve ' + 'dotted name %s' % (text, dotted_name,)) + return (text, apigen_relpath + 'api/%s.html' % (dotted_name,)) + elif name == 'source': + assert text.startswith('py/'), ('source link "%s" does not point ' + 'to the py package') % (text,) + relpath = '/'.join(text.split('/')[1:]) + if check: + pkgroot = py.__pkg__.getpath() + abspath = pkgroot.join(relpath) + assert pkgroot.join(relpath).check(), ( + 'problem with linkrole :source:`%s`: ' + 'path %s does not exist' % (text, relpath)) + if relpath.endswith('/') or not relpath: + relpath += 'index.html' + else: + relpath += '.html' + return (text, apigen_relpath + 'source/%s' % (relpath,)) + def _checkskip(self, lpath, htmlpath=None): if not self._config.getvalue("forcegen"): lpath = py.path.local(lpath) @@ -232,77 +280,6 @@ tryfn, anchor, path.basename, lineno+1)) - -_initialized = False -def checkdocutils(): - global _initialized - py.test.importorskip("docutils") - if not _initialized: - from py.__.rest import directive - directive.register_linkrole('api', resolve_linkrole) - directive.register_linkrole('source', resolve_linkrole) - _initialized = True - -def get_apigenpath(): - path = os.environ.get('APIGENPATH') - if path is None: - path = get_docpath().join("apigen") - return py.path.local(path) - -def get_docpath(): - path = os.environ.get('DOCPATH') - if path is None: - return mydir - else: - return py.path.local().join(path, abs=True) - -def get_apigen_relpath(): - return relpath(get_docpath().strpath + '/', - get_apigenpath().strpath + '/') - - -def resolve_linkrole(name, text, check=True): - apigen_relpath = get_apigen_relpath() - if name == 'api': - if text == 'py': - return ('py', apigen_relpath + 'api/index.html') - else: - assert text.startswith('py.'), ( - 'api link "%s" does not point to the py package') % (text,) - dotted_name = text - if dotted_name.find('(') > -1: - dotted_name = dotted_name[:text.find('(')] - # remove pkg root - path = dotted_name.split('.')[1:] - dotted_name = '.'.join(path) - obj = py - if check: - for chunk in path: - try: - obj = getattr(obj, chunk) - except AttributeError: - raise AssertionError( - 'problem with linkrole :api:`%s`: can not resolve ' - 'dotted name %s' % (text, dotted_name,)) - return (text, apigen_relpath + 'api/%s.html' % (dotted_name,)) - elif name == 'source': - assert text.startswith('py/'), ('source link "%s" does not point ' - 'to the py package') % (text,) - relpath = '/'.join(text.split('/')[1:]) - if check: - pkgroot = py.__pkg__.getpath() - abspath = pkgroot.join(relpath) - assert pkgroot.join(relpath).check(), ( - 'problem with linkrole :source:`%s`: ' - 'path %s does not exist' % (text, relpath)) - if relpath.endswith('/') or not relpath: - relpath += 'index.html' - else: - relpath += '.html' - return (text, apigen_relpath + 'source/%s' % (relpath,)) - - - # # PLUGIN tests # @@ -351,16 +328,7 @@ def pytest_pyfuncarg_testdir(__call__, pyfuncitem): testdir = __call__.execute(firstresult=True) - testdir.makepyfile(confrest=""" - from py.__.misc import rest - class Project: - def __init__(self, sourcepath): - self.sourcepath = sourcepath - def process(self, path): - return rest.process(path) - def get_htmloutputpath(self, path): - return path.new(ext='html') - """) + testdir.makepyfile(confrest="from py.__.misc.rest import Project") testdir.plugins.append(RestdocPlugin()) return testdir From py-svn at codespeak.net Fri Feb 27 00:09:35 2009 From: py-svn at codespeak.net (py-svn at codespeak.net) Date: Fri, 27 Feb 2009 00:09:35 +0100 (CET) Subject: [py-svn] Check, if .zip was attached? Message-ID: <20090226230935.C12DB1684C9@codespeak.net> An HTML attachment was scrubbed... URL: From hpk at codespeak.net Fri Feb 27 10:25:15 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 27 Feb 2009 10:25:15 +0100 (CET) Subject: [py-svn] r62205 - py/branch/pytestplugin/py/test Message-ID: <20090227092515.1C4D316847C@codespeak.net> Author: hpk Date: Fri Feb 27 10:25:14 2009 New Revision: 62205 Modified: py/branch/pytestplugin/py/test/collect.py Log: add a deprecated KW to avoid breakage of pypy branches Modified: py/branch/pytestplugin/py/test/collect.py ============================================================================== --- py/branch/pytestplugin/py/test/collect.py (original) +++ py/branch/pytestplugin/py/test/collect.py Fri Feb 27 10:25:14 2009 @@ -479,7 +479,9 @@ l.append(x) return l - def consider_dir(self, path): + def consider_dir(self, path, usefilters=None): + if usefilters is None: + APIWARN("0.99", "usefilters argument not needed") if not self.recfilter(path): # check if cmdline specified this dir or a subdir for arg in self._config.args: From hpk at codespeak.net Fri Feb 27 10:42:35 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 27 Feb 2009 10:42:35 +0100 (CET) Subject: [py-svn] r62207 - in py/branch/pytestplugin/py/test: . plugin testing Message-ID: <20090227094235.A0C2D16847C@codespeak.net> Author: hpk Date: Fri Feb 27 10:42:35 2009 New Revision: 62207 Added: py/branch/pytestplugin/py/test/plugin/pytest_default.py Modified: py/branch/pytestplugin/py/test/collect.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/defaultconftest.py py/branch/pytestplugin/py/test/parseopt.py py/branch/pytestplugin/py/test/testing/test_parseopt.py Log: shifting pytest defaults to a new DefaultPlugin Modified: py/branch/pytestplugin/py/test/collect.py ============================================================================== --- py/branch/pytestplugin/py/test/collect.py (original) +++ py/branch/pytestplugin/py/test/collect.py Fri Feb 27 10:42:35 2009 @@ -480,7 +480,7 @@ return l def consider_dir(self, path, usefilters=None): - if usefilters is None: + if usefilters is not None: APIWARN("0.99", "usefilters argument not needed") if not self.recfilter(path): # check if cmdline specified this dir or a subdir Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Fri Feb 27 10:42:35 2009 @@ -2,7 +2,6 @@ import py from conftesthandle import Conftest -from py.__.test.defaultconftest import adddefaultoptions from py.__.test import parseopt from py.__.misc.warn import APIWARN @@ -55,7 +54,6 @@ assert not self._initialized, ( "can only parse cmdline args at most once per Config object") self._initialized = True - adddefaultoptions(self) self._conftest.setinitial(args) self.pytestplugins.consider_env() self.pytestplugins.do_addoption(self._parser) Modified: py/branch/pytestplugin/py/test/defaultconftest.py ============================================================================== --- py/branch/pytestplugin/py/test/defaultconftest.py (original) +++ py/branch/pytestplugin/py/test/defaultconftest.py Fri Feb 27 10:42:35 2009 @@ -10,10 +10,8 @@ conf_iocapture = "fd" # overridable from conftest.py -# XXX resultlog should go, but pypy's nightrun depends on this -# default, need to change the pypy buildbot master configuration - -pytest_plugins = "terminal xfail tmpdir resultlog monkeypatch".split() +# XXX resultlog should go, pypy's nightrun depends on it +pytest_plugins = "default terminal xfail tmpdir resultlog monkeypatch".split() # =================================================== # Distributed testing specific options @@ -21,7 +19,7 @@ #dist_hosts: needs to be provided by user #dist_rsync_roots: might be provided by user, if not present or None, # whole pkgdir will be rsynced -# XXX deprecated dist_remotepython = None + dist_taskspernode = 15 dist_boxed = False if hasattr(py.std.os, 'nice'): @@ -30,85 +28,3 @@ dist_nicelevel = 0 dist_rsync_ignore = [] -# =================================================== - -class ConftestPlugin: - def pytest_collect_file(self, path, parent): - ext = path.ext - pb = path.purebasename - if pb.startswith("test_") or pb.endswith("_test") or \ - path in parent._config.args: - if ext == ".py": - return parent.Module(path, parent=parent) - -def adddefaultoptions(config): - Option = config.Option - group = config._parser.addgroup("general", "general options") - group._addoption('-v', '--verbose', action="count", - dest="verbose", default=0, help="increase verbosity."), - group._addoption('-x', '--exitfirst', - action="store_true", dest="exitfirst", default=False, - help="exit instantly on first error or failed test."), - group._addoption('-s', '--nocapture', - action="store_true", dest="nocapture", default=False, - help="disable catching of sys.stdout/stderr output."), - group._addoption('-k', - action="store", dest="keyword", default='', - help="only run test items matching the given " - "keyword expression."), - group._addoption('-l', '--showlocals', - action="store_true", dest="showlocals", default=False, - help="show locals in tracebacks (disabled by default)."), - group._addoption('--showskipsummary', - action="store_true", dest="showskipsummary", default=False, - help="always show summary of skipped tests"), - group._addoption('', '--pdb', - action="store_true", dest="usepdb", default=False, - help="start pdb (the Python debugger) on errors."), - group._addoption('', '--tb', - action="store", dest="tbstyle", default='long', - type="choice", choices=['long', 'short', 'no'], - help="traceback verboseness (long/short/no)."), - group._addoption('', '--fulltrace', - action="store_true", dest="fulltrace", default=False, - help="don't cut any tracebacks (default is to cut)."), - group._addoption('', '--nomagic', - action="store_true", dest="nomagic", default=False, - help="refrain from using magic as much as possible."), - group._addoption('', '--traceconfig', - action="store_true", dest="traceconfig", default=False, - help="trace considerations of conftest.py files."), - group._addoption('-f', '--looponfailing', - action="store_true", dest="looponfailing", default=False, - help="loop on failing test set."), - group._addoption('', '--exec', - action="store", dest="executable", default=None, - help="python executable to run the tests with."), - group._addoption('-n', '--numprocesses', dest="numprocesses", default=0, - action="store", type="int", - help="number of local test processes."), - group._addoption('', '--debug', - action="store_true", dest="debug", default=False, - help="turn on debugging information."), - - group = config._parser.addgroup("experimental", "experimental options") - group._addoption('-d', '--dist', - action="store_true", dest="dist", default=False, - help="ad-hoc distribute tests across machines (requires conftest settings)"), - group._addoption('-w', '--startserver', - action="store_true", dest="startserver", default=False, - help="starts local web server for displaying test progress.", - ), - group._addoption('-r', '--runbrowser', - action="store_true", dest="runbrowser", default=False, - help="run browser (implies --startserver)." - ), - group._addoption('', '--boxed', - action="store_true", dest="boxed", default=False, - help="box each test run in a separate process"), - group._addoption('', '--rest', - action='store_true', dest="restreport", default=False, - help="restructured text output reporting."), - group._addoption('', '--session', - action="store", dest="session", default=None, - help="lookup given sessioname in conftest.py files and use it."), Modified: py/branch/pytestplugin/py/test/parseopt.py ============================================================================== --- py/branch/pytestplugin/py/test/parseopt.py (original) +++ py/branch/pytestplugin/py/test/parseopt.py Fri Feb 27 10:42:35 2009 @@ -55,7 +55,7 @@ optgroup = optparse.OptionGroup(optparser, group.name) optgroup.add_options(group.options) optparser.add_option_group(optgroup) - return optparser.parse_args(args) + return optparser.parse_args([str(x) for x in args]) def parse_setoption(self, args, option): parsedoption, args = self.parse(args) Added: py/branch/pytestplugin/py/test/plugin/pytest_default.py ============================================================================== --- (empty file) +++ py/branch/pytestplugin/py/test/plugin/pytest_default.py Fri Feb 27 10:42:35 2009 @@ -0,0 +1,81 @@ +class DefaultPlugin: + """ Plugin implementing defaults and general options. """ + + def pytest_collect_file(self, path, parent): + ext = path.ext + pb = path.purebasename + if pb.startswith("test_") or pb.endswith("_test") or \ + path in parent._config.args: + if ext == ".py": + return parent.Module(path, parent=parent) + + def pytest_addoption(self, parser): + group = parser.addgroup("general", "general options") + group._addoption('-v', '--verbose', action="count", + dest="verbose", default=0, help="increase verbosity."), + group._addoption('-x', '--exitfirst', + action="store_true", dest="exitfirst", default=False, + help="exit instantly on first error or failed test."), + group._addoption('-s', '--nocapture', + action="store_true", dest="nocapture", default=False, + help="disable catching of sys.stdout/stderr output."), + group._addoption('-k', + action="store", dest="keyword", default='', + help="only run test items matching the given " + "keyword expression."), + group._addoption('-l', '--showlocals', + action="store_true", dest="showlocals", default=False, + help="show locals in tracebacks (disabled by default)."), + group._addoption('--showskipsummary', + action="store_true", dest="showskipsummary", default=False, + help="always show summary of skipped tests"), + group._addoption('', '--pdb', + action="store_true", dest="usepdb", default=False, + help="start pdb (the Python debugger) on errors."), + group._addoption('', '--tb', + action="store", dest="tbstyle", default='long', + type="choice", choices=['long', 'short', 'no'], + help="traceback verboseness (long/short/no)."), + group._addoption('', '--fulltrace', + action="store_true", dest="fulltrace", default=False, + help="don't cut any tracebacks (default is to cut)."), + group._addoption('', '--nomagic', + action="store_true", dest="nomagic", default=False, + help="refrain from using magic as much as possible."), + group._addoption('', '--traceconfig', + action="store_true", dest="traceconfig", default=False, + help="trace considerations of conftest.py files."), + group._addoption('-f', '--looponfailing', + action="store_true", dest="looponfailing", default=False, + help="loop on failing test set."), + group._addoption('', '--exec', + action="store", dest="executable", default=None, + help="python executable to run the tests with."), + group._addoption('-n', '--numprocesses', dest="numprocesses", default=0, + action="store", type="int", + help="number of local test processes."), + group._addoption('', '--debug', + action="store_true", dest="debug", default=False, + help="turn on debugging information."), + + group = parser.addgroup("experimental", "experimental options") + group._addoption('-d', '--dist', + action="store_true", dest="dist", default=False, + help="ad-hoc distribute tests across machines (requires conftest settings)"), + group._addoption('-w', '--startserver', + action="store_true", dest="startserver", default=False, + help="starts local web server for displaying test progress.", + ), + group._addoption('-r', '--runbrowser', + action="store_true", dest="runbrowser", default=False, + help="run browser (implies --startserver)." + ), + group._addoption('', '--boxed', + action="store_true", dest="boxed", default=False, + help="box each test run in a separate process"), + group._addoption('', '--rest', + action='store_true', dest="restreport", default=False, + help="restructured text output reporting."), + group._addoption('', '--session', + action="store", dest="session", default=None, + help="lookup given sessioname in conftest.py files and use it."), Modified: py/branch/pytestplugin/py/test/testing/test_parseopt.py ============================================================================== --- py/branch/pytestplugin/py/test/testing/test_parseopt.py (original) +++ py/branch/pytestplugin/py/test/testing/test_parseopt.py Fri Feb 27 10:42:35 2009 @@ -50,6 +50,11 @@ assert option.hello == "world" assert not args + def test_parse(self): + parser = parseopt.Parser() + option, args = parser.parse([py.path.local()]) + assert args[0] == py.path.local() + def test_parse_will_set_default(self): parser = parseopt.Parser() parser.addoption("--hello", dest="hello", default="x", action="store") From hpk at codespeak.net Fri Feb 27 10:54:30 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 27 Feb 2009 10:54:30 +0100 (CET) Subject: [py-svn] r62210 - in py/branch/pytestplugin/py: misc/testing test test/plugin Message-ID: <20090227095430.5E2DD1684CA@codespeak.net> Author: hpk Date: Fri Feb 27 10:54:29 2009 New Revision: 62210 Modified: py/branch/pytestplugin/py/misc/testing/test_cache.py py/branch/pytestplugin/py/test/config.py py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py Log: some fixes for making "-n 3" pass Modified: py/branch/pytestplugin/py/misc/testing/test_cache.py ============================================================================== --- py/branch/pytestplugin/py/misc/testing/test_cache.py (original) +++ py/branch/pytestplugin/py/misc/testing/test_cache.py Fri Feb 27 10:54:29 2009 @@ -52,7 +52,7 @@ class TestAging(BasicCacheAPITest): - maxsecs = 0.02 + maxsecs = 0.10 cache = AgingCache(maxentries=128, maxseconds=maxsecs) def test_cache_eviction(self): Modified: py/branch/pytestplugin/py/test/config.py ============================================================================== --- py/branch/pytestplugin/py/test/config.py (original) +++ py/branch/pytestplugin/py/test/config.py Fri Feb 27 10:54:29 2009 @@ -57,7 +57,6 @@ self._conftest.setinitial(args) self.pytestplugins.consider_env() self.pytestplugins.do_addoption(self._parser) - args = [str(x) for x in args] args = self._parser.parse_setoption(args, self.option) if not args: args.append(py.std.os.getcwd()) Modified: py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py (original) +++ py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py Fri Feb 27 10:54:29 2009 @@ -15,21 +15,23 @@ def pytest_collect_file(self, path, parent): if path.ext == ".txt": - project = self._getproject(path) + project = getproject(path) if project is not None: - return ReSTFile(project, path, parent=parent) - - def _getproject(self, path): - for parent in path.parts(reverse=True): - confrest = parent.join("confrest.py") - if confrest.check(): - Project = confrest.pyimport().Project - return Project(parent.dirpath()) + return ReSTFile(path, parent=parent, project=project) +def getproject(path): + for parent in path.parts(reverse=True): + confrest = parent.join("confrest.py") + if confrest.check(): + Project = confrest.pyimport().Project + return Project(parent.dirpath()) class ReSTFile(py.test.collect.File): - def __init__(self, project, fspath, parent): + def __init__(self, fspath, parent, project=None): super(ReSTFile, self).__init__(fspath=fspath, parent=parent) + if project is None: + project = getproject(fspath) + assert project is not None self.project = project def collect(self): From hpk at codespeak.net Fri Feb 27 11:18:32 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 27 Feb 2009 11:18:32 +0100 (CET) Subject: [py-svn] r62211 - in py/trunk: . contrib/py_unittest py py/apigen py/apigen/rest/testing py/apigen/testing py/bin py/cmdline/testing py/doc py/doc/example/pytest py/execnet/testing py/misc py/misc/testing py/path py/path/svn/testing py/path/testing py/rest/testing py/test py/test/attic py/test/attic/testing py/test/attic/webdata py/test/dsession py/test/dsession/testing py/test/looponfail py/test/looponfail/testing py/test/plugin py/test/report py/test/testing Message-ID: <20090227101832.7AE531684B9@codespeak.net> Author: hpk Date: Fri Feb 27 11:18:27 2009 New Revision: 62211 Added: py/trunk/py/_com.py - copied unchanged from r62210, py/branch/pytestplugin/py/_com.py py/trunk/py/doc/pytest-plugins.txt - copied unchanged from r62210, py/branch/pytestplugin/py/doc/pytest-plugins.txt py/trunk/py/misc/testing/test_com.py - copied unchanged from r62210, py/branch/pytestplugin/py/misc/testing/test_com.py py/trunk/py/test/attic/ (props changed) - copied from r62210, py/branch/pytestplugin/py/test/attic/ py/trunk/py/test/attic/__init__.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/__init__.py py/trunk/py/test/attic/rest.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/rest.py py/trunk/py/test/attic/testing/ (props changed) - copied from r62210, py/branch/pytestplugin/py/test/attic/testing/ py/trunk/py/test/attic/testing/__init__.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/testing/__init__.py py/trunk/py/test/attic/testing/test_rest.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/testing/test_rest.py py/trunk/py/test/attic/testing/test_web.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/testing/test_web.py py/trunk/py/test/attic/testing/test_webjs.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/testing/test_webjs.py py/trunk/py/test/attic/web.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/web.py py/trunk/py/test/attic/webdata/ (props changed) - copied from r62210, py/branch/pytestplugin/py/test/attic/webdata/ py/trunk/py/test/attic/webdata/__init__.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/webdata/__init__.py py/trunk/py/test/attic/webdata/index.html - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/webdata/index.html py/trunk/py/test/attic/webdata/json.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/webdata/json.py py/trunk/py/test/attic/webdata/source.js - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/webdata/source.js py/trunk/py/test/attic/webjs.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/attic/webjs.py py/trunk/py/test/parseopt.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/parseopt.py py/trunk/py/test/plugin/ (props changed) - copied from r62210, py/branch/pytestplugin/py/test/plugin/ py/trunk/py/test/plugin/__init__.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/__init__.py py/trunk/py/test/plugin/conftest.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/conftest.py py/trunk/py/test/plugin/pytest_apigen.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_apigen.py py/trunk/py/test/plugin/pytest_default.py - copied, changed from r62210, py/branch/pytestplugin/py/test/plugin/pytest_default.py py/trunk/py/test/plugin/pytest_doctest.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_doctest.py py/trunk/py/test/plugin/pytest_eventlog.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_eventlog.py py/trunk/py/test/plugin/pytest_iocapture.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_iocapture.py py/trunk/py/test/plugin/pytest_monkeypatch.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_monkeypatch.py py/trunk/py/test/plugin/pytest_plugintester.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_plugintester.py py/trunk/py/test/plugin/pytest_pocoo.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_pocoo.py py/trunk/py/test/plugin/pytest_pytester.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_pytester.py py/trunk/py/test/plugin/pytest_restdoc.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_restdoc.py py/trunk/py/test/plugin/pytest_resultlog.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_resultlog.py py/trunk/py/test/plugin/pytest_terminal.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_terminal.py py/trunk/py/test/plugin/pytest_tmpdir.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_tmpdir.py py/trunk/py/test/plugin/pytest_unittest.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_unittest.py py/trunk/py/test/plugin/pytest_xfail.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/plugin/pytest_xfail.py py/trunk/py/test/pytestplugin.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/pytestplugin.py py/trunk/py/test/testing/conftest.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/testing/conftest.py py/trunk/py/test/testing/test_collect_pickle.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/testing/test_collect_pickle.py py/trunk/py/test/testing/test_genitems.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/testing/test_genitems.py py/trunk/py/test/testing/test_parseopt.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/testing/test_parseopt.py py/trunk/py/test/testing/test_pycollect.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/testing/test_pycollect.py py/trunk/py/test/testing/test_pytestplugin.py - copied unchanged from r62210, py/branch/pytestplugin/py/test/testing/test_pytestplugin.py Removed: py/trunk/contrib/py_unittest/ py/trunk/py/doc/conftest.py py/trunk/py/doc/test_conftest.py py/trunk/py/test/report/ py/trunk/py/test/resultlog.py py/trunk/py/test/testing/suptest.py py/trunk/py/test/testing/test_doctest.py py/trunk/py/test/testing/test_event.py py/trunk/py/test/testing/test_resultlog.py Modified: py/trunk/ (props changed) py/trunk/py/__init__.py py/trunk/py/apigen/apigen.py py/trunk/py/apigen/conftest.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/layout.py py/trunk/py/apigen/project.py py/trunk/py/apigen/rest/testing/test_rest.py py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/bin/gendoc.py py/trunk/py/cmdline/testing/test_cmdline.py py/trunk/py/conftest.py py/trunk/py/doc/confrest.py py/trunk/py/doc/contact.txt py/trunk/py/doc/example/pytest/test_failures.py py/trunk/py/doc/future.txt py/trunk/py/doc/impl-test.txt py/trunk/py/doc/path.txt py/trunk/py/doc/test.txt py/trunk/py/doc/xml.txt py/trunk/py/execnet/testing/test_gateway.py py/trunk/py/initpkg.py py/trunk/py/misc/rest.py py/trunk/py/misc/testing/test_cache.py py/trunk/py/misc/testing/test_initpkg.py py/trunk/py/misc/testing/test_warn.py py/trunk/py/misc/warn.py py/trunk/py/path/common.py py/trunk/py/path/svn/testing/test_auth.py py/trunk/py/path/svn/testing/test_wccommand.py py/trunk/py/path/testing/common.py py/trunk/py/rest/testing/test_rst.py py/trunk/py/test/cmdline.py py/trunk/py/test/collect.py py/trunk/py/test/config.py py/trunk/py/test/conftesthandle.py py/trunk/py/test/defaultconftest.py py/trunk/py/test/dsession/dsession.py py/trunk/py/test/dsession/hostmanage.py py/trunk/py/test/dsession/masterslave.py py/trunk/py/test/dsession/testing/test_dsession.py py/trunk/py/test/dsession/testing/test_functional_dsession.py py/trunk/py/test/dsession/testing/test_hostmanage.py py/trunk/py/test/dsession/testing/test_masterslave.py py/trunk/py/test/event.py py/trunk/py/test/looponfail/remote.py py/trunk/py/test/looponfail/testing/test_remote.py py/trunk/py/test/looponfail/testing/test_util.py py/trunk/py/test/looponfail/util.py py/trunk/py/test/outcome.py py/trunk/py/test/pycollect.py py/trunk/py/test/runner.py py/trunk/py/test/session.py py/trunk/py/test/testing/acceptance_test.py py/trunk/py/test/testing/test_collect.py py/trunk/py/test/testing/test_config.py py/trunk/py/test/testing/test_conftesthandle.py py/trunk/py/test/testing/test_deprecated_api.py py/trunk/py/test/testing/test_recording.py py/trunk/py/test/testing/test_runner_functional.py py/trunk/py/test/testing/test_session.py py/trunk/py/test/testing/test_setup_nested.py Log: merge 60797:HEAD of pytestplugin branch: this merge contains: * a new plugin architecture * a pluginized pytest core * many pytest related refactorings * refactorings/streamlining of pytest's own tests Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Fri Feb 27 11:18:27 2009 @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- """ -The py lib is a development support library featuring these tools and APIs: +The py lib is an extensible library for testing, distributed processing and +interacting with filesystems. - `py.test`_: cross-project testing tool with many advanced features - `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes @@ -37,7 +38,7 @@ author_email = "holger at merlinux.eu, py-dev at codespeak.net", long_description = globals()['__doc__'], classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", @@ -53,6 +54,12 @@ # EXPORTED API exportdefs = { + + # py lib events and plugins + '_com.PyPlugins' : ('./_com.py', 'PyPlugins'), + '_com.MultiCall' : ('./_com.py', 'MultiCall'), + '_com.pyplugins' : ('./_com.py', 'pyplugins'), + # py lib cmdline tools 'cmdline.pytest' : ('./cmdline/pytest.py', 'main',), 'cmdline.pyrest' : ('./cmdline/pyrest.py', 'main',), @@ -64,7 +71,9 @@ # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), + 'test._PytestPlugins' : ('./test/pytestplugin.py', 'PytestPlugins'), 'test.raises' : ('./test/outcome.py', 'raises'), + 'test.keywords' : ('./test/outcome.py', 'keywords',), 'test.deprecated_call' : ('./test/outcome.py', 'deprecated_call'), 'test.skip' : ('./test/outcome.py', 'skip'), 'test.importorskip' : ('./test/outcome.py', 'importorskip'), @@ -83,7 +92,6 @@ 'test.collect.File' : ('./test/collect.py', 'File'), 'test.collect.Item' : ('./test/collect.py', 'Item'), 'test.collect.Module' : ('./test/pycollect.py', 'Module'), - 'test.collect.DoctestFile' : ('./test/pycollect.py', 'DoctestFile'), 'test.collect.Class' : ('./test/pycollect.py', 'Class'), 'test.collect.Instance' : ('./test/pycollect.py', 'Instance'), 'test.collect.Generator' : ('./test/pycollect.py', 'Generator'), @@ -187,3 +195,7 @@ 'compat.subprocess' : ('./compat/subprocess.py', '*'), }) +import py +py._com.pyplugins.consider_env() + + Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Fri Feb 27 11:18:27 2009 @@ -8,7 +8,6 @@ from py.__.apigen import linker from py.__.apigen import project from py.__.apigen.tracer.docstorage import pkg_to_dict -from py.__.doc.conftest import get_apigenpath from layout import LayoutPage @@ -26,9 +25,9 @@ def get_documentable_items(pkgdir): pkgname, pkgdict = get_documentable_items_pkgdir(pkgdir) - from py.__.execnet.channel import Channel - pkgdict['execnet.Channel'] = Channel - Channel.__apigen_hide_from_nav__ = True + #from py.__.execnet.channel import Channel + #pkgdict['execnet.Channel'] = Channel + #Channel.__apigen_hide_from_nav__ = True return pkgname, pkgdict def sourcedirfilter(p): @@ -36,7 +35,7 @@ not p.basename.startswith('.') and str(p).find('c-extension%sgreenlet%sbuild' % (p.sep, p.sep)) == -1) -def build(pkgdir, dsa, capture): +def build(config, pkgdir, dsa, capture): # create a linker (link database) for cross-linking l = linker.TempLinker() @@ -44,8 +43,7 @@ proj = project.Project() # output dir - from py.__.conftest import option - targetdir = get_apigenpath() + targetdir = proj.apigenpath targetdir.ensure(dir=True) # find out what to build Modified: py/trunk/py/apigen/conftest.py ============================================================================== --- py/trunk/py/apigen/conftest.py (original) +++ py/trunk/py/apigen/conftest.py Fri Feb 27 11:18:27 2009 @@ -1,10 +1,8 @@ import py -Option = py.test.config.Option -option = py.test.config.addoptions("apigen test options", - Option('', '--webcheck', +class ConftestPlugin: + def pytest_addoption(self, parser): + parser.addoption('--webcheck', action="store_true", dest="webcheck", default=False, help="run XHTML validation tests" - ), -) - + ) Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Fri Feb 27 11:18:27 2009 @@ -430,8 +430,9 @@ relpath = get_rel_sourcepath(self.projroot, sourcefile, sourcefile) text = 'source: %s' % (relpath,) if is_in_pkg: - href = self.linker.get_lazyhref(sourcefile, - self.get_anchor(func)) + #href = self.linker.get_lazyhref(sourcefile, + # self.get_anchor(func)) + href = self.linker.get_lazyhref(sourcefile) # csource = H.SourceSnippet(text, href, colored) cslinks = self.build_callsites(dotted_name) snippet = H.FunctionDescription(localname, argdesc, docstring, @@ -464,8 +465,8 @@ if sourcefile[-1] in ['o', 'c']: sourcefile = sourcefile[:-1] sourcelink = H.div(H.a('view source', - href=self.linker.get_lazyhref(sourcefile, - self.get_anchor(cls)))) + href=self.linker.get_lazyhref(sourcefile) #, self.get_anchor(cls) + )) snippet = H.ClassDescription( # XXX bases HTML Modified: py/trunk/py/apigen/layout.py ============================================================================== --- py/trunk/py/apigen/layout.py (original) +++ py/trunk/py/apigen/layout.py Fri Feb 27 11:18:27 2009 @@ -6,7 +6,6 @@ import py from py.__.doc import confrest from py.__.apigen import linker -from py.__.doc.conftest import get_apigenpath, get_docpath here = py.magic.autopath().dirpath() @@ -25,7 +24,7 @@ def get_relpath(self): return linker.relpath(self.targetpath.strpath, - get_apigenpath().strpath) + '/' + self.project.apigenpath.strpath) + '/' def set_content(self, contentel): self.contentspace.append(contentel) Modified: py/trunk/py/apigen/project.py ============================================================================== --- py/trunk/py/apigen/project.py (original) +++ py/trunk/py/apigen/project.py Fri Feb 27 11:18:27 2009 @@ -8,14 +8,18 @@ import py from layout import LayoutPage -class Project(py.__.doc.confrest.Project): +# XXX don't import from an internal py lib class +from py.__.doc import confrest + +class Project(confrest.Project): """ a full project this takes care of storing information on the first pass, and building pages + indexes on the second """ - def __init__(self): + def __init__(self, *args, **kwargs): + confrest.Project.__init__(self, *args, **kwargs) self.content_items = {} def add_item(self, path, content): Modified: py/trunk/py/apigen/rest/testing/test_rest.py ============================================================================== --- py/trunk/py/apigen/rest/testing/test_rest.py (original) +++ py/trunk/py/apigen/rest/testing/test_rest.py Fri Feb 27 11:18:27 2009 @@ -15,7 +15,6 @@ import pickle from py.__.apigen.tracer.testing.runtest import cut_pyc -from py.__.doc.conftest import genlinkchecks from py.__.rest.rst import Rest, Paragraph from py.__.rest.transform import HTMLHandler # XXX: UUuuuuuuuuuuuuuuuuuuuuuuu, dangerous import @@ -186,9 +185,11 @@ py.test.skip('skipping rest generation because docutils is ' 'not installed (this is a partial skip, the rest ' 'of the test was successful)') - for path in tempdir.listdir('*.txt'): - for item, arg1, arg2, arg3 in genlinkchecks(path): - item(arg1, arg2, arg3) + py.test.skip("partial skip: find a nice way to re-use pytest_restdoc's genlinkchecks") + # XXX find a nice way check pytest_restdoc's genlinkchecks() + #for path in tempdir.listdir('*.txt'): + # for item, arg1, arg2, arg3 in genlinkchecks(path): + # item(arg1, arg2, arg3) def test_generation_simple_api(self): ds = self.get_filled_docstorage() Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Fri Feb 27 11:18:27 2009 @@ -8,11 +8,8 @@ from py.__.apigen.layout import LayoutPage from py.__.apigen.project import Project from py.__.test.web import webcheck -from py.__.apigen.conftest import option from py.__.path.svn.testing.svntestbase import make_test_repo -py.test.skip("apigen needs work on py.test to work again") - def run_string_sequence_test(data, seq): currpos = -1 for s in seq: @@ -84,7 +81,7 @@ if isinstance(htmlstring, unicode): htmlstring = htmlstring.encode('UTF-8', 'replace') assert isinstance(htmlstring, str) - if option.webcheck: + if py.test.config.option.webcheck: webcheck.check_html(htmlstring) else: py.test.skip("pass --webcheck to validate html produced in tests " @@ -238,7 +235,8 @@ self.linker.replace_dirpath(self.base, False) funchtml = self.base.join('api/main.SomeClass.html').read() print funchtml - assert funchtml.find('href="../source/pkg/someclass.py.html#SomeClass"') > -1 + #assert funchtml.find('href="../source/pkg/someclass.py.html#SomeClass"') > -1 + assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) def test_build_namespace_pages(self): Modified: py/trunk/py/bin/gendoc.py ============================================================================== --- py/trunk/py/bin/gendoc.py (original) +++ py/trunk/py/bin/gendoc.py Fri Feb 27 11:18:27 2009 @@ -50,7 +50,7 @@ if apigendir.check(): print apigendir, "exists, not re-generating - remove to trigger regeneration" else: - sysexec('%(env)s %(pytest)s --apigen=%(pypath)s/apigen/apigen.py py' % locals()) + sysexec('%(env)s %(pytest)s py' % locals()) print print "*" * 30, "static generation", "*" * 30 sysexec('%(env)s %(pytest)s --forcegen %(pypath)s/doc' % locals()) Modified: py/trunk/py/cmdline/testing/test_cmdline.py ============================================================================== --- py/trunk/py/cmdline/testing/test_cmdline.py (original) +++ py/trunk/py/cmdline/testing/test_cmdline.py Fri Feb 27 11:18:27 2009 @@ -1,18 +1,18 @@ -from py.__.test.testing import suptest -from py.__.test.testing.acceptance_test import AcceptBase -class TestPyLookup(AcceptBase): - def test_basic(self): - p = self.makepyfile(hello="def x(): pass") - result = self.runpybin("py.lookup", "pass") - suptest.assert_lines_contain_lines(result.outlines, +pytest_plugins = "pytest_pytester" + +class TestPyLookup: + def test_basic(self, testdir): + p = testdir.makepyfile(hello="def x(): pass") + result = testdir.runpybin("py.lookup", "pass") + result.stdout.fnmatch_lines( ['%s:*def x(): pass' %(p.basename)] ) - def test_search_in_filename(self): - p = self.makepyfile(hello="def x(): pass") - result = self.runpybin("py.lookup", "hello") - suptest.assert_lines_contain_lines(result.outlines, + def test_search_in_filename(self, testdir): + p = testdir.makepyfile(hello="def x(): pass") + result = testdir.runpybin("py.lookup", "hello") + result.stdout.fnmatch_lines( ['*%s:*' %(p.basename)] ) Modified: py/trunk/py/conftest.py ============================================================================== --- py/trunk/py/conftest.py (original) +++ py/trunk/py/conftest.py Fri Feb 27 11:18:27 2009 @@ -1,41 +1,18 @@ -#pythonexecutables = ('python2.2', 'python2.3',) -#pythonexecutable = 'python2.2' +dist_rsync_roots = ['.'] # XXX -# in the future we want to be able to say here: -#def setup_module(extpy): -# mod = extpy.resolve() -# mod.module = 23 -# directory = pypath.root.dirpath() - -# default values for options (modified from cmdline) -verbose = 0 -nocapture = False -collectonly = False -exitfirst = False -fulltrace = False -showlocals = False -nomagic = False +pytest_plugins = 'pytest_doctest', 'pytest_pytester', 'pytest_restdoc' import py -Option = py.test.config.Option - -option = py.test.config.addoptions("execnet options", - Option('-S', '', - action="store", dest="sshtarget", default=None, +class PylibTestPlugin: + def pytest_addoption(self, parser): + group = parser.addgroup("pylib", "py lib testing options") + group.addoption('--sshhost', + action="store", dest="sshhost", default=None, help=("target to run tests requiring ssh, e.g. " - "user at codespeak.net")), - Option('', '--apigenpath', - action="store", dest="apigenpath", - default="../apigen", - type="string", - help="relative path to apigen doc output location (relative from py/)"), - Option('', '--docpath', - action='store', dest='docpath', - default="doc", type='string', - help="relative path to doc output location (relative from py/)"), - Option('', '--runslowtests', + "user at codespeak.net")) + group.addoption('--runslowtests', action="store_true", dest="runslowtests", default=False, - help="run slow tests"), - ) + help="run slow tests") + +ConftestPlugin = PylibTestPlugin -dist_rsync_roots = ['.'] Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Fri Feb 27 11:18:27 2009 @@ -1,7 +1,6 @@ import py from py.__.misc.rest import convert_rest_html, strip_html_header from py.__.misc.difftime import worded_time -from py.__.doc.conftest import get_apigenpath, get_docpath from py.__.apigen.linker import relpath html = py.xml.html @@ -25,13 +24,13 @@ self.fill() def a_docref(self, name, relhtmlpath): - docpath = self.project.get_docpath() + docpath = self.project.docpath return html.a(name, class_="menu", href=relpath(self.targetpath.strpath, docpath.join(relhtmlpath).strpath)) def a_apigenref(self, name, relhtmlpath): - apipath = get_apigenpath() + apipath = self.project.apigenpath return html.a(name, class_="menu", href=relpath(self.targetpath.strpath, apipath.join(relhtmlpath).strpath)) @@ -107,8 +106,6 @@ class Project: mydir = py.magic.autopath().dirpath() - # string for url, path for local file - stylesheet = mydir.join('style.css') title = "py lib" prefix_title = "" # we have a logo already containing "py lib" encoding = 'latin1' @@ -119,31 +116,53 @@ href="http://codespeak.net")) Page = PyPage + def __init__(self, sourcepath=None): + if sourcepath is None: + sourcepath = self.mydir + self.setpath(sourcepath) + + def setpath(self, sourcepath, docpath=None, + apigenpath=None, stylesheet=None): + self.sourcepath = sourcepath + if docpath is None: + docpath = sourcepath + self.docpath = docpath + if apigenpath is None: + apigenpath = docpath + self.apigenpath = apigenpath + if stylesheet is None: + p = sourcepath.join("style.css") + if p.check(): + self.stylesheet = p + else: + self.stylesheet = None + else: + p = py.path.local(stylesheet) + if p.check(): + stylesheet = p + self.stylesheet = stylesheet + self.apigen_relpath = relpath( + self.docpath.strpath + '/', self.apigenpath.strpath + '/') def get_content(self, txtpath, encoding): return unicode(txtpath.read(), encoding) - def get_docpath(self): - return get_docpath() - def get_htmloutputpath(self, txtpath): - docpath = self.get_docpath() - reloutputpath = txtpath.new(ext='.html').relto(self.mydir) - return docpath.join(reloutputpath) + reloutputpath = txtpath.new(ext='.html').relto(self.sourcepath) + return self.docpath.join(reloutputpath) def process(self, txtpath): encoding = self.encoding content = self.get_content(txtpath, encoding) - docpath = self.get_docpath() outputpath = self.get_htmloutputpath(txtpath) stylesheet = self.stylesheet - if isinstance(self.stylesheet, py.path.local): - if not docpath.join(stylesheet.basename).check(): + if isinstance(stylesheet, py.path.local): + if not self.docpath.join(stylesheet.basename).check(): docpath.ensure(dir=True) stylesheet.copy(docpath) stylesheet = relpath(outputpath.strpath, - docpath.join(stylesheet.basename).strpath) + self.docpath.join(stylesheet.basename).strpath) content = convert_rest_html(content, txtpath, stylesheet=stylesheet, encoding=encoding) Deleted: /py/trunk/py/doc/conftest.py ============================================================================== --- /py/trunk/py/doc/conftest.py Fri Feb 27 11:18:27 2009 +++ (empty file) @@ -1,322 +0,0 @@ -from __future__ import generators -import py -from py.__.misc import rest -from py.__.apigen.linker import relpath -import os - -pypkgdir = py.path.local(py.__file__).dirpath() - -mypath = py.magic.autopath().dirpath() - -TIMEOUT_URLOPEN = 5.0 - -Option = py.test.config.Option -option = py.test.config.addoptions("documentation check options", - Option('-R', '--checkremote', - action="store_true", dest="checkremote", default=False, - help="urlopen() remote links found in ReST text files.", - ), - Option('', '--forcegen', - action="store_true", dest="forcegen", default=False, - help="force generation of html files even if they appear up-to-date" - ), -) - -def get_apigenpath(): - from py.__.conftest import option - path = os.environ.get('APIGENPATH') - if path is None: - path = option.apigenpath - return pypkgdir.join(path, abs=True) - -def get_docpath(): - from py.__.conftest import option - path = os.environ.get('DOCPATH') - if path is None: - path = option.docpath - return pypkgdir.join(path, abs=True) - -def get_apigen_relpath(): - return relpath(get_docpath().strpath + '/', - get_apigenpath().strpath + '/') - -def deindent(s, sep='\n'): - leastspaces = -1 - lines = s.split(sep) - for line in lines: - if not line.strip(): - continue - spaces = len(line) - len(line.lstrip()) - if leastspaces == -1 or spaces < leastspaces: - leastspaces = spaces - if leastspaces == -1: - return s - for i, line in py.builtin.enumerate(lines): - if not line.strip(): - lines[i] = '' - else: - lines[i] = line[leastspaces:] - return sep.join(lines) - -_initialized = False -def checkdocutils(): - global _initialized - py.test.importorskip("docutils") - if not _initialized: - from py.__.rest import directive - directive.register_linkrole('api', resolve_linkrole) - directive.register_linkrole('source', resolve_linkrole) - _initialized = True - -def restcheck(path): - localpath = path - if hasattr(path, 'localpath'): - localpath = path.localpath - checkdocutils() - import docutils.utils - - try: - cur = localpath - for x in cur.parts(reverse=True): - confrest = x.dirpath('confrest.py') - if confrest.check(file=1): - confrest = confrest.pyimport() - project = confrest.Project() - _checkskip(path, project.get_htmloutputpath(path)) - project.process(path) - break - else: - # defer to default processor - _checkskip(path) - rest.process(path) - except KeyboardInterrupt: - raise - except docutils.utils.SystemMessage: - # we assume docutils printed info on stdout - py.test.fail("docutils processing failed, see captured stderr") - -def _checkskip(lpath, htmlpath=None): - if not option.forcegen: - lpath = py.path.local(lpath) - if htmlpath is not None: - htmlpath = py.path.local(htmlpath) - if lpath.ext == '.txt': - htmlpath = htmlpath or lpath.new(ext='.html') - if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime(): - py.test.skip("html file is up to date, use --forcegen to regenerate") - #return [] # no need to rebuild - -class ReSTSyntaxTest(py.test.collect.Item): - def runtest(self): - mypath = self.fspath - restcheck(py.path.svnwc(mypath)) - -class DoctestText(py.test.collect.Item): - def runtest(self): - s = self._normalize_linesep() - l = [] - prefix = '.. >>> ' - mod = py.std.types.ModuleType(self.fspath.purebasename) - skipchunk = False - for line in deindent(s).split('\n'): - stripped = line.strip() - if skipchunk and line.startswith(skipchunk): - print "skipping", line - continue - skipchunk = False - if stripped.startswith(prefix): - try: - exec py.code.Source(stripped[len(prefix):]).compile() in \ - mod.__dict__ - except ValueError, e: - if e.args and e.args[0] == "skipchunk": - skipchunk = " " * (len(line) - len(line.lstrip())) - else: - raise - else: - l.append(line) - docstring = "\n".join(l) - mod.__doc__ = docstring - failed, tot = py.compat.doctest.testmod(mod, verbose=1) - if failed: - py.test.fail("doctest %s: %s failed out of %s" %( - self.fspath, failed, tot)) - - def _normalize_linesep(self): - # XXX quite nasty... but it works (fixes win32 issues) - s = self.fspath.read() - linesep = '\n' - if '\r' in s: - if '\n' not in s: - linesep = '\r' - else: - linesep = '\r\n' - s = s.replace(linesep, '\n') - return s - -class LinkCheckerMaker(py.test.collect.Collector): - def collect(self): - l = [] - for call, tryfn, path, lineno in genlinkchecks(self.fspath): - name = "%s:%d" %(tryfn, lineno) - l.append( - CheckLink(name, parent=self, args=(tryfn, path, lineno), callobj=call) - ) - return l - -class CheckLink(py.test.collect.Function): - def repr_metainfo(self): - return self.ReprMetaInfo(fspath=self.fspath, lineno=self._args[2], - modpath="checklink: %s" % (self._args[0],)) - def setup(self): - pass - def teardown(self): - pass - -class DocfileTests(py.test.collect.File): - DoctestText = DoctestText - ReSTSyntaxTest = ReSTSyntaxTest - LinkCheckerMaker = LinkCheckerMaker - - def collect(self): - return [ - self.ReSTSyntaxTest(self.fspath.basename, parent=self), - self.LinkCheckerMaker("checklinks", self), - self.DoctestText("doctest", self), - ] - -# generating functions + args as single tests -def genlinkchecks(path): - for lineno, line in py.builtin.enumerate(path.readlines()): - line = line.strip() - if line.startswith('.. _'): - if line.startswith('.. _`'): - delim = '`:' - else: - delim = ':' - l = line.split(delim, 1) - if len(l) != 2: - continue - tryfn = l[1].strip() - if tryfn.startswith('http:') or tryfn.startswith('https'): - if option.checkremote: - yield urlcheck, tryfn, path, lineno - elif tryfn.startswith('webcal:'): - continue - else: - i = tryfn.find('#') - if i != -1: - checkfn = tryfn[:i] - else: - checkfn = tryfn - if checkfn.strip() and (1 or checkfn.endswith('.html')): - yield localrefcheck, tryfn, path, lineno - -def urlcheck(tryfn, path, lineno): - old = py.std.socket.getdefaulttimeout() - py.std.socket.setdefaulttimeout(TIMEOUT_URLOPEN) - try: - try: - print "trying remote", tryfn - py.std.urllib2.urlopen(tryfn) - finally: - py.std.socket.setdefaulttimeout(old) - except (py.std.urllib2.URLError, py.std.urllib2.HTTPError), e: - if e.code in (401, 403): # authorization required, forbidden - py.test.skip("%s: %s" %(tryfn, str(e))) - else: - py.test.fail("remote reference error %r in %s:%d\n%s" %( - tryfn, path.basename, lineno+1, e)) - -def localrefcheck(tryfn, path, lineno): - # assume it should be a file - i = tryfn.find('#') - if tryfn.startswith('javascript:'): - return # don't check JS refs - if i != -1: - anchor = tryfn[i+1:] - tryfn = tryfn[:i] - else: - anchor = '' - fn = path.dirpath(tryfn) - ishtml = fn.ext == '.html' - fn = ishtml and fn.new(ext='.txt') or fn - print "filename is", fn - if not fn.check(): # not ishtml or not fn.check(): - if not py.path.local(tryfn).check(): # the html could be there - py.test.fail("reference error %r in %s:%d" %( - tryfn, path.basename, lineno+1)) - if anchor: - source = unicode(fn.read(), 'latin1') - source = source.lower().replace('-', ' ') # aehem - - anchor = anchor.replace('-', ' ') - match2 = ".. _`%s`:" % anchor - match3 = ".. _%s:" % anchor - candidates = (anchor, match2, match3) - print "candidates", repr(candidates) - for line in source.split('\n'): - line = line.strip() - if line in candidates: - break - else: - py.test.fail("anchor reference error %s#%s in %s:%d" %( - tryfn, anchor, path.basename, lineno+1)) - - -# ___________________________________________________________ -# -# hooking into py.test Directory collector's chain ... - -class DocDirectory(py.test.collect.Directory): - DocfileTests = DocfileTests - def collect(self): - results = super(DocDirectory, self).collect() - for x in self.fspath.listdir('*.txt', sort=True): - results.append(self.DocfileTests(x, parent=self)) - return results - -Directory = DocDirectory - -def resolve_linkrole(name, text, check=True): - apigen_relpath = get_apigen_relpath() - if name == 'api': - if text == 'py': - return ('py', apigen_relpath + 'api/index.html') - else: - assert text.startswith('py.'), ( - 'api link "%s" does not point to the py package') % (text,) - dotted_name = text - if dotted_name.find('(') > -1: - dotted_name = dotted_name[:text.find('(')] - # remove pkg root - path = dotted_name.split('.')[1:] - dotted_name = '.'.join(path) - obj = py - if check: - for chunk in path: - try: - obj = getattr(obj, chunk) - except AttributeError: - raise AssertionError( - 'problem with linkrole :api:`%s`: can not resolve ' - 'dotted name %s' % (text, dotted_name,)) - return (text, apigen_relpath + 'api/%s.html' % (dotted_name,)) - elif name == 'source': - assert text.startswith('py/'), ('source link "%s" does not point ' - 'to the py package') % (text,) - relpath = '/'.join(text.split('/')[1:]) - if check: - pkgroot = py.__pkg__.getpath() - abspath = pkgroot.join(relpath) - assert pkgroot.join(relpath).check(), ( - 'problem with linkrole :source:`%s`: ' - 'path %s does not exist' % (text, relpath)) - if relpath.endswith('/') or not relpath: - relpath += 'index.html' - else: - relpath += '.html' - return (text, apigen_relpath + 'source/%s' % (relpath,)) - -# legacy -ReSTChecker = DocfileTests Modified: py/trunk/py/doc/contact.txt ============================================================================== --- py/trunk/py/doc/contact.txt (original) +++ py/trunk/py/doc/contact.txt Fri Feb 27 11:18:27 2009 @@ -16,8 +16,6 @@ .. _`merlinux.eu`: http://merlinux.eu -.. _`zope3`: http://zope3.zwiki.org/ -.. _twisted: http://www.twistedmatrix.org .. _future: future.html .. _`get an account`: Modified: py/trunk/py/doc/example/pytest/test_failures.py ============================================================================== --- py/trunk/py/doc/example/pytest/test_failures.py (original) +++ py/trunk/py/doc/example/pytest/test_failures.py Fri Feb 27 11:18:27 2009 @@ -2,14 +2,13 @@ import py failure_demo = py.magic.autopath().dirpath('failure_demo.py') -from py.__.test.testing import suptest -from py.__.test import event +pytest_plugins = "pytest_pytester" -def test_failure_demo_fails_properly(): - sorter = suptest.events_from_cmdline([failure_demo]) +def test_failure_demo_fails_properly(testdir): + sorter = testdir.inline_run(failure_demo) passed, skipped, failed = sorter.countoutcomes() assert passed == 0 assert failed == 20, failed - colreports = sorter.get(event.CollectionReport) + colreports = sorter.getnamed("collectionreport") failed = len([x.failed for x in colreports]) assert failed == 5 Modified: py/trunk/py/doc/future.txt ============================================================================== --- py/trunk/py/doc/future.txt (original) +++ py/trunk/py/doc/future.txt Fri Feb 27 11:18:27 2009 @@ -105,10 +105,7 @@ In other words, implementing a `memoryfs`_ or a `dictfs`_ would give you two things for free: a filesystem mountable at kernel level as well as a uniform "path" object allowing you to access your -filesystem in convenient ways. (At some point it might -even become interesting to think about interfacing to -`reiserfs v4 features`_ at the Filesystem level but that -is a can of subsequent worms). +filesystem in convenient ways. Also interesting to check out is Will McGugan's work on his `fs package`_. @@ -128,7 +125,6 @@ .. _`dictfs`: http://codespeak.net/pipermail/py-dev/2005-January/000191.html .. _`pylufs`: http://codespeak.net/svn/user/arigo/hack/pylufs/ .. _`pyfuse`: http://codespeak.net/svn/user/arigo/hack/pyfuse/ -.. _`reiserfs v4 features`: http://www.namesys.com/v4/v4.html Integrate interactive completion Modified: py/trunk/py/doc/impl-test.txt ============================================================================== --- py/trunk/py/doc/impl-test.txt (original) +++ py/trunk/py/doc/impl-test.txt Fri Feb 27 11:18:27 2009 @@ -270,3 +270,5 @@ function with the given (usually empty set of) arguments. .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev + + Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Fri Feb 27 11:18:27 2009 @@ -52,7 +52,7 @@ Some example usage of :api:`py.path.svnurl`:: .. >>> import py - .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') + .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> url = py.path.svnurl('http://codespeak.net/svn/py') >>> info = url.info() >>> info.kind @@ -64,7 +64,7 @@ Example usage of :api:`py.path.svnwc`:: - .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') + .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> temp = py.test.ensuretemp('py.path_documentation') >>> wc = py.path.svnwc(temp.join('svnwc')) >>> wc.checkout('http://codespeak.net/svn/py/dist/py/path/local') @@ -132,6 +132,10 @@ >>> sep = py.path.local.sep >>> p2.relto(p1).replace(sep, '/') # os-specific path sep in the string 'baz/qux' + >>> p2.bestrelpath(p1) + '../..' + >>> p2.join(p2.bestrelpath(p1)) == p1 + True >>> p3 = p1 / 'baz/qux' # the / operator allows joining, too >>> p2 == p3 True @@ -178,7 +182,7 @@ As an example of 'uncommon' methods, we'll show how to read and write properties in an :api:`py.path.svnwc` instance:: - .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') + .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> wc.propget('foo') '' >>> wc.propset('foo', 'bar') @@ -196,7 +200,7 @@ Some uncommon functionality can also be provided as extensions, such as SVN authentication:: - .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') + .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> auth = py.path.SvnAuth('anonymous', 'user', cache_auth=False, ... interactive=False) >>> wc.auth = auth Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Fri Feb 27 11:18:27 2009 @@ -142,23 +142,18 @@ selecting/unselecting tests by keyword --------------------------------------------- +Pytest's keyword mechanism provides a powerful way to +group and selectively run tests in your test code base. You can selectively run tests by specifiying a keyword -on the command line. Example:: +on the command line. Examples: - py.test -k test_simple + py.test -k test_simple + py.test -k "-test_simple" -will run all tests that are found from the current directory -and where the word "test_simple" equals the start of one part of the -path leading up to the test item. Directory and file basenames as well -as function, class and function/method names each form a possibly -matching name. You can also unselect tests by preceding a keyword -with a dash:: - - py.test. -k "-test_simple" - -will run all tests except where the word "test_simple" matches a tests keyword. -Note that you need to quote the keyword if the shell recognizes "-" as an intro -to a cmdline option. Lastly, you may use +will run all tests matching (or not matching) the +"test_simple" keyword. Note that you need to quote +the keyword if "-" is recognized as an indicator +for a commandline option. Lastly, you may use py.test. -k "test_simple:" @@ -166,6 +161,15 @@ all tests that are seen after a test that matches the "test_simple" 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 +kewords like this:: + + @py.test.keywords("webtest") + def test_send_http(): + ... + testing with multiple python versions / executables --------------------------------------------------- Deleted: /py/trunk/py/doc/test_conftest.py ============================================================================== --- /py/trunk/py/doc/test_conftest.py Fri Feb 27 11:18:27 2009 +++ (empty file) @@ -1,115 +0,0 @@ - -import py -from py.__.test import event -from py.__.test.testing import suptest -from py.__.doc import conftest as doc_conftest - - -class TestDoctest(suptest.InlineCollection): - def setup_method(self, method): - super(TestDoctest, self).setup_method(method) - p = py.path.local(doc_conftest.__file__) - if p.ext == ".pyc": - p = p.new(ext=".py") - p.copy(self.tmpdir.join("conftest.py")) - - def test_doctest_extra_exec(self): - xtxt = self.maketxtfile(x=""" - hello:: - .. >>> raise ValueError - >>> None - """) - sorter = suptest.events_from_cmdline([xtxt]) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 1 - - def test_doctest_basic(self): - xtxt = self.maketxtfile(x=""" - .. - >>> from os.path import abspath - - hello world - - >>> assert abspath - >>> i=3 - >>> print i - 3 - - yes yes - - >>> i - 3 - - end - """) - sorter = suptest.events_from_cmdline([xtxt]) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 0 - assert passed + skipped == 2 - - def test_doctest_eol(self): - ytxt = self.maketxtfile(y=".. >>> 1 + 1\r\n 2\r\n\r\n") - sorter = suptest.events_from_cmdline([ytxt]) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 0 - assert passed + skipped == 2 - - def test_doctest_indentation(self): - footxt = self.maketxtfile(foo= - '..\n >>> print "foo\\n bar"\n foo\n bar\n') - sorter = suptest.events_from_cmdline([footxt]) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 0 - assert skipped + passed == 2 - - def test_js_ignore(self): - xtxt = self.maketxtfile(xtxt=""" - `blah`_ - - .. _`blah`: javascript:some_function() - """) - sorter = suptest.events_from_cmdline([xtxt]) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 0 - assert skipped + passed == 3 - -def test_deindent(): - deindent = doc_conftest.deindent - assert deindent('foo') == 'foo' - assert deindent('foo\n bar') == 'foo\n bar' - assert deindent(' foo\n bar\n') == 'foo\nbar\n' - assert deindent(' foo\n\n bar\n') == 'foo\n\nbar\n' - assert deindent(' foo\n bar\n') == 'foo\n bar\n' - assert deindent(' foo\n bar\n') == ' foo\nbar\n' - - -def test_resolve_linkrole(): - from py.__.doc.conftest import get_apigen_relpath - apigen_relpath = get_apigen_relpath() - from py.__.doc.conftest import resolve_linkrole - assert resolve_linkrole('api', 'py.foo.bar', False) == ( - 'py.foo.bar', apigen_relpath + 'api/foo.bar.html') - assert resolve_linkrole('api', 'py.foo.bar()', False) == ( - 'py.foo.bar()', apigen_relpath + 'api/foo.bar.html') - assert resolve_linkrole('api', 'py', False) == ( - 'py', apigen_relpath + 'api/index.html') - py.test.raises(AssertionError, 'resolve_linkrole("api", "foo.bar")') - assert resolve_linkrole('source', 'py/foo/bar.py', False) == ( - 'py/foo/bar.py', apigen_relpath + 'source/foo/bar.py.html') - assert resolve_linkrole('source', 'py/foo/', False) == ( - 'py/foo/', apigen_relpath + 'source/foo/index.html') - assert resolve_linkrole('source', 'py/', False) == ( - 'py/', apigen_relpath + 'source/index.html') - py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")') - -def test_resolve_linkrole_check_api(): - from py.__.doc.conftest import resolve_linkrole - assert resolve_linkrole('api', 'py.test.ensuretemp') - py.test.raises(AssertionError, "resolve_linkrole('api', 'py.foo.baz')") - -def test_resolve_linkrole_check_source(): - from py.__.doc.conftest import resolve_linkrole - assert resolve_linkrole('source', 'py/path/common.py') - py.test.raises(AssertionError, - "resolve_linkrole('source', 'py/foo/bar.py')") - Modified: py/trunk/py/doc/xml.txt ============================================================================== --- py/trunk/py/doc/xml.txt (original) +++ py/trunk/py/doc/xml.txt Fri Feb 27 11:18:27 2009 @@ -168,5 +168,4 @@ your Tags. Hum, it's probably harder to explain this than to actually code it :-) -.. _Nevow: http://www.divmod.org/projects/nevow .. _`py.test`: test.html Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Fri Feb 27 11:18:27 2009 @@ -2,7 +2,6 @@ import os, sys, time, signal import py from py.__.execnet import gateway -from py.__.conftest import option mypath = py.magic.autopath() from StringIO import StringIO @@ -247,7 +246,10 @@ channel.waitclose(TESTTIMEOUT) assert l == [42] - def test_channel_callback_stays_active(self, earlyfree=True): + def test_channel_callback_stays_active(self): + self.check_channel_callback_stays_active(earlyfree=True) + + def check_channel_callback_stays_active(self, earlyfree=True): # with 'earlyfree==True', this tests the "sendonly" channel state. l = [] channel = self.gw.remote_exec(source=''' @@ -278,7 +280,7 @@ return subchannel def test_channel_callback_remote_freed(self): - channel = self.test_channel_callback_stays_active(False) + channel = self.check_channel_callback_stays_active(earlyfree=False) channel.waitclose(TESTTIMEOUT) # freed automatically at the end of producer() def test_channel_endmarker_callback(self): @@ -568,16 +570,16 @@ class TestSshGateway(BasicRemoteExecution): def setup_class(cls): - if option.sshtarget is None: - py.test.skip("no known ssh target, use -S to set one") - cls.gw = py.execnet.SshGateway(option.sshtarget) + if py.test.config.option.sshhost is None: + py.test.skip("no known ssh target, use --sshhost to set one") + cls.gw = py.execnet.SshGateway(py.test.config.option.sshhost) def test_sshconfig_functional(self): tmpdir = py.test.ensuretemp("test_sshconfig") ssh_config = tmpdir.join("ssh_config") ssh_config.write( "Host alias123\n" - " HostName %s\n" % (option.sshtarget,)) + " HostName %s\n" % (py.test.config.option.sshhost,)) gw = py.execnet.SshGateway("alias123", ssh_config=ssh_config) assert gw._cmd.find("-F") != -1 assert gw._cmd.find(str(ssh_config)) != -1 @@ -585,7 +587,7 @@ gw.exit() def test_sshaddress(self): - assert self.gw.remoteaddress == option.sshtarget + assert self.gw.remoteaddress == py.test.config.option.sshhost def test_connexion_failes_on_non_existing_hosts(self): py.test.raises(IOError, Modified: py/trunk/py/initpkg.py ============================================================================== --- py/trunk/py/initpkg.py (original) +++ py/trunk/py/initpkg.py Fri Feb 27 11:18:27 2009 @@ -64,12 +64,6 @@ def _resolve(self, extpyish): """ resolve a combined filesystem/python extpy-ish path. """ fspath, modpath = extpyish - if not fspath.endswith('.py'): - import py - e = py.path.local(self.implmodule.__file__) - e = e.dirpath(fspath, abs=True) - e = py.path.extpy(e, modpath) - return e.resolve() assert fspath.startswith('./'), \ "%r is not an implementation path (XXX)" % (extpyish,) implmodule = self._loadimpl(fspath[:-3]) @@ -166,15 +160,18 @@ sys.modules[modpath] = module # --------------------------------------------------- -# Virtual Module Object +# API Module Object # --------------------------------------------------- -class Module(ModuleType): +class ApiModule(ModuleType): def __init__(self, pkg, name): self.__pkg__ = pkg self.__name__ = name self.__map__ = {} + def __repr__(self): + return '' % (self.__name__,) + def __getattr__(self, name): if '*' in self.__map__: extpy = self.__map__['*'][0], name @@ -209,9 +206,6 @@ except (AttributeError, TypeError): pass - def __repr__(self): - return '' % (self.__name__, ) - def getdict(self): # force all the content of the module to be loaded when __dict__ is read dictdescr = ModuleType.__dict__['__dict__'] @@ -254,7 +248,7 @@ previous = current current += '.' + name if current not in seen: - seen[current] = mod = Module(pkg, current) + seen[current] = mod = ApiModule(pkg, current) setattr(seen[previous], name, mod) setmodule(current, mod) @@ -272,3 +266,12 @@ for mod, pypart, extpy in deferred_imports: setattr(mod, pypart, pkg._resolve(extpy)) + autoimport(pkgname) + +def autoimport(pkgname): + import py + ENVKEY = pkgname.upper() + "_AUTOIMPORT" + if ENVKEY in os.environ: + for impname in os.environ[ENVKEY].split(","): + py._com.pyplugins.notify("autoimport", impname) + __import__(impname) Modified: py/trunk/py/misc/rest.py ============================================================================== --- py/trunk/py/misc/rest.py (original) +++ py/trunk/py/misc/rest.py Fri Feb 27 11:18:27 2009 @@ -71,3 +71,11 @@ break uni = match.group(1) return uni + +class Project: # used for confrest.py files + def __init__(self, sourcepath): + self.sourcepath = sourcepath + def process(self, path): + return process(path) + def get_htmloutputpath(self, path): + return path.new(ext='html') Modified: py/trunk/py/misc/testing/test_cache.py ============================================================================== --- py/trunk/py/misc/testing/test_cache.py (original) +++ py/trunk/py/misc/testing/test_cache.py Fri Feb 27 11:18:27 2009 @@ -52,7 +52,7 @@ class TestAging(BasicCacheAPITest): - maxsecs = 0.02 + maxsecs = 0.10 cache = AgingCache(maxentries=128, maxseconds=maxsecs) def test_cache_eviction(self): Modified: py/trunk/py/misc/testing/test_initpkg.py ============================================================================== --- py/trunk/py/misc/testing/test_initpkg.py (original) +++ py/trunk/py/misc/testing/test_initpkg.py Fri Feb 27 11:18:27 2009 @@ -12,20 +12,20 @@ assert getattr(obj, '__map__') == {} def test_dir(): - from py.__.initpkg import Module + from py.__.initpkg import ApiModule for name in dir(py): if name == 'magic': # greenlets don't work everywhere, we don't care here continue if not name.startswith('_'): yield checksubpackage, name -from py.initpkg import Module +from py.initpkg import ApiModule glob = [] -class MyModule(Module): +class MyModule(ApiModule): def __init__(self, *args): glob.append(self.__dict__) assert isinstance(glob[-1], (dict, type(None))) - Module.__init__(self, *args) + ApiModule.__init__(self, *args) def test_early__dict__access(): mymod = MyModule("whatever", "myname") @@ -68,7 +68,10 @@ base.join('execnet', 'script'), base.join('compat', 'testing'), ) - for p in base.visit('*.py', lambda x: x.check(dotfile=0)): + def recurse(p): + return p.check(dotfile=0) and p.basename != "attic" + + for p in base.visit('*.py', recurse): if p.basename == '__init__.py': continue relpath = p.new(ext='').relto(base) @@ -255,3 +258,8 @@ # help(std.path) # #assert False + +def test_autoimport(): + from py.initpkg import autoimport + py.std.os.environ['AUTOTEST_AUTOIMPORT'] = "nonexistmodule" + py.test.raises(ImportError, "autoimport('autotest')") Modified: py/trunk/py/misc/testing/test_warn.py ============================================================================== --- py/trunk/py/misc/testing/test_warn.py (original) +++ py/trunk/py/misc/testing/test_warn.py Fri Feb 27 11:18:27 2009 @@ -1,20 +1,20 @@ import py -from py.__.misc.warn import WarningBus +from py.__.misc.warn import WarningPlugin mypath = py.magic.autopath() -class TestWarningBus: +class TestWarningPlugin: def setup_method(self, method): - self.wb = WarningBus() + self.bus = py._com.PyPlugins() + self.wb = WarningPlugin(self.bus) + self.bus.register(self) self.warnings = [] - self.wb.subscribe(self.warnings.append) - def test_basic(self): + def pyevent_WARNING(self, warning): + self.warnings.append(warning) + + def test_event_generation(self): self.wb.warn("hello") assert len(self.warnings) == 1 - self.wb.unsubscribe(self.warnings.append) - self.wb.warn("this") - assert len(self.warnings) == 1 - w = self.warnings[0] def test_location(self): self.wb.warn("again") @@ -27,19 +27,16 @@ assert str(warning) == warning.msg def test_stacklevel(self): - l = [] - self.wb.subscribe(l.append) def f(): self.wb.warn("x", stacklevel=2) - # 5 - # 6 + # 3 + # 4 f() - lno = self.test_stacklevel.im_func.func_code.co_firstlineno + 7 - warning = l[0] + lno = self.test_stacklevel.im_func.func_code.co_firstlineno + 5 + warning = self.warnings[0] assert warning.lineno == lno def test_forwarding_to_warnings_module(self): - self.wb._setforwarding() py.test.deprecated_call(self.wb.warn, "x") def test_apiwarn(self): @@ -47,7 +44,6 @@ warning = self.warnings[0] assert warning.msg == "xxx (since version 3.0)" -def test_APIWARN(): +def test_default(): from py.__.misc.warn import APIWARN - wb = APIWARN.im_self - assert wb._forward in wb._eventbus._subscribers + assert py._com.pyplugins.isregistered(APIWARN.im_self) Modified: py/trunk/py/misc/warn.py ============================================================================== --- py/trunk/py/misc/warn.py (original) +++ py/trunk/py/misc/warn.py Fri Feb 27 11:18:27 2009 @@ -1,5 +1,4 @@ import py, sys -from py.__.test.event import EventBus class Warning(py.std.exceptions.DeprecationWarning): def __init__(self, msg, path, lineno): @@ -11,19 +10,16 @@ def __str__(self): return self.msg -class WarningBus(object): - def __init__(self): - self._eventbus = EventBus() +# XXX probably only apiwarn() + py._com.pyplugins forwarding +# warn_explicit is actually needed - def subscribe(self, callable): - self._eventbus.subscribe(callable) - - def unsubscribe(self, callable): - self._eventbus.unsubscribe(callable) - - def _setforwarding(self): - self._eventbus.subscribe(self._forward) - def _forward(self, warning): +class WarningPlugin(object): + def __init__(self, bus): + self.bus = bus + bus.register(self) + + def pyevent_WARNING(self, warning): + # forward to python warning system py.std.warnings.warn_explicit(warning, category=Warning, filename=str(warning.path), lineno=warning.lineno, @@ -66,9 +62,8 @@ filename = module path = py.path.local(filename) warning = Warning(msg, path, lineno) - self._eventbus.notify(warning) + self.bus.notify("WARNING", warning) # singleton api warner for py lib -apiwarner = WarningBus() -apiwarner._setforwarding() +apiwarner = WarningPlugin(py._com.pyplugins) APIWARN = apiwarner.apiwarn Modified: py/trunk/py/path/common.py ============================================================================== --- py/trunk/py/path/common.py (original) +++ py/trunk/py/path/common.py Fri Feb 27 11:18:27 2009 @@ -152,6 +152,30 @@ return strself[len(strrelpath):] return "" + def bestrelpath(self, dest): + """ return relative path from self to dest + such that self.join(bestrelpath) == dest. + if not such path can be determined return dest. + """ + try: + base = self.common(dest) + if not base: # can be the case on windows + return dest + self2base = self.relto(base) + reldest = dest.relto(base) + if self2base: + n = self2base.count(self.sep) + 1 + else: + n = 0 + l = ['..'] * n + if reldest: + l.append(reldest) + target = dest.sep.join(l) + return target + except AttributeError: + return dest + + def parts(self, reverse=False): """ return a root-first list of all ancestor directories plus the path itself. Modified: py/trunk/py/path/svn/testing/test_auth.py ============================================================================== --- py/trunk/py/path/svn/testing/test_auth.py (original) +++ py/trunk/py/path/svn/testing/test_auth.py Fri Feb 27 11:18:27 2009 @@ -3,7 +3,6 @@ import svntestbase from threading import Thread import time -from py.__.conftest import option def make_repo_auth(repo, userdata): """ write config to repo @@ -251,7 +250,7 @@ class SvnAuthFunctionalTestBase(object): def setup_class(cls): svntestbase.getsvnbin() - if not option.runslowtests: + if not py.test.config.option.runslowtests: py.test.skip('skipping slow functional tests - use --runslowtests ' 'to override') Modified: py/trunk/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/trunk/py/path/svn/testing/test_wccommand.py (original) +++ py/trunk/py/path/svn/testing/test_wccommand.py Fri Feb 27 11:18:27 2009 @@ -4,7 +4,6 @@ from py.__.path.svn.wccommand import InfoSvnWCCommand, XMLWCStatus from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn import svncommon -from py.__.conftest import option if sys.platform != 'win32': def normpath(p): @@ -157,7 +156,7 @@ self.root.revert(rec=1) def test_status_conflict(self): - if not option.runslowtests: + if not py.test.config.option.runslowtests: py.test.skip('skipping slow unit tests - use --runslowtests ' 'to override') wc = self.root @@ -177,7 +176,7 @@ assert [x.basename for x in s.conflict] == ['conflictsamplefile'] def test_status_external(self): - if not option.runslowtests: + if not py.test.config.option.runslowtests: py.test.skip('skipping slow unit tests - use --runslowtests ' 'to override') otherrepo, otherwc = getrepowc('externalrepo', 'externalwc') Modified: py/trunk/py/path/testing/common.py ============================================================================== --- py/trunk/py/path/testing/common.py (original) +++ py/trunk/py/path/testing/common.py Fri Feb 27 11:18:27 2009 @@ -120,6 +120,18 @@ assert self.root.check(notrelto=l) assert not self.root.check(relto=l) + def test_bestrelpath(self): + curdir = self.root + sep = curdir.sep + s = curdir.bestrelpath(curdir.join("hello", "world")) + assert s == "hello" + sep + "world" + + s = curdir.bestrelpath(curdir.dirpath().join("sister")) + assert s == ".." + sep + "sister" + assert curdir.bestrelpath(curdir.dirpath()) == ".." + + assert curdir.bestrelpath("hello") == "hello" + def test_relto_not_relative(self): l1=self.root.join("bcde") l2=self.root.join("b") Modified: py/trunk/py/rest/testing/test_rst.py ============================================================================== --- py/trunk/py/rest/testing/test_rst.py (original) +++ py/trunk/py/rest/testing/test_rst.py Fri Feb 27 11:18:27 2009 @@ -3,7 +3,7 @@ """ from py.__.rest.rst import * -from py.__.doc.conftest import restcheck +from py.__.misc.rest import process as restcheck import traceback tempdir = py.test.ensuretemp('rest') Modified: py/trunk/py/test/cmdline.py ============================================================================== --- py/trunk/py/test/cmdline.py (original) +++ py/trunk/py/test/cmdline.py Fri Feb 27 11:18:27 2009 @@ -9,9 +9,11 @@ if args is None: args = py.std.sys.argv[1:] config = py.test.config - config.parse(args) + config.parse(args) + config.pytestplugins.configure(config) session = config.initsession() exitstatus = session.main() + config.pytestplugins.unconfigure(config) raise SystemExit(exitstatus) def warn_about_missing_assertion(): Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Fri Feb 27 11:18:27 2009 @@ -53,7 +53,6 @@ col = self.stack.pop() col.teardown() for col in needed_collectors[len(self.stack):]: - #print "setting up", col col.setup() self.stack.append(col) @@ -67,7 +66,7 @@ params = self.__dict__.copy() if self.fspath: if basedir is not None: - params['fspath'] = getrelpath(basedir, self.fspath) + params['fspath'] = basedir.bestrelpath(self.fspath) if self.lineno is not None: params['lineno'] = self.lineno + 1 @@ -108,6 +107,7 @@ self._config = config self.fspath = getattr(parent, 'fspath', None) + # # note to myself: Pickling is uh. # @@ -172,15 +172,17 @@ setattr(self, attrname, res) return res - def listchain(self): - """ return list of all parent collectors up to self. """ + def listchain(self, rootfirst=False): + """ return list of all parent collectors up to self, + starting form root of collection tree. """ l = [self] while 1: x = l[-1] if x.parent is not None: l.append(x.parent) else: - l.reverse() + if not rootfirst: + l.reverse() return l def listnames(self): @@ -200,6 +202,53 @@ cur = next return cur + + def _getfsnode(self, path): + # this method is usually called from + # config.getfsnode() which returns a colitem + # from filename arguments + # + # pytest's collector tree does not neccessarily + # follow the filesystem and we thus need to do + # some special matching code here because + # _getitembynames() works by colitem names, not + # basenames. + if path == self.fspath: + return self + basenames = path.relto(self.fspath).split(path.sep) + cur = self + while basenames: + basename = basenames.pop(0) + assert basename + fspath = cur.fspath.join(basename) + colitems = cur._memocollect() + l = [] + for colitem in colitems: + if colitem.fspath == fspath or colitem.name == basename: + l.append(colitem) + if not l: + msg = ("Collector %r does not provide %r colitem " + "existing colitems are: %s" % + (cur, fspath, colitems)) + raise AssertionError(msg) + if basenames: + if len(l) > 1: + msg = ("Collector %r has more than one %r colitem " + "existing colitems are: %s" % + (cur, fspath, colitems)) + raise AssertionError(msg) + cur = l[0] + else: + if len(l) > 1: + cur = l + else: + cur = l[0] + break + return cur + + def readkeywords(self): + return dict([(x, True) for x in self._keywords()]) + def _keywords(self): return [self.name] @@ -284,7 +333,6 @@ return repr repr_failure = _repr_failure_py - shortfailurerepr = "F" class Collector(Node): @@ -298,7 +346,7 @@ """ Directory = configproperty('Directory') Module = configproperty('Module') - DoctestFile = configproperty('DoctestFile') + #DoctestFile = configproperty('DoctestFile') def collect(self): """ returns a list of children (items and collectors) @@ -407,41 +455,45 @@ return l l = [] for path in self.fspath.listdir(sort=True): - res = self.consider(path, usefilters=True) + res = self.consider(path) if res is not None: - l.append(res) + if isinstance(res, (list, tuple)): + l.extend(res) + else: + l.append(res) return l - def consider(self, path, usefilters=True): + def consider(self, path): if path.check(file=1): - return self.consider_file(path, usefilters=usefilters) + return self.consider_file(path) elif path.check(dir=1): - return self.consider_dir(path, usefilters=usefilters) + return self.consider_dir(path) - def consider_file(self, path, usefilters=True): - ext = path.ext - pb = path.purebasename - if not usefilters or pb.startswith("test_") or pb.endswith("_test"): - if ext == ".py": - return self.Module(path, parent=self) - elif ext == ".txt": - return self.DoctestFile(path, parent=self) - - def consider_dir(self, path, usefilters=True): - if not usefilters or self.recfilter(path): - # not use self.Directory here as - # dir/conftest.py shall be able to - # define Directory(dir) already - Directory = self._config.getvalue('Directory', path) - return Directory(path, parent=self) + def consider_file(self, path): + res = self._config.pytestplugins.call_each( + 'pytest_collect_file', path=path, parent=self) + l = [] + # throw out identical modules + for x in res: + if x not in l: + l.append(x) + return l - def collect_by_name(self, name): - """ get a child with the given name. """ - res = super(Directory, self).collect_by_name(name) - if res is None: - p = self.fspath.join(name) - res = self.consider(p, usefilters=False) - return res + def consider_dir(self, path, usefilters=None): + if usefilters is not None: + APIWARN("0.99", "usefilters argument not needed") + if not self.recfilter(path): + # check if cmdline specified this dir or a subdir + for arg in self._config.args: + if path == arg or arg.relto(path): + break + else: + return + # not use self.Directory here as + # dir/conftest.py shall be able to + # define Directory(dir) already + Directory = self._config.getvalue('Directory', path) + return Directory(path, parent=self) from py.__.test.runner import basic_run_report, forked_run_report class Item(Node): @@ -479,27 +531,6 @@ def runtest(self): """ execute this test item.""" - -def getrelpath(curdir, dest): - try: - base = curdir.common(dest) - if not base: # can be the case on windows - return dest - curdir2base = curdir.relto(base) - reldest = dest.relto(base) - if curdir2base: - n = curdir2base.count(curdir.sep) + 1 - else: - n = 0 - l = ['..'] * n - if reldest: - l.append(reldest) - target = dest.sep.join(l) - return target - except AttributeError: - return dest - - def warnoldcollect(): APIWARN("1.0", "implement collector.collect() instead of " Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Fri Feb 27 11:18:27 2009 @@ -2,9 +2,9 @@ import py from conftesthandle import Conftest -from py.__.test.defaultconftest import adddefaultoptions -optparse = py.compat.optparse +from py.__.test import parseopt +from py.__.misc.warn import APIWARN # XXX move to Config class basetemp = None @@ -26,14 +26,26 @@ class Config(object): """ central bus for dealing with configuration/initialization data. """ - Option = optparse.Option + Option = py.compat.optparse.Option # deprecated _initialized = False - def __init__(self): + def __init__(self, pytestplugins=None): self.option = CmdOptions() - self._parser = optparse.OptionParser( - usage="usage: %prog [options] [query] [filenames of tests]") - self._conftest = Conftest() + self._parser = parseopt.Parser( + usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]", + processopt=self._processopt, + ) + if pytestplugins is None: + pytestplugins = py.test._PytestPlugins() + assert isinstance(pytestplugins, py.test._PytestPlugins) + self.bus = pytestplugins.pyplugins + self.pytestplugins = pytestplugins + self._conftest = Conftest(onimport=self.pytestplugins.consider_conftest) + + def _processopt(self, opt): + if hasattr(opt, 'default') and opt.dest: + if not hasattr(self.option, opt.dest): + setattr(self.option, opt.dest, opt.default) def parse(self, args): """ parse cmdline arguments into this config object. @@ -42,15 +54,14 @@ assert not self._initialized, ( "can only parse cmdline args at most once per Config object") self._initialized = True - adddefaultoptions(self) self._conftest.setinitial(args) - args = [str(x) for x in args] - cmdlineoption, args = self._parser.parse_args(args) - self.option.__dict__.update(vars(cmdlineoption)) + self.pytestplugins.consider_env() + self.pytestplugins.do_addoption(self._parser) + args = self._parser.parse_setoption(args, self.option) if not args: args.append(py.std.os.getcwd()) self.topdir = gettopdir(args) - self.args = args + self.args = [py.path.local(x) for x in args] # config objects are usually pickled across system # barriers but they contain filesystem paths. @@ -62,11 +73,15 @@ self._repr = repr def _initafterpickle(self, topdir): - self.__init__() + self.__init__( + #issue1 + #pytestplugins=py.test._PytestPlugins(py._com.pyplugins) + ) self._initialized = True self.topdir = py.path.local(topdir) self._mergerepr(self._repr) del self._repr + self.pytestplugins.configure(config=self) def _makerepr(self): l = [] @@ -87,6 +102,9 @@ self.option = cmdlineopts self._conftest.setinitial(self.args) + def getcolitems(self): + return [self.getfsnode(arg) for arg in self.args] + def getfsnode(self, path): path = py.path.local(path) assert path.check(), "%s: path does not exist" %(path,) @@ -96,8 +114,7 @@ pkgpath = path.check(file=1) and path.dirpath() or path Dir = self._conftest.rget("Directory", pkgpath) col = Dir(pkgpath, config=self) - names = path.relto(col.fspath).split(path.sep) - return col._getitembynames(names) + return col._getfsnode(path) def getvalue_pathlist(self, name, path=None): """ return a matching value, which needs to be sequence @@ -119,24 +136,14 @@ """ add a named group of options to the current testing session. This function gets invoked during testing session initialization. """ - for spec in specs: - for shortopt in spec._short_opts: - if not shortopt.isupper(): - raise ValueError( - "custom options must be capital letter " - "got %r" %(spec,) - ) - return self._addoptions(groupname, *specs) - - def _addoptions(self, groupname, *specs): - optgroup = optparse.OptionGroup(self._parser, groupname) - optgroup.add_options(specs) - self._parser.add_option_group(optgroup) - for opt in specs: - if hasattr(opt, 'default') and opt.dest: - if not hasattr(self.option, opt.dest): - setattr(self.option, opt.dest, opt.default) - return self.option + APIWARN("1.0", "define plugins to add options", stacklevel=2) + group = self._parser.addgroup(groupname) + for opt in specs: + group._addoption_instance(opt) + return self.option + + def addoption(self, *optnames, **attrs): + return self._parser.addoption(*optnames, **attrs) def getvalue(self, name, path=None): """ return 'name' value looked up from the 'options' @@ -150,30 +157,21 @@ except AttributeError: return self._conftest.rget(name, path) - def initreporter(self, bus): - if self.option.collectonly: - from py.__.test.report.collectonly import Reporter - else: - from py.__.test.report.terminal import Reporter - rep = Reporter(self, bus=bus) - return rep - def initsession(self): """ return an initialized session object. """ - cls = self._getsessionclass() + cls = self._getestdirclass() session = cls(self) session.fixoptions() - session.reporter = self.initreporter(session.bus) return session - def _getsessionclass(self): + def _getestdirclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. """ if self.option.session is not None: return self._conftest.rget(self.option.session) else: - name = self._getsessionname() + name = self._getestdirname() try: return self._conftest.rget(name) except KeyError: @@ -182,7 +180,7 @@ mod = __import__(importpath, None, None, '__doc__') return getattr(mod, name) - def _getsessionname(self): + def _getestdirname(self): """ return default session name as determined from options. """ if self.option.collectonly: name = 'Session' @@ -221,42 +219,10 @@ raise ValueError("unknown io capturing: " + iocapture) - def gettracedir(self): - """ return a tracedirectory or None, depending on --tracedir. """ - if self.option.tracedir is not None: - return py.path.local(self.option.tracedir) - - def maketrace(self, name, flush=True): - """ return a tracedirectory or None, depending on --tracedir. """ - tracedir = self.gettracedir() - if tracedir is None: - return NullTracer() - tracedir.ensure(dir=1) - return Tracer(tracedir.join(name), flush=flush) - -class Tracer(object): - file = None - def __init__(self, path, flush=True): - self.file = path.open(mode='w') - self.flush = flush - - def __call__(self, *args): - time = round(py.std.time.time(), 3) - print >>self.file, time, " ".join(map(str, args)) - if self.flush: - self.file.flush() - - def close(self): - self.file.close() - -class NullTracer: - def __call__(self, *args): - pass - def close(self): - pass - # this is the one per-process instance of py.test configuration -config_per_process = Config() +config_per_process = Config( + pytestplugins=py.test._PytestPlugins(py._com.pyplugins) +) # default import paths for sessions Modified: py/trunk/py/test/conftesthandle.py ============================================================================== --- py/trunk/py/test/conftesthandle.py (original) +++ py/trunk/py/test/conftesthandle.py Fri Feb 27 11:18:27 2009 @@ -9,8 +9,9 @@ conftest.py files may result in added cmdline options. XXX """ - def __init__(self, path=None): + def __init__(self, path=None, onimport=None): self._path2confmods = {} + self._onimport = onimport if path is not None: self.setinitial([path]) @@ -37,11 +38,11 @@ except KeyError: dp = path.dirpath() if dp == path: - return [importconfig(defaultconftestpath)] + return [self.importconftest(defaultconftestpath)] clist = self.getconftestmodules(dp) conftestpath = path.join("conftest.py") if conftestpath.check(file=1): - clist.append(importconfig(conftestpath)) + clist.append(self.importconftest(conftestpath)) self._path2confmods[path] = clist # be defensive: avoid changes from caller side to # affect us by always returning a copy of the actual list @@ -61,15 +62,17 @@ continue raise KeyError, name -def importconfig(configpath): - # We could have used caching here, but it's redundant since - # they're cached on path anyway, so we use it only when doing rget_path - assert configpath.check(), configpath - if not configpath.dirpath('__init__.py').check(file=1): - # HACK: we don't want any "globally" imported conftest.py, - # prone to conflicts and subtle problems - modname = str(configpath).replace('.', configpath.sep) - mod = configpath.pyimport(modname=modname) - else: - mod = configpath.pyimport() - return mod + def importconftest(self, conftestpath): + # Using caching here looks redundant since ultimately + # sys.modules caches already + assert conftestpath.check(), conftestpath + if not conftestpath.dirpath('__init__.py').check(file=1): + # HACK: we don't want any "globally" imported conftest.py, + # prone to conflicts and subtle problems + modname = str(conftestpath).replace('.', conftestpath.sep) + mod = conftestpath.pyimport(modname=modname) + else: + mod = conftestpath.pyimport() + if self._onimport: + self._onimport(mod) + return mod Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Fri Feb 27 11:18:27 2009 @@ -1,7 +1,7 @@ import py Module = py.test.collect.Module -DoctestFile = py.test.collect.DoctestFile +#DoctestFile = py.test.collect.DoctestFile Directory = py.test.collect.Directory Class = py.test.collect.Class Generator = py.test.collect.Generator @@ -10,13 +10,16 @@ conf_iocapture = "fd" # overridable from conftest.py +# XXX resultlog should go, pypy's nightrun depends on it +pytest_plugins = "default terminal xfail tmpdir resultlog monkeypatch".split() + # =================================================== # Distributed testing specific options #dist_hosts: needs to be provided by user #dist_rsync_roots: might be provided by user, if not present or None, # whole pkgdir will be rsynced -# XXX deprecated dist_remotepython = None + dist_taskspernode = 15 dist_boxed = False if hasattr(py.std.os, 'nice'): @@ -25,97 +28,3 @@ dist_nicelevel = 0 dist_rsync_ignore = [] -# =================================================== - -def adddefaultoptions(config): - Option = config.Option - config._addoptions('general options', - Option('-v', '--verbose', - action="count", dest="verbose", default=0, - help="increase verbosity."), - Option('-x', '--exitfirst', - action="store_true", dest="exitfirst", default=False, - help="exit instantly on first error or failed test."), - Option('-s', '--nocapture', - action="store_true", dest="nocapture", default=False, - help="disable catching of sys.stdout/stderr output."), - Option('-k', - action="store", dest="keyword", default='', - help="only run test items matching the given " - "space separated keywords. precede a keyword with '-' to negate. " - "Terminate the expression with ':' to treat a match as a signal to run all subsequent tests. " - ), - Option('-l', '--showlocals', - action="store_true", dest="showlocals", default=False, - help="show locals in tracebacks (disabled by default)."), - Option('--showskipsummary', - action="store_true", dest="showskipsummary", default=False, - help="always show summary of skipped tests"), - Option('', '--pdb', - action="store_true", dest="usepdb", default=False, - help="start pdb (the Python debugger) on errors."), - Option('', '--eventlog', - action="store", dest="eventlog", default=None, - help="write reporting events to given file."), - Option('', '--tracedir', - action="store", dest="tracedir", default=None, - help="write tracing information to the given directory."), - Option('', '--tb', - action="store", dest="tbstyle", default='long', - type="choice", choices=['long', 'short', 'no'], - help="traceback verboseness (long/short/no)."), - Option('', '--fulltrace', - action="store_true", dest="fulltrace", default=False, - help="don't cut any tracebacks (default is to cut)."), - Option('', '--nomagic', - action="store_true", dest="nomagic", default=False, - help="refrain from using magic as much as possible."), - Option('', '--collectonly', - action="store_true", dest="collectonly", default=False, - help="only collect tests, don't execute them."), - Option('', '--traceconfig', - action="store_true", dest="traceconfig", default=False, - help="trace considerations of conftest.py files."), - Option('-f', '--looponfailing', - action="store_true", dest="looponfailing", default=False, - help="loop on failing test set."), - Option('', '--exec', - action="store", dest="executable", default=None, - help="python executable to run the tests with."), - Option('-n', '--numprocesses', dest="numprocesses", default=0, - action="store", type="int", - help="number of local test processes."), - Option('', '--debug', - action="store_true", dest="debug", default=False, - help="turn on debugging information."), - ) - - config._addoptions('EXPERIMENTAL options', - Option('-d', '--dist', - action="store_true", dest="dist", default=False, - help="ad-hoc distribute tests across machines (requires conftest settings)"), - Option('-w', '--startserver', - action="store_true", dest="startserver", default=False, - help="starts local web server for displaying test progress.", - ), - Option('-r', '--runbrowser', - action="store_true", dest="runbrowser", default=False, - help="run browser (implies --startserver)." - ), - Option('', '--boxed', - action="store_true", dest="boxed", default=False, - help="box each test run in a separate process"), - Option('', '--rest', - action='store_true', dest="restreport", default=False, - help="restructured text output reporting."), - Option('', '--apigen', - action="store", dest="apigen", - help="generate api documentation while testing (requires " - "argument pointing to a script)."), - Option('', '--session', - action="store", dest="session", default=None, - help="lookup given sessioname in conftest.py files and use it."), - Option('--resultlog', action="store", - default=None, dest="resultlog", - help="path for machine-readable result log") - ) Modified: py/trunk/py/test/dsession/dsession.py ============================================================================== --- py/trunk/py/test/dsession/dsession.py (original) +++ py/trunk/py/test/dsession/dsession.py Fri Feb 27 11:18:27 2009 @@ -8,11 +8,10 @@ from py.__.test import event import py.__.test.custompdb from py.__.test.dsession.hostmanage import HostManager -Item = (py.test.collect.Item, py.test.collect.Item) -Collector = (py.test.collect.Collector, py.test.collect.Collector) +Item = py.test.collect.Item +Collector = py.test.collect.Collector from py.__.test.runner import basic_run_report, basic_collect_report from py.__.test.session import Session -from py.__.test.runner import OutcomeRepr from py.__.test import outcome import Queue @@ -42,7 +41,6 @@ self.host2pending = {} self.item2host = {} self._testsfailed = False - self.trace = config.maketrace("dsession.log") def fixoptions(self): """ check, fix and determine conflicting options. """ @@ -79,11 +77,13 @@ def main(self, colitems=None): colitems = self.getinitialitems(colitems) - self.bus.notify(event.TestrunStart()) + #self.bus.notify(event.TestrunStart()) + self.sessionstarts() self.setup_hosts() exitstatus = self.loop(colitems) - self.bus.notify(event.TestrunFinish(exitstatus=exitstatus)) + #self.bus.notify(event.TestrunFinish(exitstatus=exitstatus)) self.teardown_hosts() + self.sessionfinishes() return exitstatus def loop_once(self, loopstate): @@ -96,28 +96,34 @@ # we use a timeout here so that control-C gets through while 1: try: - ev = self.queue.get(timeout=2.0) + eventcall = self.queue.get(timeout=2.0) break except Queue.Empty: continue loopstate.dowork = True - self.bus.notify(ev) - if isinstance(ev, event.ItemTestReport): + + eventname, args, kwargs = eventcall + self.bus.notify(eventname, *args, **kwargs) + if args: + ev, = args + else: + ev = None + if eventname == "itemtestreport": self.removeitem(ev.colitem) if ev.failed: loopstate.testsfailed = True - elif isinstance(ev, event.CollectionReport): + elif eventname == "collectionreport": if ev.passed: colitems.extend(ev.result) - elif isinstance(ev, event.HostUp): + elif eventname == "hostup": self.addhost(ev.host) - elif isinstance(ev, event.HostDown): + elif eventname == "hostdown": pending = self.removehost(ev.host) if pending: crashitem = pending.pop(0) self.handle_crashitem(crashitem, ev.host) colitems.extend(pending) - elif isinstance(ev, event.RescheduleItems): + elif eventname == "rescheduleitems": colitems.extend(ev.items) loopstate.dowork = False # avoid busywait @@ -132,9 +138,10 @@ def loop_once_shutdown(self, loopstate): # once we are in shutdown mode we dont send # events other than HostDown upstream - ev = self.queue.get() - if isinstance(ev, event.HostDown): - self.bus.notify(ev) + eventname, args, kwargs = self.queue.get() + if eventname == "hostdown": + ev, = args + self.bus.notify("hostdown", ev) self.removehost(ev.host) if not self.host2pending: # finished @@ -155,7 +162,7 @@ except KeyboardInterrupt: exitstatus = outcome.EXIT_INTERRUPTED except: - self.bus.notify(event.InternalException()) + self.bus.notify("internalerror", event.InternalException()) exitstatus = outcome.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: exitstatus = outcome.EXIT_TESTSFAILED @@ -173,7 +180,6 @@ pending = self.host2pending.pop(host) for item in pending: del self.item2host[item] - self.trace("removehost %s, pending=%r" %(host.hostid, pending)) return pending def triggertesting(self, colitems): @@ -183,11 +189,13 @@ if isinstance(next, Item): senditems.append(next) else: - ev = basic_collect_report(next) - self.bus.notify(event.CollectionStart(next)) - self.queue.put(ev) + self.bus.notify("collectionstart", event.CollectionStart(next)) + self.queueevent("collectionreport", basic_collect_report(next)) self.senditems(senditems) + def queueevent(self, eventname, *args, **kwargs): + self.queue.put((eventname, args, kwargs)) + def senditems(self, tosend): if not tosend: return @@ -196,38 +204,36 @@ if room > 0: sending = tosend[:room] host.node.sendlist(sending) - self.trace("sent to host %s: %r" %(host.hostid, sending)) for item in sending: #assert item not in self.item2host, ( # "sending same item %r to multiple hosts " # "not implemented" %(item,)) self.item2host[item] = host - self.bus.notify(event.ItemStart(item, host)) + self.bus.notify("itemstart", event.ItemStart(item, host)) pending.extend(sending) tosend[:] = tosend[room:] # update inplace if not tosend: break if tosend: # we have some left, give it to the main loop - self.queue.put(event.RescheduleItems(tosend)) + self.queueevent("rescheduleitems", event.RescheduleItems(tosend)) def removeitem(self, item): if item not in self.item2host: raise AssertionError(item, self.item2host) host = self.item2host.pop(item) self.host2pending[host].remove(item) - self.trace("removed %r, host=%r" %(item,host.hostid)) + #self.config.bus.notify("removeitem", item, host.hostid) def handle_crashitem(self, item, host): - longrepr = "%r CRASHED THE HOST %r" %(item, host) - outcome = OutcomeRepr(when="execute", shortrepr="c", longrepr=longrepr) - rep = event.ItemTestReport(item, failed=outcome) - self.bus.notify(rep) + longrepr = "!!! Host %r crashed during running of test %r" %(host, item) + rep = event.ItemTestReport(item, when="???", excinfo=longrepr) + self.bus.notify("itemtestreport", rep) def setup_hosts(self): """ setup any neccessary resources ahead of the test run. """ self.hm = HostManager(self) - self.hm.setup_hosts(notify=self.queue.put) + self.hm.setup_hosts(putevent=self.queue.put) def teardown_hosts(self): """ teardown any resources after a test run. """ Modified: py/trunk/py/test/dsession/hostmanage.py ============================================================================== --- py/trunk/py/test/dsession/hostmanage.py (original) +++ py/trunk/py/test/dsession/hostmanage.py Fri Feb 27 11:18:27 2009 @@ -148,7 +148,7 @@ def prepare_gateways(self): for host in self.hosts: host.initgateway() - self.session.bus.notify(event.HostGatewayReady(host, self.roots)) + self.session.bus.notify("hostgatewayready", event.HostGatewayReady(host, self.roots)) def init_rsync(self): self.prepare_gateways() @@ -164,16 +164,14 @@ for host in self.hosts: rsync.add_target_host(host, destrelpath) rsync.send(raises=False) - self.session.bus.notify(event.RsyncFinished()) + self.session.bus.notify("rsyncfinished", event.RsyncFinished()) - def setup_hosts(self, notify=None): - if notify is None: - notify = self.session.bus.notify + def setup_hosts(self, putevent): self.init_rsync() for host in self.hosts: host.node = MasterNode(host, self.session.config, - notify) + putevent) # # helpers Modified: py/trunk/py/test/dsession/masterslave.py ============================================================================== --- py/trunk/py/test/dsession/masterslave.py (original) +++ py/trunk/py/test/dsession/masterslave.py Fri Feb 27 11:18:27 2009 @@ -8,16 +8,19 @@ class MasterNode(object): ENDMARK = -1 - def __init__(self, host, config, notify): + def __init__(self, host, config, putevent): self.host = host self.config = config - self.notify = notify + self.putevent = putevent self.channel = install_slave(host, config) self.channel.setcallback(self.callback, endmarker=self.ENDMARK) self._down = False + + def notify(self, eventname, *args, **kwargs): + self.putevent((eventname, args, kwargs)) - def callback(self, ev): - """ this gets called for each item we receive from + def callback(self, eventcall): + """ this gets called for each object we receive from the other side and if the channel closes. Note that the callback runs in the receiver @@ -25,24 +28,27 @@ avoid raising exceptions or doing heavy work. """ try: - if ev == self.ENDMARK: + if eventcall == self.ENDMARK: err = self.channel._getremoteerror() if not self._down: if not err: err = "TERMINATED" - self.notify(event.HostDown(self.host, err)) + self.notify("hostdown", event.HostDown(self.host, err)) return - if ev is None: + elif eventcall is None: self._down = True - self.notify(event.HostDown(self.host, None)) + self.notify("hostdown", event.HostDown(self.host, None)) return except KeyboardInterrupt: raise except: excinfo = py.code.ExceptionInfo() print "!" * 20, excinfo - ev = event.InternalException(excinfo) - self.notify(ev) + self.notify("internalerror", event.InternalException(excinfo)) + else: + # XXX we need to have the proper event name + eventname, args, kwargs = eventcall + self.notify(eventname, *args, **kwargs) def send(self, item): assert item is not None @@ -101,24 +107,22 @@ def __repr__(self): host = getattr(self, 'host', '') - return "<%s host=%s>" %(self.__class__.__name__, host.hostid) + return "<%s host=%s>" %(self.__class__.__name__, host) + + def sendevent(self, eventname, *args, **kwargs): + self.channel.send((eventname, args, kwargs)) def run(self): from py.__.test.dsession.hostmanage import makehostup channel = self.channel self.host = host = channel.receive() - channel.send(makehostup(host)) - self.trace = self.config.maketrace(host.hostid) - self.trace("initialized") - + self.sendevent("hostup", makehostup(host)) try: while 1: task = channel.receive() - self.trace("received", task) - + self.config.bus.notify("masterslave_receivedtask", task) if task is None: # shutdown - channel.send(None) - self.trace("shutting down, send None to", channel) + self.channel.send(None) break if isinstance(task, list): for item in task: @@ -128,15 +132,10 @@ except KeyboardInterrupt: raise except: - rep = event.InternalException() - self.trace("sending back internal exception report, breaking loop") - channel.send(rep) + self.sendevent("internalerror", event.InternalException()) raise - else: - self.trace("normal shutdown") def runtest(self, item): runner = item._getrunner() testrep = runner(item) - self.channel.send(testrep) - self.trace("sent back testreport", testrep) + self.sendevent("itemtestreport", testrep) Modified: py/trunk/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/trunk/py/test/dsession/testing/test_dsession.py (original) +++ py/trunk/py/test/dsession/testing/test_dsession.py Fri Feb 27 11:18:27 2009 @@ -1,4 +1,3 @@ -from py.__.test.testing.suptest import InlineCollection from py.__.test.dsession.dsession import DSession, LoopState from py.__.test.dsession.hostmanage import Host, makehostup from py.__.test.runner import basic_collect_report @@ -24,17 +23,17 @@ while queue.qsize(): print queue.get() -class TestDSession(InlineCollection): - def test_fixoptions(self): - config = self.parseconfig("--exec=xxx") +class TestDSession: + def test_fixoptions(self, testdir): + config = testdir.parseconfig("--exec=xxx") config.initsession().fixoptions() assert config.option.numprocesses == 1 - config = self.parseconfig("--exec=xxx", '-n3') + config = testdir.parseconfig("--exec=xxx", '-n3') config.initsession().fixoptions() assert config.option.numprocesses == 3 - def test_add_remove_host(self): - item = self.getitem("def test_func(): pass") + def test_add_remove_host(self, testdir): + item = testdir.getitem("def test_func(): pass") rep = run(item) session = DSession(item._config) host = Host("localhost") @@ -48,8 +47,8 @@ assert item not in session.item2host py.test.raises(Exception, "session.removehost(host)") - def test_senditems_removeitems(self): - item = self.getitem("def test_func(): pass") + def test_senditems_removeitems(self, testdir): + item = testdir.getitem("def test_func(): pass") rep = run(item) session = DSession(item._config) host = Host("localhost") @@ -62,19 +61,20 @@ assert not session.host2pending[host] assert not session.item2host - def test_triggertesting_collect(self): - modcol = self.getmodulecol(""" + def test_triggertesting_collect(self, testdir): + modcol = testdir.getmodulecol(""" def test_func(): pass """) session = DSession(modcol._config) session.triggertesting([modcol]) - rep = session.queue.get(block=False) - assert isinstance(rep, event.CollectionReport) + name, args, kwargs = session.queue.get(block=False) + assert name == 'collectionreport' + rep, = args assert len(rep.result) == 1 - def test_triggertesting_item(self): - item = self.getitem("def test_func(): pass") + def test_triggertesting_item(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() @@ -89,51 +89,52 @@ assert host2_sent == [item] * session.MAXITEMSPERHOST assert session.host2pending[host1] == host1_sent assert session.host2pending[host2] == host2_sent - ev = session.queue.get(block=False) - assert isinstance(ev, event.RescheduleItems) + name, args, kwargs = session.queue.get(block=False) + assert name == "rescheduleitems" + ev, = args assert ev.items == [item] - def test_keyboardinterrupt(self): - item = self.getitem("def test_func(): pass") + def test_keyboardinterrupt(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) def raise_(timeout=None): raise KeyboardInterrupt() session.queue.get = raise_ exitstatus = session.loop([]) assert exitstatus == outcome.EXIT_INTERRUPTED - def test_internalerror(self): - item = self.getitem("def test_func(): pass") + def test_internalerror(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) def raise_(): raise ValueError() session.queue.get = raise_ exitstatus = session.loop([]) assert exitstatus == outcome.EXIT_INTERNALERROR - def test_rescheduleevent(self): - item = self.getitem("def test_func(): pass") + def test_rescheduleevent(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) ev = event.RescheduleItems([item]) loopstate = LoopState([]) - session.queue.put(ev) + session.queueevent("rescheduleitems", ev) session.loop_once(loopstate) # check that RescheduleEvents are not immediately # rescheduled if there are no hosts assert loopstate.dowork == False - session.queue.put(event.NOP()) + session.queueevent("anonymous", event.NOP()) session.loop_once(loopstate) - session.queue.put(event.NOP()) + session.queueevent("anonymous", event.NOP()) session.loop_once(loopstate) assert host1.node.sent == [[item]] - session.queue.put(run(item)) + session.queueevent("itemtestreport", run(item)) session.loop_once(loopstate) assert loopstate.shuttingdown assert not loopstate.testsfailed - def test_no_hosts_remaining_for_tests(self): - item = self.getitem("def test_func(): pass") + def test_no_hosts_remaining_for_tests(self, testdir): + item = testdir.getitem("def test_func(): pass") # setup a session with one host session = DSession(item._config) host1 = Host("localhost") @@ -142,7 +143,7 @@ # setup a HostDown event ev = event.HostDown(host1, None) - session.queue.put(ev) + session.queueevent("hostdown", ev) loopstate = LoopState([item]) loopstate.dowork = False @@ -150,8 +151,8 @@ dumpqueue(session.queue) assert loopstate.exitstatus == outcome.EXIT_NOHOSTS - def test_hostdown_causes_reschedule_pending(self): - modcol = self.getmodulecol(""" + def test_hostdown_causes_reschedule_pending(self, testdir, EventRecorder): + modcol = testdir.getmodulecol(""" def test_crash(): assert 0 def test_fail(): @@ -172,41 +173,39 @@ session.senditems([item1, item2]) host = session.item2host[item1] ev = event.HostDown(host, None) - session.queue.put(ev) - - events = [] ; session.bus.subscribe(events.append) + session.queueevent("hostdown", ev) + evrec = EventRecorder(session.bus) loopstate = LoopState([]) session.loop_once(loopstate) assert loopstate.colitems == [item2] # do not reschedule crash item - testrep = [x for x in events if isinstance(x, event.ItemTestReport)][0] + testrep = evrec.getfirstnamed("itemtestreport") assert testrep.failed assert testrep.colitem == item1 - assert str(testrep.outcome.longrepr).find("CRASHED") != -1 - assert str(testrep.outcome.longrepr).find(host.hostname) != -1 + assert str(testrep.longrepr).find("crashed") != -1 + assert str(testrep.longrepr).find(host.hostname) != -1 - def test_hostup_adds_to_available(self): - item = self.getitem("def test_func(): pass") + def test_hostup_adds_to_available(self, testdir): + item = testdir.getitem("def test_func(): pass") # setup a session with two hosts session = DSession(item._config) host1 = Host("localhost") hostup = makehostup(host1) - session.queue.put(hostup) + session.queueevent("hostup", hostup) loopstate = LoopState([item]) loopstate.dowork = False assert len(session.host2pending) == 0 session.loop_once(loopstate) assert len(session.host2pending) == 1 - def test_event_propagation(self): - item = self.getitem("def test_func(): pass") + def test_event_propagation(self, testdir, EventRecorder): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) - ev = event.NOP() - events = [] ; session.bus.subscribe(events.append) - session.queue.put(ev) + evrec = EventRecorder(session.bus) + session.queueevent("NOPevent", 42) session.loop_once(LoopState([])) - assert events[0] == ev + assert evrec.getfirstnamed('NOPevent') def runthrough(self, item): session = DSession(item._config) @@ -215,31 +214,31 @@ session.addhost(host1) loopstate = LoopState([item]) - session.queue.put(event.NOP()) + session.queueevent("NOP") session.loop_once(loopstate) assert host1.node.sent == [[item]] ev = run(item) - session.queue.put(ev) + session.queueevent("itemtestreport", ev) session.loop_once(loopstate) assert loopstate.shuttingdown - session.queue.put(event.HostDown(host1, None)) + session.queueevent("hostdown", event.HostDown(host1, None)) session.loop_once(loopstate) dumpqueue(session.queue) return session, loopstate.exitstatus - def test_exit_completed_tests_ok(self): - item = self.getitem("def test_func(): pass") + def test_exit_completed_tests_ok(self, testdir): + item = testdir.getitem("def test_func(): pass") session, exitstatus = self.runthrough(item) assert exitstatus == outcome.EXIT_OK - def test_exit_completed_tests_fail(self): - item = self.getitem("def test_func(): 0/0") + def test_exit_completed_tests_fail(self, testdir): + item = testdir.getitem("def test_func(): 0/0") session, exitstatus = self.runthrough(item) assert exitstatus == outcome.EXIT_TESTSFAILED - def test_exit_on_first_failing(self): - modcol = self.getmodulecol(""" + def test_exit_on_first_failing(self, testdir): + modcol = testdir.getmodulecol(""" def test_fail(): assert 0 def test_pass(): @@ -258,33 +257,32 @@ # run tests ourselves and produce reports ev1 = run(items[0]) ev2 = run(items[1]) - session.queue.put(ev1) # a failing one - session.queue.put(ev2) + session.queueevent("itemtestreport", ev1) # a failing one + session.queueevent("itemtestreport", ev2) # now call the loop loopstate = LoopState(items) session.loop_once(loopstate) assert loopstate.testsfailed assert loopstate.shuttingdown - def test_shuttingdown_filters_events(self): - item = self.getitem("def test_func(): pass") + def test_shuttingdown_filters_events(self, testdir, EventRecorder): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) host = Host("localhost") session.addhost(host) loopstate = LoopState([]) loopstate.shuttingdown = True - l = [] - session.bus.subscribe(l.append) - session.queue.put(run(item)) + evrec = EventRecorder(session.bus) + session.queueevent("itemtestreport", run(item)) session.loop_once(loopstate) - assert not l + assert not evrec.getfirstnamed("hostdown") ev = event.HostDown(host) - session.queue.put(ev) + session.queueevent("hostdown", ev) session.loop_once(loopstate) - assert l == [ev] + assert evrec.getfirstnamed('hostdown') == ev - def test_filteritems(self): - modcol = self.getmodulecol(""" + def test_filteritems(self, testdir, EventRecorder): + modcol = testdir.getmodulecol(""" def test_fail(): assert 0 def test_pass(): @@ -296,42 +294,42 @@ dsel = session.filteritems([modcol]) assert dsel == [modcol] items = modcol.collect() - events = [] ; session.bus.subscribe(events.append) + evrec = EventRecorder(session.bus) remaining = session.filteritems(items) assert remaining == [] - ev = events[-1] - assert isinstance(ev, event.Deselected) + evname, ev = evrec.events[-1] + assert evname == "deselected" assert ev.items == items modcol._config.option.keyword = "test_fail" remaining = session.filteritems(items) assert remaining == [items[0]] - ev = events[-1] - assert isinstance(ev, event.Deselected) + evname, ev = evrec.events[-1] + assert evname == "deselected" assert ev.items == [items[1]] - def test_hostdown_shutdown_after_completion(self): - item = self.getitem("def test_func(): pass") + def test_hostdown_shutdown_after_completion(self, testdir): + item = testdir.getitem("def test_func(): pass") session = DSession(item._config) host = Host("localhost") host.node = MockNode() session.addhost(host) session.senditems([item]) - session.queue.put(run(item)) + session.queueevent("itemtestreport", run(item)) loopstate = LoopState([]) session.loop_once(loopstate) assert host.node._shutdown is True assert loopstate.exitstatus is None, "loop did not wait for hostdown" assert loopstate.shuttingdown - session.queue.put(event.HostDown(host, None)) + session.queueevent("hostdown", event.HostDown(host, None)) session.loop_once(loopstate) assert loopstate.exitstatus == 0 - def test_nopending_but_collection_remains(self): - modcol = self.getmodulecol(""" + def test_nopending_but_collection_remains(self, testdir): + modcol = testdir.getmodulecol(""" def test_fail(): assert 0 def test_pass(): @@ -347,10 +345,10 @@ session.senditems([item1]) # host2pending will become empty when the loop sees # the report - session.queue.put(run(item1)) + session.queueevent("itemtestreport", run(item1)) # but we have a collection pending - session.queue.put(colreport) + session.queueevent("collectionreport", colreport) loopstate = LoopState([]) session.loop_once(loopstate) Modified: py/trunk/py/test/dsession/testing/test_functional_dsession.py ============================================================================== --- py/trunk/py/test/dsession/testing/test_functional_dsession.py (original) +++ py/trunk/py/test/dsession/testing/test_functional_dsession.py Fri Feb 27 11:18:27 2009 @@ -3,74 +3,48 @@ """ import py -from py.__.test import event from py.__.test.dsession.dsession import DSession from py.__.test.dsession.hostmanage import HostManager, Host -from py.__.test.testing import suptest +from test_masterslave import EventQueue + import os -def eventreader(session): - queue = py.std.Queue.Queue() - session.bus.subscribe(queue.put) - def readevent(eventtype=event.ItemTestReport, timeout=2.0): - events = [] - while 1: - try: - ev = queue.get(timeout=timeout) - except py.std.Queue.Empty: - print "seen events", events - raise IOError("did not see %r events" % (eventtype)) - else: - if isinstance(ev, eventtype): - #print "other events seen", events - return ev - events.append(ev) - return readevent - -class TestAsyncFunctional(suptest.InlineCollection): - def test_dist_no_disthost(self): - config = self.parseconfig(self.tmpdir, '-d') - py.test.raises(SystemExit, "config.initsession()") - def test_session_eventlog_dist(self): - self.makepyfile(conftest="dist_hosts=['localhost']\n") - eventlog = self.tmpdir.join("mylog") - config = self.parseconfig(self.tmpdir, '-d', '--eventlog=%s' % eventlog) - session = config.initsession() - session.bus.notify(event.TestrunStart()) - s = eventlog.read() - assert s.find("TestrunStart") != -1 +class TestAsyncFunctional: + def test_dist_no_disthost(self, testdir): + config = testdir.parseconfig(testdir.tmpdir, '-d') + py.test.raises(SystemExit, "config.initsession()") - def test_conftest_options(self): - self.makepyfile(conftest=""" + def test_conftest_options(self, testdir): + testdir.makepyfile(conftest=""" print "importing conftest" import py Option = py.test.config.Option option = py.test.config.addoptions("someopt", - Option('', '--forcegen', action="store_true", dest="forcegen", default=False)) - """) - self.makepyfile(__init__="#") - p1 = self.makepyfile(test_one=""" + Option('--someopt', action="store_true", dest="someopt", default=False)) + """, + ) + p1 = testdir.makepyfile(""" def test_1(): import py, conftest - print "test_1: py.test.config.option.forcegen", py.test.config.option.forcegen + print "test_1: py.test.config.option.someopt", py.test.config.option.someopt print "test_1: conftest", conftest - print "test_1: conftest.option.forcegen", conftest.option.forcegen - assert conftest.option.forcegen - """) + print "test_1: conftest.option.someopt", conftest.option.someopt + assert conftest.option.someopt + """, __init__="#") print p1 - config = self.parseconfig('-n1', p1, '--forcegen') + config = py.test.config._reparse(['-n1', p1, '--someopt']) dsession = DSession(config) - readevent = eventreader(dsession) + eq = EventQueue(config.bus) dsession.main() - ev = readevent(event.ItemTestReport) + ev, = eq.geteventargs("itemtestreport") if not ev.passed: - print ev.outcome.longrepr + print ev assert 0 - def test_dist_some_tests(self): - self.makepyfile(conftest="dist_hosts=['localhost']\n") - p1 = self.makepyfile(test_one=""" + def test_dist_some_tests(self, testdir): + testdir.makepyfile(conftest="dist_hosts=['localhost']\n") + p1 = testdir.makepyfile(test_one=""" def test_1(): pass def test_x(): @@ -79,22 +53,22 @@ def test_fail(): assert 0 """) - config = self.parseconfig('-d', p1) + config = testdir.parseconfig('-d', p1) dsession = DSession(config) - readevent = eventreader(dsession) + eq = EventQueue(config.bus) dsession.main([config.getfsnode(p1)]) - ev = readevent(event.ItemTestReport) + ev, = eq.geteventargs("itemtestreport") assert ev.passed - ev = readevent(event.ItemTestReport) + ev, = eq.geteventargs("itemtestreport") assert ev.skipped - ev = readevent(event.ItemTestReport) + ev, = eq.geteventargs("itemtestreport") assert ev.failed # see that the host is really down - ev = readevent(event.HostDown) + ev, = eq.geteventargs("hostdown") assert ev.host.hostname == "localhost" - ev = readevent(event.TestrunFinish) + ev, = eq.geteventargs("testrunfinish") - def test_distribution_rsync_roots_example(self): + def test_distribution_rsync_roots_example(self, testdir): py.test.skip("testing for root rsync needs rework") destdir = py.test.ensuretemp("example_dist_destdir") subdir = "sub_example_dist" @@ -124,28 +98,26 @@ assert config.topdir == tmpdir assert not tmpdir.join("__init__.py").check() dist = DSession(config) - sorter = suptest.events_from_session(dist) - testevents = sorter.get(event.ItemTestReport) + sorter = testdir.inline_runsession(dist) + testevents = sorter.getnamed('itemtestreport') assert len([x for x in testevents if x.passed]) == 2 assert len([x for x in testevents if x.failed]) == 3 assert len([x for x in testevents if x.skipped]) == 0 - def test_nice_level(self): + def test_nice_level(self, testdir): """ Tests if nice level behaviour is ok """ if not hasattr(os, 'nice'): py.test.skip("no os.nice() available") - self.makepyfile(conftest=""" + testdir.makepyfile(conftest=""" dist_hosts=['localhost'] dist_nicelevel = 10 """) - p1 = self.makepyfile(test_nice=""" + p1 = testdir.makepyfile(""" def test_nice(): import os assert os.nice(0) == 10 """) - config = self.parseconfig('-d', p1) - session = config.initsession() - events = suptest.events_from_session(session) - ev = events.getreport('test_nice') + evrec = testdir.inline_run('-d', p1) + ev = evrec.getreport('test_nice') assert ev.passed Modified: py/trunk/py/test/dsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/dsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/dsession/testing/test_hostmanage.py Fri Feb 27 11:18:27 2009 @@ -3,23 +3,15 @@ """ import py -from py.__.test.testing import suptest from py.__.test.dsession.hostmanage import HostRSync, Host, HostManager, gethosts from py.__.test.dsession.hostmanage import sethomedir, gethomedir, getpath_relto_home from py.__.test import event -class TmpWithSourceDest(suptest.FileCreation): - def setup_method(self, method): - super(TmpWithSourceDest, self).setup_method(method) - self.source = self.tmpdir.mkdir("source") - self.dest = self.tmpdir.mkdir("dest") - -class TestHost(suptest.FileCreation): - def _gethostinfo(self, relpath=""): - exampledir = self.tmpdir.join("gethostinfo") +class TestHost: + def _gethostinfo(self, testdir, relpath=""): + exampledir = testdir.mkdir("gethostinfo") if relpath: exampledir = exampledir.join(relpath) - assert not exampledir.check() hostinfo = Host("localhost:%s" % exampledir) return hostinfo @@ -62,8 +54,8 @@ py.test.raises((py.process.cmdexec.Error, IOError, EOFError), host.initgateway) - def test_remote_has_homedir_as_currentdir(self): - host = self._gethostinfo() + def test_remote_has_homedir_as_currentdir(self, testdir): + host = self._gethostinfo(testdir) old = py.path.local.get_temproot().chdir() try: host.initgateway() @@ -104,10 +96,10 @@ assert l[0] == host.python def test_initgateway_ssh_and_remotepath(self): - from py.__.conftest import option - if not option.sshtarget: + hostspec = py.test.config.option.sshhost + if not hostspec: py.test.skip("no known ssh target, use -S to set one") - host = Host("%s" % (option.sshtarget, )) + host = Host("%s" % (hostspec)) # this test should be careful to not write/rsync anything # as the remotepath is the default location # and may be used in the real world @@ -125,18 +117,23 @@ res = channel.receive() assert res == host.gw_remotepath -class TestSyncing(TmpWithSourceDest): - def _gethostinfo(self): - hostinfo = Host("localhost:%s" % self.dest) +def pytest_pyfuncarg_source(pyfuncitem): + return py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir("source") +def pytest_pyfuncarg_dest(pyfuncitem): + return py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir("dest") + +class TestSyncing: + def _gethostinfo(self, dest): + hostinfo = Host("localhost:%s" % dest) return hostinfo - def test_hrsync_filter(self): - self.source.ensure("dir", "file.txt") - self.source.ensure(".svn", "entries") - self.source.ensure(".somedotfile", "moreentries") - self.source.ensure("somedir", "editfile~") - syncer = HostRSync(self.source) - l = list(self.source.visit(rec=syncer.filter, + def test_hrsync_filter(self, source, dest): + source.ensure("dir", "file.txt") + source.ensure(".svn", "entries") + source.ensure(".somedotfile", "moreentries") + source.ensure("somedir", "editfile~") + syncer = HostRSync(source) + l = list(source.visit(rec=syncer.filter, fil=syncer.filter)) assert len(l) == 3 basenames = [x.basename for x in l] @@ -144,10 +141,10 @@ assert 'file.txt' in basenames assert 'somedir' in basenames - def test_hrsync_localhost_inplace(self): + def test_hrsync_localhost_inplace(self, source, dest): h1 = Host("localhost") events = [] - rsync = HostRSync(self.source) + rsync = HostRSync(source) h1.initgateway() rsync.add_target_host(h1, notify=events.append) assert events @@ -161,24 +158,24 @@ if isinstance(x, event.HostRSyncRootReady)] assert len(l) == 1 ev = l[0] - assert ev.root == self.source + assert ev.root == source assert ev.host == h1 - def test_hrsync_one_host(self): - h1 = self._gethostinfo() + def test_hrsync_one_host(self, source, dest): + h1 = self._gethostinfo(dest) finished = [] - rsync = HostRSync(self.source) + rsync = HostRSync(source) h1.initgateway() rsync.add_target_host(h1) - self.source.join("hello.py").write("world") + source.join("hello.py").write("world") rsync.send() - assert self.dest.join("hello.py").check() + assert dest.join("hello.py").check() - def test_hrsync_same_host_twice(self): - h1 = self._gethostinfo() - h2 = self._gethostinfo() + def test_hrsync_same_host_twice(self, source, dest): + h1 = self._gethostinfo(dest) + h2 = self._gethostinfo(dest) finished = [] - rsync = HostRSync(self.source) + rsync = HostRSync(source) l = [] h1.initgateway() h2.initgateway() @@ -187,87 +184,87 @@ res2 = rsync.add_target_host(h2) assert not res2 -class TestHostManager(TmpWithSourceDest): - def gethostmanager(self, dist_hosts, dist_rsync_roots=None): +class TestHostManager: + def gethostmanager(self, source, dist_hosts, dist_rsync_roots=None): l = ["dist_hosts = %r" % dist_hosts] if dist_rsync_roots: l.append("dist_rsync_roots = %r" % dist_rsync_roots) - self.source.join("conftest.py").write("\n".join(l)) - config = py.test.config._reparse([self.source]) - assert config.topdir == self.source + source.join("conftest.py").write("\n".join(l)) + config = py.test.config._reparse([source]) + assert config.topdir == source session = config.initsession() hm = HostManager(session) assert hm.hosts return hm - def test_hostmanager_custom_hosts(self): - session = py.test.config._reparse([self.source]).initsession() + def test_hostmanager_custom_hosts(self, source, dest): + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, hosts=[1,2,3]) assert hm.hosts == [1,2,3] - def test_hostmanager_init_rsync_topdir(self): - dir2 = self.source.ensure("dir1", "dir2", dir=1) + def test_hostmanager_init_rsync_topdir(self, source, dest): + dir2 = source.ensure("dir1", "dir2", dir=1) dir2.ensure("hello") - hm = self.gethostmanager( - dist_hosts = ["localhost:%s" % self.dest] + hm = self.gethostmanager(source, + dist_hosts = ["localhost:%s" % dest] ) - assert hm.session.config.topdir == self.source + assert hm.session.config.topdir == source hm.init_rsync() - dest = self.dest.join(self.source.basename) + dest = dest.join(source.basename) assert dest.join("dir1").check() assert dest.join("dir1", "dir2").check() assert dest.join("dir1", "dir2", 'hello').check() - def test_hostmanager_init_rsync_topdir_explicit(self): - dir2 = self.source.ensure("dir1", "dir2", dir=1) + def test_hostmanager_init_rsync_topdir_explicit(self, source, dest): + dir2 = source.ensure("dir1", "dir2", dir=1) dir2.ensure("hello") - hm = self.gethostmanager( - dist_hosts = ["localhost:%s" % self.dest], - dist_rsync_roots = [str(self.source)] + hm = self.gethostmanager(source, + dist_hosts = ["localhost:%s" % dest], + dist_rsync_roots = [str(source)] ) - assert hm.session.config.topdir == self.source + assert hm.session.config.topdir == source hm.init_rsync() - dest = self.dest.join(self.source.basename) + dest = dest.join(source.basename) assert dest.join("dir1").check() assert dest.join("dir1", "dir2").check() assert dest.join("dir1", "dir2", 'hello').check() - def test_hostmanager_init_rsync_roots(self): - dir2 = self.source.ensure("dir1", "dir2", dir=1) - self.source.ensure("dir1", "somefile", dir=1) + def test_hostmanager_init_rsync_roots(self, source, dest): + dir2 = source.ensure("dir1", "dir2", dir=1) + source.ensure("dir1", "somefile", dir=1) dir2.ensure("hello") - self.source.ensure("bogusdir", "file") - self.source.join("conftest.py").write(py.code.Source(""" + source.ensure("bogusdir", "file") + source.join("conftest.py").write(py.code.Source(""" dist_rsync_roots = ['dir1/dir2'] """)) - session = py.test.config._reparse([self.source]).initsession() + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, - hosts=[Host("localhost:" + str(self.dest))]) + hosts=[Host("localhost:" + str(dest))]) hm.init_rsync() - assert self.dest.join("dir2").check() - assert not self.dest.join("dir1").check() - assert not self.dest.join("bogus").check() - - def test_hostmanager_rsync_ignore(self): - dir2 = self.source.ensure("dir1", "dir2", dir=1) - dir5 = self.source.ensure("dir5", "dir6", "bogus") - dirf = self.source.ensure("dir5", "file") + assert dest.join("dir2").check() + assert not dest.join("dir1").check() + assert not dest.join("bogus").check() + + def test_hostmanager_rsync_ignore(self, source, dest): + dir2 = source.ensure("dir1", "dir2", dir=1) + dir5 = source.ensure("dir5", "dir6", "bogus") + dirf = source.ensure("dir5", "file") dir2.ensure("hello") - self.source.join("conftest.py").write(py.code.Source(""" + source.join("conftest.py").write(py.code.Source(""" dist_rsync_ignore = ['dir1/dir2', 'dir5/dir6'] """)) - session = py.test.config._reparse([self.source]).initsession() + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, - hosts=[Host("localhost:" + str(self.dest))]) + hosts=[Host("localhost:" + str(dest))]) hm.init_rsync() - assert self.dest.join("dir1").check() - assert not self.dest.join("dir1", "dir2").check() - assert self.dest.join("dir5","file").check() - assert not self.dest.join("dir6").check() + assert dest.join("dir1").check() + assert not dest.join("dir1", "dir2").check() + assert dest.join("dir5","file").check() + assert not dest.join("dir6").check() - def test_hostmanage_optimise_localhost(self): + def test_hostmanage_optimise_localhost(self, source, dest): hosts = [Host("localhost") for i in range(3)] - session = py.test.config._reparse([self.source]).initsession() + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, hosts=hosts) hm.init_rsync() for host in hosts: @@ -275,28 +272,31 @@ assert host.gw_remotepath is None assert not host.relpath - def test_hostmanage_setup_hosts(self): + def test_hostmanage_setup_hosts(self, source): hosts = [Host("localhost") for i in range(3)] - session = py.test.config._reparse([self.source]).initsession() + session = py.test.config._reparse([source]).initsession() hm = HostManager(session, hosts=hosts) queue = py.std.Queue.Queue() - hm.setup_hosts(notify=queue.put) + hm.setup_hosts(putevent=queue.put) for host in hm.hosts: - ev = queue.get(timeout=2.0) - assert isinstance(ev, event.HostUp) + eventcall = queue.get(timeout=2.0) + name, args, kwargs = eventcall + assert name == "hostup" for host in hm.hosts: host.node.shutdown() for host in hm.hosts: - ev = queue.get(timeout=2.0) - assert isinstance(ev, event.HostDown) + eventcall = queue.get(timeout=2.0) + name, args, kwargs = eventcall + print name, args, kwargs + assert name == "hostdown" def XXXtest_ssh_rsync_samehost_twice(self): #XXX we have no easy way to have a temp directory remotely! option = py.test.config.option - if option.sshtarget is None: + if option.sshhost is None: py.test.skip("no known ssh target, use -S to set one") - host1 = Host("%s" % (option.sshtarget, )) - host2 = Host("%s" % (option.sshtarget, )) + host1 = Host("%s" % (option.sshhost, )) + host2 = Host("%s" % (option.sshhost, )) hm = HostManager(config, hosts=[host1, host2]) events = [] hm.init_rsync(events.append) Modified: py/trunk/py/test/dsession/testing/test_masterslave.py ============================================================================== --- py/trunk/py/test/dsession/testing/test_masterslave.py (original) +++ py/trunk/py/test/dsession/testing/test_masterslave.py Fri Feb 27 11:18:27 2009 @@ -2,99 +2,116 @@ import py from py.__.test.dsession.masterslave import MasterNode from py.__.test.dsession.hostmanage import Host -from py.__.test import event -from py.__.test.testing import suptest -class TestMasterSlaveConnection(suptest.InlineCollection): - def getevent(self, eventtype=event.ItemTestReport, timeout=2.0): +class EventQueue: + def __init__(self, bus, queue=None): + if queue is None: + queue = py.std.Queue.Queue() + self.queue = queue + bus.register(self) + + def pyevent(self, eventname, *args, **kwargs): + self.queue.put((eventname, args, kwargs)) + + def geteventargs(self, eventname, timeout=2.0): events = [] while 1: try: - ev = self.queue.get(timeout=timeout) + eventcall = self.queue.get(timeout=timeout) except py.std.Queue.Empty: - print "node channel", self.node.channel - print "remoteerror", self.node.channel._getremoteerror() + #print "node channel", self.node.channel + #print "remoteerror", self.node.channel._getremoteerror() print "seen events", events - raise IOError("did not see %r events" % (eventtype)) + raise IOError("did not see %r events" % (eventname)) else: - if isinstance(ev, eventtype): - return ev - events.append(ev) - - def setup_method(self, method): - super(TestMasterSlaveConnection, self).setup_method(method) - self.makepyfile(__init__="") - self.config = self.parseconfig(self.tmpdir) + name, args, kwargs = eventcall + assert isinstance(name, str) + if name == eventname: + return args + events.append(name) + +class MySetup: + def __init__(self, pyfuncitem): + self.pyfuncitem = pyfuncitem + + def geteventargs(self, eventname, timeout=2.0): + eq = EventQueue(self.config.bus, self.queue) + return eq.geteventargs(eventname, timeout=timeout) + + def makenode(self, config=None): + if config is None: + config = py.test.config._reparse([]) + self.config = config self.queue = py.std.Queue.Queue() self.host = Host("localhost") self.host.initgateway() - self.node = MasterNode(self.host, self.config, self.queue.put) + self.node = MasterNode(self.host, self.config, putevent=self.queue.put) assert not self.node.channel.isclosed() + return self.node - def getitem(self, source): - kw = {"test_" + self.tmpdir.basename: py.code.Source(source).strip()} - path = self.makepyfile(**kw) - fscol = self.config.getfsnode(path) - return fscol.collect_by_name("test_func") - - def getitems(self, source): - kw = {"test_" + self.tmpdir.basename: py.code.Source(source).strip()} - path = self.makepyfile(**kw) - fscol = self.config.getfsnode(path) - return fscol.collect() - - def teardown_method(self, method): - print "at teardown:", self.node.channel - #if not self.node.channel.isclosed(): - # self.node.shutdown() - self.host.gw.exit() - - def test_crash_invalid_item(self): - self.node.send(123) # invalid item - ev = self.getevent(event.HostDown) - assert ev.host == self.host + def finalize(self): + if hasattr(self, 'host'): + print "exiting:", self.host.gw + self.host.gw.exit() + +def pytest_pyfuncarg_mysetup(pyfuncitem): + mysetup = MySetup(pyfuncitem) + pyfuncitem.addfinalizer(mysetup.finalize) + return mysetup + +class TestMasterSlaveConnection: + + def test_crash_invalid_item(self, mysetup): + node = mysetup.makenode() + node.send(123) # invalid item + ev, = mysetup.geteventargs("hostdown") + assert ev.host == mysetup.host assert str(ev.error).find("AttributeError") != -1 - def test_crash_killed(self): + def test_crash_killed(self, testdir, mysetup): if not hasattr(py.std.os, 'kill'): py.test.skip("no os.kill") - item = self.getitem(""" + item = testdir.getitem(""" def test_func(): import os os.kill(os.getpid(), 15) """) - self.node.send(item) - ev = self.getevent(event.HostDown) - assert ev.host == self.host + node = mysetup.makenode(item._config) + node.send(item) + ev, = mysetup.geteventargs("hostdown") + assert ev.host == mysetup.host assert str(ev.error).find("TERMINATED") != -1 - def test_node_down(self): - self.node.shutdown() - ev = self.getevent(event.HostDown) - assert ev.host == self.host + def test_node_down(self, mysetup): + node = mysetup.makenode() + node.shutdown() + ev, = mysetup.geteventargs("hostdown") + assert ev.host == mysetup.host assert not ev.error - self.node.callback(self.node.ENDMARK) + node.callback(node.ENDMARK) excinfo = py.test.raises(IOError, - "self.getevent(event.HostDown, timeout=0.01)") + "mysetup.geteventargs('hostdown', timeout=0.01)") - def test_send_on_closed_channel(self): - item = self.getitem("def test_func(): pass") - self.node.channel.close() - py.test.raises(IOError, "self.node.send(item)") - #ev = self.getevent(event.InternalException) + def test_send_on_closed_channel(self, testdir, mysetup): + item = testdir.getitem("def test_func(): pass") + node = mysetup.makenode(item._config) + node.channel.close() + py.test.raises(IOError, "node.send(item)") + #ev = self.geteventargs(event.InternalException) #assert ev.excinfo.errisinstance(IOError) - def test_send_one(self): - item = self.getitem("def test_func(): pass") - self.node.send(item) - ev = self.getevent() + def test_send_one(self, testdir, mysetup): + item = testdir.getitem("def test_func(): pass") + node = mysetup.makenode(item._config) + node.send(item) + ev, = mysetup.geteventargs("itemtestreport") assert ev.passed assert ev.colitem == item #assert event.item == item #assert event.item is not item - def test_send_some(self): - items = self.getitems(""" + def test_send_some(self, testdir, mysetup): + items = testdir.getitems(""" def test_pass(): pass def test_fail(): @@ -103,13 +120,14 @@ import py py.test.skip("x") """) + node = mysetup.makenode(items[0]._config) for item in items: - self.node.send(item) + node.send(item) for outcome in "passed failed skipped".split(): - ev = self.getevent() + ev, = mysetup.geteventargs("itemtestreport") assert getattr(ev, outcome) - self.node.sendlist(items) + node.sendlist(items) for outcome in "passed failed skipped".split(): - ev = self.getevent() + ev, = mysetup.geteventargs("itemtestreport") assert getattr(ev, outcome) Modified: py/trunk/py/test/event.py ============================================================================== --- py/trunk/py/test/event.py (original) +++ py/trunk/py/test/event.py Fri Feb 27 11:18:27 2009 @@ -6,24 +6,6 @@ import time from py.__.test.outcome import Skipped -class EventBus(object): - """ General Event Bus for distributing events. """ - def __init__(self): - self._subscribers = [] - - def subscribe(self, callback): - """ subscribe given callback to bus events. """ - self._subscribers.append(callback) - - def unsubscribe(self, callback): - """ unsubscribe given callback from bus events. """ - self._subscribers.remove(callback) - - def notify(self, event): - for subscriber in self._subscribers: - subscriber(event) - - class BaseEvent(object): def __repr__(self): l = ["%s=%s" %(key, value) @@ -75,33 +57,73 @@ class BaseReport(BaseEvent): - failed = passed = skipped = None - def __init__(self, colitem, **kwargs): - self.colitem = colitem - assert len(kwargs) == 1, kwargs - name, value = kwargs.items()[0] - setattr(self, name, True) - self.outcome = value - def toterminal(self, out): - longrepr = self.outcome.longrepr + longrepr = self.longrepr if hasattr(longrepr, 'toterminal'): longrepr.toterminal(out) else: out.line(str(longrepr)) - + class ItemTestReport(BaseReport): """ Test Execution Report. """ + failed = passed = skipped = False + def __init__(self, colitem, excinfo=None, when=None, outerr=None): + self.colitem = colitem + self.keywords = colitem and colitem.readkeywords() + if not excinfo: + self.passed = True + self.shortrepr = "." + else: + self.when = when + if not isinstance(excinfo, py.code.ExceptionInfo): + self.failed = True + shortrepr = "?" + longrepr = excinfo + elif excinfo.errisinstance(Skipped): + self.skipped = True + shortrepr = "s" + longrepr = self.colitem._repr_failure_py(excinfo, outerr) + else: + self.failed = True + shortrepr = self.colitem.shortfailurerepr + if self.when == "execute": + longrepr = self.colitem.repr_failure(excinfo, outerr) + else: # exception in setup or teardown + longrepr = self.colitem._repr_failure_py(excinfo, outerr) + shortrepr = shortrepr.lower() + self.shortrepr = shortrepr + self.longrepr = longrepr + class CollectionStart(BaseEvent): def __init__(self, collector): self.collector = collector class CollectionReport(BaseReport): """ Collection Report. """ - def __init__(self, colitem, result, **kwargs): - super(CollectionReport, self).__init__(colitem, **kwargs) - self.result = result + skipped = failed = passed = False + + def __init__(self, colitem, result, excinfo=None, when=None, outerr=None): + self.colitem = colitem + if not excinfo: + self.passed = True + self.result = result + else: + self.when = when + self.outerr = outerr + self.longrepr = self.colitem._repr_failure_py(excinfo, outerr) + if excinfo.errisinstance(Skipped): + self.skipped = True + self.reason = str(excinfo.value) + else: + self.failed = True + + def toterminal(self, out): + longrepr = self.longrepr + if hasattr(longrepr, 'toterminal'): + longrepr.toterminal(out) + else: + out.line(str(longrepr)) class LooponfailingInfo(BaseEvent): def __init__(self, failreports, rootdirs): @@ -151,3 +173,12 @@ self.host = host self.root = root + +# make all eventclasses available on BaseEvent so that +# consumers of events can easily filter by +# 'isinstance(event, event.Name)' checks + +for name, cls in vars().items(): + if hasattr(cls, '__bases__') and issubclass(cls, BaseEvent): + setattr(BaseEvent, name, cls) +# Modified: py/trunk/py/test/looponfail/remote.py ============================================================================== --- py/trunk/py/test/looponfail/remote.py (original) +++ py/trunk/py/test/looponfail/remote.py Fri Feb 27 11:18:27 2009 @@ -13,7 +13,6 @@ from py.__.test.session import Session from py.__.test.outcome import Failed, Passed, Skipped from py.__.test.dsession.mypickle import PickleChannel -from py.__.test.report.terminal import TerminalReporter from py.__.test import event from py.__.test.looponfail import util @@ -83,8 +82,8 @@ from py.__.test.looponfail.remote import slave_runsession from py.__.test.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) - width, hasmarkup = channel.receive() - slave_runsession(channel, config, width, hasmarkup) + fullwidth, hasmarkup = channel.receive() + slave_runsession(channel, config, fullwidth, hasmarkup) """, stdout=out, stderr=out) channel = PickleChannel(channel) masterslave.send_and_receive_pickled_config( @@ -117,7 +116,7 @@ finally: self.ensure_teardown() -def slave_runsession(channel, config, width, hasmarkup): +def slave_runsession(channel, config, fullwidth, hasmarkup): """ we run this on the other side. """ if config.option.debug: def DEBUG(*args): @@ -134,8 +133,10 @@ DEBUG("SLAVE: initsession()") session = config.initsession() - session.reporter._tw.hasmarkup = hasmarkup - session.reporter._tw.fullwidth = width + # XXX configure the reporter object's terminal writer more directly + # XXX and write a test for this remote-terminal setting logic + config.pytest_terminal_hasmarkup = hasmarkup + config.pytest_terminal_fullwidth = fullwidth if trails: colitems = [] for trail in trails: @@ -148,19 +149,18 @@ else: colitems = None session.shouldclose = channel.isclosed - #def sendevent(ev): - # channel.send(ev) - #session.bus.subscribe(sendevent) - failreports = [] - def recordfailures(ev): - if isinstance(ev, event.BaseReport): + + class Failures(list): + def pyevent_itemtestreport(self, ev): if ev.failed: - failreports.append(ev) - session.bus.subscribe(recordfailures) + self.append(ev) + pyevent_collectionreport = pyevent_itemtestreport + + failreports = Failures() + session.bus.register(failreports) DEBUG("SLAVE: starting session.main()") session.main(colitems) - session.bus.unsubscribe(recordfailures) - ev = event.LooponfailingInfo(failreports, [config.topdir]) - session.bus.notify(ev) + ev = event.LooponfailingInfo(list(failreports), [config.topdir]) + session.bus.notify("looponfailinginfo", ev) channel.send([x.colitem._totrail() for x in failreports if x.failed]) Modified: py/trunk/py/test/looponfail/testing/test_remote.py ============================================================================== --- py/trunk/py/test/looponfail/testing/test_remote.py (original) +++ py/trunk/py/test/looponfail/testing/test_remote.py Fri Feb 27 11:18:27 2009 @@ -1,33 +1,16 @@ import py -from py.__.test.testing import suptest from py.__.test.looponfail.remote import LooponfailingSession, LoopState, RemoteControl -from py.__.test import event -def getevent(l, evtype): - result = getevents(l, evtype) - if not result: - raise ValueError("event %r not found in %r" %(evtype, l)) - return result[0] - -def getevents(l, evtype): - result = [] - for ev in l: - if isinstance(ev, evtype): - result.append(ev) - return result - - -class TestRemoteControl(suptest.InlineCollection): - def test_nofailures(self): - item = self.getitem("def test_func(): pass\n") - events = [] +class TestRemoteControl: + def test_nofailures(self, testdir): + item = testdir.getitem("def test_func(): pass\n") control = RemoteControl(item._config) control.setup() failures = control.runsession() assert not failures - def test_failures(self): - item = self.getitem("def test_func(): assert 0\n") + def test_failures_somewhere(self, testdir): + item = testdir.getitem("def test_func(): assert 0\n") control = RemoteControl(item._config) control.setup() failures = control.runsession() @@ -38,8 +21,8 @@ failures = control.runsession(failures) assert not failures - def test_failure_change(self): - modcol = self.getitem(""" + def test_failure_change(self, testdir): + modcol = testdir.getitem(""" def test_func(): assert 0 """) @@ -62,9 +45,9 @@ assert failures assert str(failures).find("test_new") != -1 -class TestLooponFailing(suptest.InlineCollection): - def test_looponfailing_from_fail_to_ok(self): - modcol = self.getmodulecol(""" +class TestLooponFailing: + def test_looponfailing_from_fail_to_ok(self, testdir): + modcol = testdir.getmodulecol(""" def test_one(): x = 0 assert x == 1 @@ -88,8 +71,8 @@ session.loop_once(loopstate) assert not loopstate.colitems - def test_looponfailing_from_one_to_two_tests(self): - modcol = self.getmodulecol(""" + def test_looponfailing_from_one_to_two_tests(self, testdir): + modcol = testdir.getmodulecol(""" def test_one(): assert 0 """) @@ -113,8 +96,8 @@ session.loop_once(loopstate) assert len(loopstate.colitems) == 1 - def test_looponfailing_removed_test(self): - modcol = self.getmodulecol(""" + def test_looponfailing_removed_test(self, testdir): + modcol = testdir.getmodulecol(""" def test_one(): assert 0 def test_two(): Modified: py/trunk/py/test/looponfail/testing/test_util.py ============================================================================== --- py/trunk/py/test/looponfail/testing/test_util.py (original) +++ py/trunk/py/test/looponfail/testing/test_util.py Fri Feb 27 11:18:27 2009 @@ -1,9 +1,8 @@ import py -from py.__.test.looponfail.util import StatRecorder, EventRecorder -from py.__.test import event +from py.__.test.looponfail.util import StatRecorder -def test_filechange(): - tmp = py.test.ensuretemp("test_filechange") +def test_filechange(tmpdir): + tmp = tmpdir hello = tmp.ensure("hello.py") sd = StatRecorder([tmp]) changed = sd.check() @@ -35,8 +34,8 @@ changed = sd.check() assert changed -def test_pycremoval(): - tmp = py.test.ensuretemp("test_pycremoval") +def test_pycremoval(tmpdir): + tmp = tmpdir hello = tmp.ensure("hello.py") sd = StatRecorder([tmp]) changed = sd.check() @@ -52,8 +51,8 @@ assert not pycfile.check() -def test_waitonchange(): - tmp = py.test.ensuretemp("test_waitonchange") +def test_waitonchange(tmpdir): + tmp = tmpdir sd = StatRecorder([tmp]) wp = py._thread.WorkerPool(1) @@ -62,29 +61,4 @@ tmp.ensure("newfile.py") reply.get(timeout=0.5) wp.shutdown() - -def test_eventrecorder(): - bus = event.EventBus() - recorder = EventRecorder(bus) - bus.notify(event.NOP()) - assert recorder.events - assert not recorder.getfailures() - rep = event.ItemTestReport(None, failed=True) - bus.notify(rep) - failures = recorder.getfailures() - assert failures == [rep] - recorder.clear() - assert not recorder.events - assert not recorder.getfailures() - recorder.unsubscribe() - bus.notify(rep) - assert not recorder.events - assert not recorder.getfailures() - - - - - - - - + Modified: py/trunk/py/test/looponfail/util.py ============================================================================== --- py/trunk/py/test/looponfail/util.py (original) +++ py/trunk/py/test/looponfail/util.py Fri Feb 27 11:18:27 2009 @@ -52,19 +52,3 @@ self.statcache = newstat return changed - -class EventRecorder(object): - def __init__(self, bus): - self.events = [] - self.bus = bus - self.bus.subscribe(self.events.append) - - def getfailures(self): - return [ev for ev in self.events - if isinstance(ev, event.BaseReport) and \ - ev.failed] - def clear(self): - self.events[:] = [] - - def unsubscribe(self): - self.bus.unsubscribe(self.events.append) Modified: py/trunk/py/test/outcome.py ============================================================================== --- py/trunk/py/test/outcome.py (original) +++ py/trunk/py/test/outcome.py Fri Feb 27 11:18:27 2009 @@ -139,6 +139,13 @@ raise AssertionError("%r did not produce DeprecationWarning" %(func,)) return ret +class keywords: + """ decorator for setting function attributes. """ + def __init__(self, **kw): + self.kw = kw + def __call__(self, func): + func.func_dict.update(self.kw) + return func # exitcodes for the command line EXIT_OK = 0 Copied: py/trunk/py/test/plugin/pytest_default.py (from r62210, py/branch/pytestplugin/py/test/plugin/pytest_default.py) ============================================================================== --- py/branch/pytestplugin/py/test/plugin/pytest_default.py (original) +++ py/trunk/py/test/plugin/pytest_default.py Fri Feb 27 11:18:27 2009 @@ -20,9 +20,11 @@ action="store_true", dest="nocapture", default=False, help="disable catching of sys.stdout/stderr output."), group._addoption('-k', - action="store", dest="keyword", default='', - help="only run test items matching the given " - "keyword expression."), + action="store", dest="keyword", default='', + help="only run test items matching the given " + "space separated keywords. precede a keyword with '-' to negate. " + "Terminate the expression with ':' to treat a match as a signal " + "to run all subsequent tests. ") group._addoption('-l', '--showlocals', action="store_true", dest="showlocals", default=False, help="show locals in tracebacks (disabled by default)."), Modified: py/trunk/py/test/pycollect.py ============================================================================== --- py/trunk/py/test/pycollect.py (original) +++ py/trunk/py/test/pycollect.py Fri Feb 27 11:18:27 2009 @@ -36,7 +36,7 @@ def _getobj(self): return getattr(self.parent.obj, self.name) - def getmodpath(self, stopatmodule=True): + def getmodpath(self, stopatmodule=True, includemodule=False): """ return python path relative to the containing module. """ chain = self.listchain() chain.reverse() @@ -46,10 +46,12 @@ continue name = node.name if isinstance(node, Module): - if stopatmodule: - break assert name.endswith(".py") name = name[:-3] + if stopatmodule: + if includemodule: + parts.append(name) + break parts.append(name) parts.reverse() s = ".".join(parts) @@ -136,14 +138,18 @@ warnoldcollect() return self.join(name) - def makeitem(self, name, obj, usefilters=True): - if (not usefilters or self.classnamefilter(name)) and \ + def makeitem(self, name, obj): + res = self._config.pytestplugins.call_firstresult( + "pytest_pymodule_makeitem", modcol=self, name=name, obj=obj) + if res: + return res + if (self.classnamefilter(name)) and \ py.std.inspect.isclass(obj): res = self._deprecated_join(name) if res is not None: return res return self.Class(name, parent=self) - elif (not usefilters or self.funcnamefilter(name)) and callable(obj): + elif self.funcnamefilter(name) and callable(obj): res = self._deprecated_join(name) if res is not None: return res @@ -159,14 +165,23 @@ return super(Module, self).collect() def _getobj(self): - return self._memoizedcall('_obj', self.fspath.pyimport) + return self._memoizedcall('_obj', self._importtestmodule) + + def _importtestmodule(self): + # we assume we are only called once per module + mod = self.fspath.pyimport() + #print "imported test module", mod + self._config.pytestplugins.consider_module(mod) + return mod def setup(self): if not self._config.option.nomagic: #print "*" * 20, "INVOKE assertion", self py.magic.invoke(assertion=1) - if hasattr(self.obj, 'setup_module'): - self.obj.setup_module(self.obj) + mod = self.obj + self._config.pytestplugins.register(mod) + if hasattr(mod, 'setup_module'): + self.obj.setup_module(mod) def teardown(self): if hasattr(self.obj, 'teardown_module'): @@ -174,6 +189,7 @@ if not self._config.option.nomagic: #print "*" * 20, "revoke assertion", self py.magic.revoke(assertion=1) + self._config.pytestplugins.unregister(self.obj) class Class(PyCollectorMixin, py.test.collect.Collector): @@ -305,14 +321,57 @@ """ def __init__(self, name, parent=None, config=None, args=(), callobj=_dummy): super(Function, self).__init__(name, parent, config=config) + self._finalizers = [] self._args = args if callobj is not _dummy: self._obj = callobj + def addfinalizer(self, func): + self._finalizers.append(func) + + def teardown(self): + finalizers = self._finalizers + while finalizers: + call = finalizers.pop() + call() + super(Function, self).teardown() + + def readkeywords(self): + d = super(Function, self).readkeywords() + d.update(self.obj.func_dict) + return d + def runtest(self): """ execute the given test function. """ if not self._deprecated_testexecution(): - self.obj(*self._args) + kw = self.lookup_allargs() + pytest_pyfunc_call = self._config.pytestplugins.getfirst("pytest_pyfunc_call") + if pytest_pyfunc_call is not None: + if pytest_pyfunc_call(pyfuncitem=self, args=self._args, kwargs=kw): + return + self.obj(*self._args, **kw) + + def lookup_allargs(self): + kwargs = {} + if not self._args: + # standard Python Test function/method case + funcobj = self.obj + startindex = getattr(funcobj, 'im_self', None) and 1 or 0 + for argname in py.std.inspect.getargs(self.obj.func_code)[0][startindex:]: + kwargs[argname] = self.lookup_onearg(argname) + else: + pass # XXX lookup of arguments for yielded/generated tests as well + return kwargs + + def lookup_onearg(self, argname): + value = self._config.pytestplugins.call_firstresult( + "pytest_pyfuncarg_" + argname, pyfuncitem=self) + if value is not None: + return value + else: + metainfo = self.repr_metainfo() + #self._config.bus.notify("pyfuncarg_lookuperror", argname) + raise LookupError("funcargument %r not found for: %s" %(argname,metainfo.verboseline())) def __eq__(self, other): try: @@ -326,50 +385,6 @@ def __ne__(self, other): return not self == other -class DoctestFile(py.test.collect.File): - - def collect(self): - return [DoctestFileContent(self.fspath.basename, parent=self)] - -from py.__.code.excinfo import Repr, ReprFileLocation - -class ReprFailDoctest(Repr): - def __init__(self, reprlocation, lines): - self.reprlocation = reprlocation - self.lines = lines - def toterminal(self, tw): - for line in self.lines: - tw.line(line) - self.reprlocation.toterminal(tw) - -class DoctestFileContent(py.test.collect.Item): - def repr_failure(self, excinfo, outerr): - if excinfo.errisinstance(py.compat.doctest.DocTestFailure): - doctestfailure = excinfo.value - example = doctestfailure.example - test = doctestfailure.test - filename = test.filename - lineno = example.lineno + 1 - message = excinfo.type.__name__ - reprlocation = ReprFileLocation(filename, lineno, message) - checker = py.compat.doctest.OutputChecker() - REPORT_UDIFF = py.compat.doctest.REPORT_UDIFF - filelines = py.path.local(filename).readlines(cr=0) - i = max(0, lineno - 10) - lines = [] - for line in filelines[i:lineno]: - lines.append("%03d %s" % (i+1, line)) - i += 1 - lines += checker.output_difference(example, - doctestfailure.got, REPORT_UDIFF).split("\n") - return ReprFailDoctest(reprlocation, lines) - #elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): - else: - return super(DoctestFileContent, self).repr_failure(excinfo, outerr) - - def runtest(self): - if not self._deprecated_testexecution(): - failed, tot = py.compat.doctest.testfile( - str(self.fspath), module_relative=False, - raise_on_error=True, verbose=0) +# DEPRECATED +#from py.__.test.plugin.pytest_doctest import DoctestFile Deleted: /py/trunk/py/test/resultlog.py ============================================================================== --- /py/trunk/py/test/resultlog.py Fri Feb 27 11:18:27 2009 +++ (empty file) @@ -1,54 +0,0 @@ -import py -from py.__.test import event - -def generic_path(item): - chain = item.listchain() - gpath = [chain[0].name] - fspath = chain[0].fspath - fspart = False - for node in chain[1:]: - newfspath = node.fspath - if newfspath == fspath: - if fspart: - gpath.append(':') - fspart = False - else: - gpath.append('.') - else: - gpath.append('/') - fspart = True - name = node.name - if name[0] in '([': - gpath.pop() - gpath.append(name) - fspath = newfspath - return ''.join(gpath) - -class ResultLog(object): - - def __init__(self, bus, logfile): - bus.subscribe(self.log_event_to_file) - self.logfile = logfile # preferably line buffered - - def write_log_entry(self, shortrepr, name, longrepr): - print >>self.logfile, "%s %s" % (shortrepr, name) - for line in longrepr.splitlines(): - print >>self.logfile, " %s" % line - - def log_outcome(self, ev): - outcome = ev.outcome - gpath = generic_path(ev.colitem) - self.write_log_entry(outcome.shortrepr, gpath, str(outcome.longrepr)) - - def log_event_to_file(self, ev): - if isinstance(ev, event.ItemTestReport): - self.log_outcome(ev) - elif isinstance(ev, event.CollectionReport): - if not ev.passed: - self.log_outcome(ev) - elif isinstance(ev, event.InternalException): - path = ev.repr.reprcrash.path # fishing :( - self.write_log_entry('!', path, str(ev.repr)) - - - Modified: py/trunk/py/test/runner.py ============================================================================== --- py/trunk/py/test/runner.py (original) +++ py/trunk/py/test/runner.py Fri Feb 27 11:18:27 2009 @@ -9,12 +9,14 @@ import py, os, sys from py.__.test import event -from py.__.test.outcome import Skipped, Exit +from py.__.test.outcome import Exit from py.__.test.dsession.mypickle import ImmutablePickler import py.__.test.custompdb class RobustRun(object): - """ a robust setup/execute/teardown protocol. """ + """ a robust setup/execute/teardown protocol used both for test collectors + and test items. + """ def __init__(self, colitem, pdb=None): self.colitem = colitem self.getcapture = colitem._config._getcapture @@ -46,35 +48,18 @@ excinfo = py.code.ExceptionInfo() return self.makereport(res, when, excinfo, outerr) - def getkw(self, when, excinfo, outerr): - if excinfo.errisinstance(Skipped): - outcome = "skipped" - shortrepr = "s" - longrepr = excinfo._getreprcrash() - else: - outcome = "failed" - if when == "execute": - longrepr = self.colitem.repr_failure(excinfo, outerr) - shortrepr = self.colitem.shortfailurerepr - else: - longrepr = self.colitem._repr_failure_py(excinfo, outerr) - shortrepr = self.colitem.shortfailurerepr.lower() - kw = { outcome: OutcomeRepr(when, shortrepr, longrepr)} - return kw - class ItemRunner(RobustRun): def setup(self): self.colitem._setupstate.prepare(self.colitem) def teardown(self): self.colitem._setupstate.teardown_exact(self.colitem) def execute(self): + #self.colitem.config.pytestplugins.pre_execute(self.colitem) self.colitem.runtest() + #self.colitem.config.pytestplugins.post_execute(self.colitem) + def makereport(self, res, when, excinfo, outerr): - if excinfo: - kw = self.getkw(when, excinfo, outerr) - else: - kw = {'passed': OutcomeRepr(when, '.', "")} - testrep = event.ItemTestReport(self.colitem, **kw) + testrep = event.ItemTestReport(self.colitem, excinfo, when, outerr) if self.pdb and testrep.failed: tw = py.io.TerminalWriter() testrep.toterminal(tw) @@ -89,26 +74,13 @@ def execute(self): return self.colitem._memocollect() def makereport(self, res, when, excinfo, outerr): - if excinfo: - kw = self.getkw(when, excinfo, outerr) - else: - kw = {'passed': OutcomeRepr(when, '', "")} - return event.CollectionReport(self.colitem, res, **kw) - + return event.CollectionReport(self.colitem, res, excinfo, when, outerr) + NORESULT = object() # # public entrypoints / objects # -class OutcomeRepr(object): - def __init__(self, when, shortrepr, longrepr): - self.when = when - self.shortrepr = shortrepr - self.longrepr = longrepr - def __str__(self): - return ">f, ev - f.flush() - self.bus.subscribe(eventwrite) - resultlog = self.config.option.resultlog - if resultlog: - f = open(resultlog, 'w', 1) # line buffered - self.resultlog = ResultLog(self.bus, f) def fixoptions(self): """ check, fix and determine conflicting options. """ @@ -56,19 +42,24 @@ """ yield Items from iterating over the given colitems. """ while colitems: next = colitems.pop(0) + if isinstance(next, (tuple, list)): + colitems[:] = list(next) + colitems + continue + assert self.bus is next._config.bus + notify = self.bus.notify if isinstance(next, Item): remaining = self.filteritems([next]) if remaining: - self.bus.notify(event.ItemStart(next)) + notify("itemstart", event.ItemStart(next)) yield next else: assert isinstance(next, Collector) - self.bus.notify(event.CollectionStart(next)) + notify("collectionstart", event.CollectionStart(next)) ev = basic_collect_report(next) if ev.passed: for x in self.genitems(ev.result, keywordexpr): yield x - self.bus.notify(ev) + notify("collectionreport", ev) def filteritems(self, colitems): """ return items to process (some may be deselected)""" @@ -86,7 +77,7 @@ continue remaining.append(colitem) if deselected: - self.bus.notify(event.Deselected(deselected, )) + self.bus.notify("deselected", event.Deselected(deselected, )) if self.config.option.keyword.endswith(":"): self._nomatch = True return remaining @@ -98,23 +89,19 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.bus.notify(event.TestrunStart()) - self._failurelist = [] - self.bus.subscribe(self._processfailures) - - def _processfailures(self, ev): - if isinstance(ev, event.BaseReport) and ev.failed: - self._failurelist.append(ev) - if self.config.option.exitfirst: - self.shouldstop = True + self.bus.notify("testrunstart", event.TestrunStart()) + + def pyevent_itemtestreport(self, rep): + if rep.failed: + self._testsfailed = True + if self.config.option.exitfirst: + self.shouldstop = True + pyevent_collectionreport = pyevent_itemtestreport def sessionfinishes(self, exitstatus=0, excinfo=None): """ teardown any resources after a test run. """ - self.bus.notify(event.TestrunFinish(exitstatus=exitstatus, - excinfo=excinfo)) - self.bus.unsubscribe(self._processfailures) - #self.reporter.deactivate() - return self._failurelist + self.bus.notify("testrunfinish", + event.TestrunFinish(exitstatus=exitstatus, excinfo=excinfo)) def getinitialitems(self, colitems): if colitems is None: @@ -127,7 +114,7 @@ colitems = self.getinitialitems(colitems) self.shouldstop = False self.sessionstarts() - self.bus.notify(makehostup()) + self.bus.notify("hostup", makehostup()) exitstatus = outcome.EXIT_OK captured_excinfo = None try: @@ -136,24 +123,26 @@ break if not self.config.option.collectonly: self.runtest(item) + py.test.collect.Item._setupstate.teardown_all() except KeyboardInterrupt: captured_excinfo = py.code.ExceptionInfo() exitstatus = outcome.EXIT_INTERRUPTED except: - self.bus.notify(event.InternalException()) + captured_excinfo = py.code.ExceptionInfo() + self.bus.notify("internalerror", event.InternalException(captured_excinfo)) exitstatus = outcome.EXIT_INTERNALERROR - if self._failurelist and exitstatus == 0: + if exitstatus == 0 and self._testsfailed: exitstatus = outcome.EXIT_TESTSFAILED self.sessionfinishes(exitstatus=exitstatus, excinfo=captured_excinfo) return exitstatus def runpdb(self, excinfo): - py.__.test.custompdb.post_mortem(excinfo._excinfo[2]) + from py.__.test.custompdb import post_mortem + post_mortem(excinfo._excinfo[2]) def runtest(self, item): runner = item._getrunner() pdb = self.config.option.usepdb and self.runpdb or None testrep = runner(item, pdb=pdb) - self.bus.notify(testrep) - + self.bus.notify("itemtestreport", testrep) Modified: py/trunk/py/test/testing/acceptance_test.py ============================================================================== --- py/trunk/py/test/testing/acceptance_test.py (original) +++ py/trunk/py/test/testing/acceptance_test.py Fri Feb 27 11:18:27 2009 @@ -1,110 +1,73 @@ import py -from suptest import assert_lines_contain_lines, FileCreation pydir = py.path.local(py.__file__).dirpath() pytestpath = pydir.join("bin", "py.test") EXPECTTIMEOUT=10.0 -def setup_module(mod): - mod.modtmpdir = py.test.ensuretemp(mod.__name__) - -class Result: - def __init__(self, ret, outlines, errlines): - self.ret = ret - self.outlines = outlines - self.errlines = errlines - -class AcceptBase(FileCreation): - def popen(self, cmdargs, stdout, stderr, **kw): - if not hasattr(py.std, 'subprocess'): - py.test.skip("no subprocess module") - return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) - - def run(self, *cmdargs): - cmdargs = map(str, cmdargs) - p1 = py.path.local("stdout") - p2 = py.path.local("stderr") - print "running", cmdargs, "curdir=", py.path.local() - popen = self.popen(cmdargs, stdout=p1.open("w"), stderr=p2.open("w")) - ret = popen.wait() - out, err = p1.readlines(cr=0), p2.readlines(cr=0) - if err: - for line in err: - print >>py.std.sys.stderr, line - return Result(ret, out, err) - - def runpybin(self, scriptname, *args): - bindir = py.path.local(py.__file__).dirpath("bin") - if py.std.sys.platform == "win32": - script = bindir.join("win32", scriptname + ".cmd") - else: - script = bindir.join(scriptname) - assert script.check() - return self.run(script, *args) - - def runpytest(self, *args): - return self.runpybin("py.test", *args) - - def setup_method(self, method): - super(AcceptBase, self).setup_method(method) - self.old = self.tmpdir.chdir() - - def teardown_method(self, method): - self.old.chdir() - -class TestPyTest(AcceptBase): - def test_assertion_magic(self): - p = self.makepyfile(test_one=""" +class TestPyTest: + def test_assertion_magic(self, testdir): + p = testdir.makepyfile(""" def test_this(): x = 0 assert x """) - result = self.runpytest(p) - extra = assert_lines_contain_lines(result.outlines, [ + result = testdir.runpytest(p) + extra = result.stdout.fnmatch_lines([ "> assert x", "E assert 0", ]) assert result.ret == 1 - - def test_collectonly_simple(self): - p = self.makepyfile(test_one=""" + def test_collectonly_simple(self, testdir): + p = testdir.makepyfile(""" def test_func1(): pass class TestClass: def test_method(self): pass """) - result = self.runpytest("--collectonly", p) - err = "".join(result.errlines) - assert err.strip().startswith("inserting into sys.path") + result = testdir.runpytest("--collectonly", p) + stderr = result.stderr.str().strip() + assert stderr.startswith("inserting into sys.path") assert result.ret == 0 - extra = assert_lines_contain_lines(result.outlines, py.code.Source(""" - + extra = result.stdout.fnmatch_lines(py.code.Source(""" + """).strip()) - def test_nested_import_error(self): - p = self.makepyfile( - test_one=""" + def test_collectonly_error(self, testdir): + p = testdir.makepyfile("import Errlkjqweqwe") + result = testdir.runpytest("--collectonly", p) + stderr = result.stderr.str().strip() + assert stderr.startswith("inserting into sys.path") + assert result.ret == 1 + extra = result.stdout.fnmatch_lines(py.code.Source(""" + + *ImportError* + !!!*failures*!!! + *test_collectonly_error.py:1* + """).strip()) + + + def test_nested_import_error(self, testdir): + p = testdir.makepyfile(""" import import_fails def test_this(): assert import_fails.a == 1 - """, - import_fails="import does_not_work" - ) - result = self.runpytest(p) - extra = assert_lines_contain_lines(result.outlines, [ + """) + testdir.makepyfile(import_fails="import does_not_work") + result = testdir.runpytest(p) + extra = result.stdout.fnmatch_lines([ "> import import_fails", "E ImportError: No module named does_not_work", ]) assert result.ret == 1 - def test_skipped_reasons(self): - p1 = self.makepyfile( + def test_skipped_reasons(self, testdir): + testdir.makepyfile( test_one=""" from conftest import doskip def setup_function(func): @@ -125,34 +88,34 @@ py.test.skip('test') """ ) - result = self.runpytest() - extra = assert_lines_contain_lines(result.outlines, [ + result = testdir.runpytest() + extra = result.stdout.fnmatch_lines([ "*test_one.py ss", - "*test_two.py - Skipped*", + "*test_two.py S", "___* skipped test summary *_", "*conftest.py:3: *3* Skipped: 'test'", ]) assert result.ret == 0 - def test_deselected(self): - p1 = self.makepyfile(test_one=""" + def test_deselected(self, testdir): + testpath = testdir.makepyfile(""" def test_one(): pass def test_two(): pass def test_three(): pass - """, + """ ) - result = self.runpytest("-k", "test_two:") - extra = assert_lines_contain_lines(result.outlines, [ - "*test_one.py ..", + result = testdir.runpytest("-k", "test_two:", testpath) + extra = result.stdout.fnmatch_lines([ + "*test_deselected.py ..", "=* 1 test*deselected by 'test_two:'*=", ]) assert result.ret == 0 - def test_no_skip_summary_if_failure(self): - p1 = self.makepyfile(test_one=""" + def test_no_skip_summary_if_failure(self, testdir): + testdir.makepyfile(""" import py def test_ok(): pass @@ -161,12 +124,12 @@ def test_skip(): py.test.skip("dontshow") """) - result = self.runpytest() - assert str(result.outlines).find("skip test summary") == -1 + result = testdir.runpytest() + assert result.stdout.str().find("skip test summary") == -1 assert result.ret == 1 - def test_passes(self): - p1 = self.makepyfile(test_one=""" + def test_passes(self, testdir): + p1 = testdir.makepyfile(""" def test_passes(): pass class TestClass: @@ -175,33 +138,32 @@ """) old = p1.dirpath().chdir() try: - result = self.runpytest() + result = testdir.runpytest() finally: old.chdir() - extra = assert_lines_contain_lines(result.outlines, [ - "test_one.py ..", - "* failures: no failures*", + extra = result.stdout.fnmatch_lines([ + "test_passes.py ..", + "* 2 pass*", ]) assert result.ret == 0 - def test_header_trailer_info(self): - p1 = self.makepyfile(test_one=""" + def test_header_trailer_info(self, testdir): + p1 = testdir.makepyfile(""" def test_passes(): pass """) - result = self.runpytest() + result = testdir.runpytest() verinfo = ".".join(map(str, py.std.sys.version_info[:3])) - extra = assert_lines_contain_lines(result.outlines, [ + extra = result.stdout.fnmatch_lines([ "*===== test session starts ====*", "*localhost* %s %s - Python %s*" %( py.std.sys.platform, py.std.sys.executable, verinfo), - "*test_one.py .", - "=* 1/1 passed + 0 skips in *.[0-9][0-9] seconds *=", - "=* no failures :)*=", + "*test_header_trailer_info.py .", + "=* 1 passed in *.[0-9][0-9] seconds *=", ]) - def test_traceback_failure(self): - p1 = self.makepyfile(test_fail=""" + def test_traceback_failure(self, testdir): + p1 = testdir.makepyfile(""" def g(): return 2 def f(x): @@ -209,16 +171,16 @@ def test_onefails(): f(3) """) - result = self.runpytest(p1) - assert_lines_contain_lines(result.outlines, [ - "*test_fail.py F", + result = testdir.runpytest(p1) + result.stdout.fnmatch_lines([ + "*test_traceback_failure.py F", "====* FAILURES *====", "____*____", "", " def test_onefails():", "> f(3)", "", - "*test_fail.py:6: ", + "*test_*.py:6: ", "_ _ _ *", #"", " def f(x):", @@ -226,11 +188,11 @@ "E assert 3 == 2", "E + where 2 = g()", "", - "*test_fail.py:4: AssertionError" + "*test_traceback_failure.py:4: AssertionError" ]) - def test_capturing_outerr(self): - p1 = self.makepyfile(test_one=""" + def test_capturing_outerr(self, testdir): + p1 = testdir.makepyfile(""" import sys def test_capturing(): print 42 @@ -240,52 +202,35 @@ print >>sys.stderr, 2 raise ValueError """) - result = self.runpytest(p1) - assert_lines_contain_lines(result.outlines, [ - "*test_one.py .F", + result = testdir.runpytest(p1) + result.stdout.fnmatch_lines([ + "*test_capturing_outerr.py .F", "====* FAILURES *====", "____*____", - "*test_one.py:8: ValueError", + "*test_capturing_outerr.py:8: ValueError", "*--- Captured stdout ---*", "1", "*--- Captured stderr ---*", "2", ]) - def test_showlocals(self): - p1 = self.makepyfile(test_one=""" + def test_showlocals(self, testdir): + p1 = testdir.makepyfile(""" def test_showlocals(): x = 3 y = "x" * 5000 assert 0 """) - result = self.runpytest(p1, '-l') - assert_lines_contain_lines(result.outlines, [ + result = testdir.runpytest(p1, '-l') + result.stdout.fnmatch_lines([ #"_ _ * Locals *", "x* = 3", "y* = 'xxxxxx*" ]) - def test_doctest_simple_failing(self): - p = self.maketxtfile(doc=""" - >>> i = 0 - >>> i + 1 - 2 - """) - result = self.runpytest(p) - assert_lines_contain_lines(result.outlines, [ - '001 >>> i = 0', - '002 >>> i + 1', - 'Expected:', - " 2", - "Got:", - " 1", - "*doc.txt:2: DocTestFailure" - ]) - def test_dist_testing(self): - p1 = self.makepyfile( - test_one=""" + def test_dist_testing(self, testdir): + p1 = testdir.makepyfile(""" import py def test_fail0(): assert 0 @@ -300,22 +245,20 @@ dist_hosts = ['localhost'] * 3 """ ) - result = self.runpytest(p1, '-d') - assert_lines_contain_lines(result.outlines, [ + result = testdir.runpytest(p1, '-d') + result.stdout.fnmatch_lines([ "HOSTUP: localhost*Python*", #"HOSTUP: localhost*Python*", #"HOSTUP: localhost*Python*", - "*1/3 passed + 1 skip*", - "*failures: 2*", + "*2 failed, 1 passed, 1 skipped*", ]) assert result.ret == 1 - def test_dist_tests_with_crash(self): + def test_dist_tests_with_crash(self, testdir): if not hasattr(py.std.os, 'kill'): py.test.skip("no os.kill") - p1 = self.makepyfile( - test_one=""" + p1 = testdir.makepyfile(""" import py def test_fail0(): assert 0 @@ -335,34 +278,33 @@ dist_hosts = ['localhost'] * 3 """ ) - result = self.runpytest(p1, '-d') - assert_lines_contain_lines(result.outlines, [ + result = testdir.runpytest(p1, '-d') + result.stdout.fnmatch_lines([ "*localhost*Python*", "*localhost*Python*", "*localhost*Python*", "HostDown*localhost*TERMINATED*", - "*1/4 passed + 1 skip*", - "*failures: 3*", + "*3 failed, 1 passed, 1 skipped*" ]) assert result.ret == 1 - def test_keyboard_interrupt(self): - p1 = self.makepyfile(test_one=""" + def test_keyboard_interrupt(self, testdir): + p1 = testdir.makepyfile(""" import py def test_fail(): raise ValueError() def test_inter(): raise KeyboardInterrupt() """) - result = self.runpytest(p1) - assert_lines_contain_lines(result.outlines, [ + result = testdir.runpytest(p1) + result.stdout.fnmatch_lines([ #"*test_inter() INTERRUPTED", "*KEYBOARD INTERRUPT*", - "*0/1 passed*", + "*1 failed*", ]) - def test_verbose_reporting(self): - p1 = self.makepyfile(test_one=""" + def test_verbose_reporting(self, testdir): + p1 = testdir.makepyfile(""" import py def test_fail(): raise ValueError() @@ -376,20 +318,20 @@ assert x == 1 yield check, 0 """) - result = self.runpytest(p1, '-v') - assert_lines_contain_lines(result.outlines, [ - "*test_one.py:2: test_fail*FAIL", - "*test_one.py:4: test_pass*PASS", - "*test_one.py:7: TestClass.test_skip*SKIP", - "*test_one.py:10: test_gen*FAIL", + result = testdir.runpytest(p1, '-v') + result.stdout.fnmatch_lines([ + "*test_verbose_reporting.py:2: test_fail*FAIL", + "*test_verbose_reporting.py:4: test_pass*PASS", + "*test_verbose_reporting.py:7: TestClass.test_skip*SKIP", + "*test_verbose_reporting.py:10: test_gen*FAIL", ]) assert result.ret == 1 -class TestInteractive(AcceptBase): - def getspawn(self): +class TestInteractive: + def getspawn(self, tmpdir): pexpect = py.test.importorskip("pexpect") def spawn(cmd): - return pexpect.spawn(cmd, logfile=self.tmpdir.join("spawn.out").open("w")) + return pexpect.spawn(cmd, logfile=tmpdir.join("spawn.out").open("w")) return spawn def requirespexpect(self, version_needed): @@ -398,45 +340,43 @@ if ver < version_needed: py.test.skip("pexpect version %s needed" %(".".join(map(str, version_needed)))) - def test_pdb_interaction(self): + def test_pdb_interaction(self, testdir): self.requirespexpect((2,3)) - spawn = self.getspawn() - self.makepyfile(test_one=""" + spawn = self.getspawn(testdir.tmpdir) + p1 = testdir.makepyfile(""" def test_1(): - #hello - assert 1 == 0 + i = 0 + assert i == 1 """) - child = spawn("%s %s --pdb test_one.py" % (py.std.sys.executable, - pytestpath)) + child = spawn("%s %s --pdb %s" % (py.std.sys.executable, pytestpath, p1)) child.timeout = EXPECTTIMEOUT - child.expect(".*def test_1.*") - child.expect(".*hello.*") + #child.expect(".*def test_1.*") + child.expect(".*i = 0.*") child.expect("(Pdb)") child.sendeof() - child.expect("failures: 1") + child.expect("1 failed") if child.isalive(): child.wait() - def test_simple_looponfailing_interaction(self): - spawn = self.getspawn() - test_one = self.makepyfile(test_one=""" + def test_simple_looponfailing_interaction(self, testdir): + spawn = self.getspawn(testdir.tmpdir) + p1 = testdir.makepyfile(""" def test_1(): assert 1 == 0 """) - test_one.setmtime(test_one.mtime() - 5.0) - child = spawn("%s %s --looponfailing test_one.py" % (py.std.sys.executable, - str(pytestpath))) + p1.setmtime(p1.mtime() - 50.0) + child = spawn("%s %s --looponfailing %s" % (py.std.sys.executable, pytestpath, p1)) child.timeout = EXPECTTIMEOUT child.expect("assert 1 == 0") - child.expect("test_one.py:") - child.expect("failures: 1") + child.expect("test_simple_looponfailing_interaction.py:") + child.expect("1 failed") child.expect("waiting for changes") - test_one.write(py.code.Source(""" + p1.write(py.code.Source(""" def test_1(): assert 1 == 1 """)) - child.expect("MODIFIED.*test_one.py", timeout=4.0) - child.expect("failures: no failures", timeout=5.0) + child.expect("MODIFIED.*test_simple_looponfailing_interaction.py", timeout=4.0) + child.expect("1 passed", timeout=5.0) child.kill(15) Deleted: /py/trunk/py/test/testing/suptest.py ============================================================================== --- /py/trunk/py/test/testing/suptest.py Fri Feb 27 11:18:27 2009 +++ (empty file) @@ -1,230 +0,0 @@ -""" - test support code - - makeuniquepyfile(source) generates a per-test-run-unique directory and test_*.py file - - for analyzing events an EventSorter instance is returned for both of: - * events_from_cmdline(args): inprocess-run of cmdline invocation - * events_from_session(session): inprocess-run of given session - - eventappender(config): for getting all events in a list: -""" -import py -from py.__.test import event -from fnmatch import fnmatch - -def eventappender(session): - l = [] - def app(ev): - print ev - l.append(ev) - session.bus.subscribe(app) - return l - -def initsorter_from_cmdline(args=None): - if args is None: - args = [] - config = py.test.config._reparse(args) - session = config.initsession() - sorter = EventSorter(config, session) - return sorter - -def getcolitems(config): - return [config.getfsnode(arg) for arg in config.args] - -def events_from_cmdline(args=None): - sorter = initsorter_from_cmdline(args) - sorter.session.main(getcolitems(sorter.session.config)) - return sorter - -def events_from_runsource(source): - source = py.code.Source(source) - tfile = makeuniquepyfile(source) - return events_from_cmdline([tfile]) - -def events_from_session(session): - sorter = EventSorter(session.config, session) - session.main(getcolitems(session.config)) - return sorter - -class EventSorter(object): - def __init__(self, config, session=None): - self.config = config - self.session = session - self.cls2events = d = {} - def app(event): - print "[event]", event - for cls in py.std.inspect.getmro(event.__class__): - if cls is not object: - d.setdefault(cls, []).append(event) - session.bus.subscribe(app) - - def get(self, cls): - return self.cls2events.get(cls, []) - - def listoutcomes(self): - passed = [] - skipped = [] - failed = [] - for ev in self.get(event.ItemTestReport): - if ev.passed: - passed.append(ev) - elif ev.skipped: - skipped.append(ev) - elif ev.failed: - failed.append(ev) - return passed, skipped, failed - - def countoutcomes(self): - return map(len, self.listoutcomes()) - - def assertoutcome(self, passed=0, skipped=0, failed=0): - realpassed, realskipped, realfailed = self.listoutcomes() - assert passed == len(realpassed) - assert skipped == len(realskipped) - assert failed == len(realfailed) - - def getfailedcollections(self): - l = [] - for ev in self.get(event.CollectionReport): - if ev.failed: - l.append(ev) - return l - - def getreport(self, inamepart): - """ return a testreport whose dotted import path matches """ - l = [] - for rep in self.get(event.ItemTestReport): - if inamepart in rep.colitem.listnames(): - l.append(rep) - if len(l) != 1: - raise ValueError("did not find exactly one testreport" - "found" + str(l)) - return l[0] - - -counter = py.std.itertools.count().next -def makeuniquepyfile(source): - dirname = "test_%d" %(counter(),) - tmpdir = py.test.ensuretemp(dirname) - p = tmpdir.join(dirname + ".py") - assert not p.check() - p.write(py.code.Source(source)) - print "created test file", p - p.dirpath("__init__.py").ensure() - return p - -def getItemTestReport(source, tb="long"): - tfile = makeuniquepyfile(source) - sorter = events_from_cmdline([tfile, "--tb=%s" %tb]) - # get failure base info - failevents = sorter.get(event.ItemTestReport) - assert len(failevents) == 1 - return failevents[0],tfile - -def assert_lines_contain_lines(lines1, lines2): - """ assert that lines2 are contained (linearly) in lines1. - return a list of extralines found. - """ - __tracebackhide__ = True - if isinstance(lines2, str): - lines2 = py.code.Source(lines2) - if isinstance(lines2, py.code.Source): - lines2 = lines2.strip().lines - - extralines = [] - lines1 = lines1[:] - nextline = None - for line in lines2: - nomatchprinted = False - while lines1: - nextline = lines1.pop(0) - if line == nextline: - print "exact match:", repr(line) - break - elif fnmatch(nextline, line): - print "fnmatch:", repr(line) - print " with:", repr(nextline) - break - else: - if not nomatchprinted: - print "nomatch:", repr(line) - nomatchprinted = True - print " and:", repr(nextline) - extralines.append(nextline) - else: - if line != nextline: - #__tracebackhide__ = True - raise AssertionError("expected line not found: %r" % line) - extralines.extend(lines1) - return extralines - -# XXX below some code to help with inlining examples -# as source code. -# -class FileCreation(object): - def setup_method(self, method): - self.tmpdir = py.test.ensuretemp("%s_%s" % - (self.__class__.__name__, method.__name__)) - def makepyfile(self, **kwargs): - return self._makefile('.py', **kwargs) - def maketxtfile(self, **kwargs): - return self._makefile('.txt', **kwargs) - def _makefile(self, ext, **kwargs): - ret = None - for name, value in kwargs.iteritems(): - p = self.tmpdir.join(name).new(ext=ext) - source = py.code.Source(value) - p.write(str(py.code.Source(value)).lstrip()) - if ret is None: - ret = p - return ret - - def parseconfig(self, *args): - return py.test.config._reparse(list(args)) - -class InlineCollection(FileCreation): - """ helps to collect and run test functions inlining other test functions. """ - - def getmodulecol(self, source, configargs=(), withsession=False): - self.tmpdir.ensure("__init__.py") - kw = {"test_" + self.tmpdir.basename: py.code.Source(source).strip()} - path = self.makepyfile(**kw) - self.config = self.parseconfig(path, *configargs) - if withsession: - self.session = self.config.initsession() - return self.config.getfsnode(path) - - def getitems(self, source): - modulecol = self.getmodulecol(source) - return modulecol.collect() - - def getitem(self, source, funcname="test_func"): - modulecol = self.getmodulecol(source) - item = modulecol.join(funcname) - assert item is not None, "%r item not found in module:\n%s" %(funcname, source) - return item - - def runitem(self, func, funcname="test_func", **runnerargs): - item = self.getitem(func, funcname=funcname) - runner = self.getrunner() - return runner(item, **runnerargs) - -class InlineSession(InlineCollection): - def parse_and_run(self, *args): - config = self.parseconfig(*args) - session = config.initsession() - sorter = EventSorter(config, session) - session.main() - return sorter - -def popvalue(stringio): - value = stringio.getvalue().rstrip() - stringio.truncate(0) - return value - -def assert_stringio_contains_lines(stringio, tomatchlines): - stringio.seek(0) - l = stringio.readlines() - l = map(str.rstrip, l) - assert_lines_contain_lines(l, tomatchlines) Modified: py/trunk/py/test/testing/test_collect.py ============================================================================== --- py/trunk/py/test/testing/test_collect.py (original) +++ py/trunk/py/test/testing/test_collect.py Fri Feb 27 11:18:27 2009 @@ -1,220 +1,13 @@ -from __future__ import generators import py -from py.__.test import event, outcome -from py.__.test.testing import suptest -from py.__.test.conftesthandle import Conftest -from py.__.test.collect import SetupState -from test_config import getcolitems -from py.__.test.pycollect import DoctestFileContent - -class DummyConfig: - def __init__(self): - self._conftest = Conftest() - self._setupstate = SetupState() - class dummyoption: - nomagic = False - self.option = dummyoption - def getvalue(self, name, fspath): - return self._conftest.rget(name, fspath) - -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(mod.__name__) - mod.dummyconfig = DummyConfig() - -def test_collect_versus_item(): - from py.__.test.collect import Collector, Item - assert not issubclass(Collector, Item) - assert not issubclass(Item, Collector) - -def test_ignored_certain_directories(): - tmp = py.test.ensuretemp("ignore_certain_directories") - tmp.ensure("_darcs", 'test_notfound.py') - tmp.ensure("CVS", 'test_notfound.py') - tmp.ensure("{arch}", 'test_notfound.py') - tmp.ensure(".whatever", 'test_notfound.py') - tmp.ensure(".bzr", 'test_notfound.py') - tmp.ensure("normal", 'test_found.py') - tmp.ensure('test_found.py') - - col = py.test.collect.Directory(tmp, config=dummyconfig) - items = col.collect() - names = [x.name for x in items] - assert len(items) == 2 - assert 'normal' in names - assert 'test_found.py' in names - -class TestCollect(suptest.InlineCollection): - def test_failing_import(self): - modcol = self.getmodulecol("import alksdjalskdjalkjals") - py.test.raises(ImportError, modcol.collect) - py.test.raises(ImportError, modcol.collect) - py.test.raises(ImportError, modcol.run) - - def test_syntax_error_in_module(self): - modcol = self.getmodulecol("this is a syntax error") - py.test.raises(SyntaxError, modcol.collect) - py.test.raises(SyntaxError, modcol.collect) - py.test.raises(SyntaxError, modcol.run) - def test_listnames_and__getitembynames(self): - modcol = self.getmodulecol("pass") - names = modcol.listnames() - dircol = modcol._config.getfsnode(modcol._config.topdir) - x = dircol._getitembynames(names) - assert modcol.name == x.name - assert modcol.name == x.name - - def test_listnames_getitembynames_custom(self): - hello = self._makefile(".xxx", hello="world") - self.makepyfile(conftest=""" - import py - class CustomFile(py.test.collect.File): - pass - class MyDirectory(py.test.collect.Directory): - def collect(self): - return [CustomFile(self.fspath.join("hello.xxx"), parent=self)] - Directory = MyDirectory - """) - config = self.parseconfig(hello) - node = config.getfsnode(hello) - assert isinstance(node, py.test.collect.File) - assert node.name == "hello.xxx" - names = node.listnames()[1:] - dircol = config.getfsnode(config.topdir) - node = dircol._getitembynames(names) - assert isinstance(node, py.test.collect.File) - - def test_found_certain_testfiles(self): - p1 = self.makepyfile(test_found = "pass", found_test="pass") - col = py.test.collect.Directory(p1.dirpath(), config=dummyconfig) - items = col.collect() # Directory collect returns files sorted by name - assert len(items) == 2 - assert items[1].name == 'test_found.py' - assert items[0].name == 'found_test.py' - - def test_disabled_class(self): - modcol = self.getmodulecol(""" - class TestClass: - disabled = True - def test_method(self): - pass - """) - l = modcol.collect() - assert len(l) == 1 - modcol = l[0] - assert isinstance(modcol, py.test.collect.Class) - assert not modcol.collect() - - def test_disabled_module(self): - modcol = self.getmodulecol(""" - disabled = True - def setup_module(mod): - raise ValueError - """) - assert not modcol.collect() - assert not modcol.run() - - def test_generative_functions(self): - modcol = self.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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == '[0]' - assert gencolitems[0].obj.func_name == 'func1' - - def test_generative_methods(self): - modcol = self.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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == '[0]' - assert gencolitems[0].obj.func_name == 'func1' - - def test_generative_functions_with_explicit_names(self): - modcol = self.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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == "['seventeen']" - assert gencolitems[0].obj.func_name == 'func1' - assert gencolitems[1].name == "['fortytwo']" - assert gencolitems[1].obj.func_name == 'func1' - - def test_generative_methods_with_explicit_names(self): - modcol = self.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, py.test.collect.Generator) - gencolitems = gencol.collect() - assert len(gencolitems) == 2 - assert isinstance(gencolitems[0], py.test.collect.Function) - assert isinstance(gencolitems[1], py.test.collect.Function) - assert gencolitems[0].name == "['m1']" - assert gencolitems[0].obj.func_name == 'func1' - assert gencolitems[1].name == "['m2']" - assert gencolitems[1].obj.func_name == 'func1' - - def test_module_assertion_setup(self): - modcol = self.getmodulecol("pass") - from py.__.magic import assertion - l = [] - py.magic.patch(assertion, "invoke", lambda: l.append(None)) - try: - modcol.setup() - finally: - py.magic.revert(assertion, "invoke") - x = l.pop() - assert x is None - py.magic.patch(assertion, "revoke", lambda: l.append(None)) - try: - modcol.teardown() - finally: - py.magic.revert(assertion, "revoke") - x = l.pop() - assert x is None +class TestCollector: + def test_collect_versus_item(self): + from py.__.test.collect import Collector, Item + assert not issubclass(Collector, Item) + assert not issubclass(Item, Collector) - def test_check_equality_and_cmp_basic(self): - modcol = self.getmodulecol(""" + def test_check_equality_and_cmp_basic(self, testdir): + modcol = testdir.getmodulecol(""" def test_pass(): pass def test_fail(): assert 0 """) @@ -244,110 +37,138 @@ assert [1,2,3] != fn assert modcol != fn - def test_directory_file_sorting(self): - p1 = self.makepyfile(test_one="hello") + def test_totrail_and_back(self, tmpdir): + a = tmpdir.ensure("a", dir=1) + tmpdir.ensure("a", "__init__.py") + x = tmpdir.ensure("a", "trail.py") + config = py.test.config._reparse([x]) + col = config.getfsnode(x) + trail = col._totrail() + assert len(trail) == 2 + assert trail[0] == a.relto(config.topdir) + assert trail[1] == ('trail.py',) + col2 = py.test.collect.Collector._fromtrail(trail, config) + assert col2.listnames() == col.listnames() + + def test_totrail_topdir_and_beyond(self, tmpdir): + config = py.test.config._reparse([tmpdir]) + col = config.getfsnode(config.topdir) + trail = col._totrail() + assert len(trail) == 2 + assert trail[0] == '.' + assert trail[1] == () + col2 = py.test.collect.Collector._fromtrail(trail, config) + assert col2.fspath == config.topdir + assert len(col2.listchain()) == 1 + col3 = config.getfsnode(config.topdir.dirpath()) + py.test.raises(ValueError, + "col3._totrail()") + + + def test_listnames_and__getitembynames(self, testdir): + modcol = testdir.getmodulecol("pass", withinit=True) + print modcol._config.pytestplugins.getplugins() + names = modcol.listnames() + print names + dircol = modcol._config.getfsnode(modcol._config.topdir) + x = dircol._getitembynames(names) + assert modcol.name == x.name + + def test_listnames_getitembynames_custom(self, testdir): + hello = testdir.makefile(".xxx", hello="world") + testdir.makepyfile(conftest=""" + import py + class CustomFile(py.test.collect.File): + pass + class MyDirectory(py.test.collect.Directory): + def collect(self): + return [CustomFile(self.fspath.join("hello.xxx"), parent=self)] + Directory = MyDirectory + """) + config = testdir.parseconfig(hello) + node = config.getfsnode(hello) + assert isinstance(node, py.test.collect.File) + assert node.name == "hello.xxx" + names = node.listnames()[1:] + dircol = config.getfsnode(config.topdir) + node = dircol._getitembynames(names) + assert isinstance(node, py.test.collect.File) + +class TestCollectFS: + def test_ignored_certain_directories(self, testdir): + tmpdir = testdir.tmpdir + tmpdir.ensure("_darcs", 'test_notfound.py') + tmpdir.ensure("CVS", 'test_notfound.py') + tmpdir.ensure("{arch}", 'test_notfound.py') + tmpdir.ensure(".whatever", 'test_notfound.py') + tmpdir.ensure(".bzr", 'test_notfound.py') + tmpdir.ensure("normal", 'test_found.py') + tmpdir.ensure('test_found.py') + + col = testdir.parseconfig(tmpdir).getfsnode(tmpdir) + items = col.collect() + names = [x.name for x in items] + assert len(items) == 2 + assert 'normal' in names + assert 'test_found.py' in names + + def test_found_certain_testfiles(self, testdir): + p1 = testdir.makepyfile(test_found = "pass", found_test="pass") + col = testdir.parseconfig(p1).getfsnode(p1.dirpath()) + items = col.collect() # Directory collect returns files sorted by name + assert len(items) == 2 + assert items[1].name == 'test_found.py' + assert items[0].name == 'found_test.py' + + def test_directory_file_sorting(self, testdir): + p1 = testdir.makepyfile(test_one="hello") p1.dirpath().mkdir("x") p1.dirpath().mkdir("dir1") - self.makepyfile(test_two="hello") + testdir.makepyfile(test_two="hello") p1.dirpath().mkdir("dir2") - config = self.parseconfig() + config = testdir.parseconfig() col = config.getfsnode(p1.dirpath()) names = [x.name for x in col.collect()] assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"] - def test_collector_deprecated_run_method(self): - modcol = self.getmodulecol("pass") + def test_collector_deprecated_run_method(self, testdir): + modcol = testdir.getmodulecol("pass") res1 = py.test.deprecated_call(modcol.run) res2 = modcol.collect() assert res1 == [x.name for x in res2] - def test_allow_sane_sorting_for_decorators(self): - modcol = self.getmodulecol(""" - def dec(f): - g = lambda: f(2) - g.place_as = f - return g - - - def test_a(y): - pass - test_a = dec(test_a) - - def test_b(y): - pass - test_b = dec(test_b) - """) - colitems = modcol.collect() - assert len(colitems) == 2 - f1, f2 = colitems - assert cmp(f2, f1) > 0 - - -class TestCustomConftests(suptest.InlineCollection): - def test_extra_python_files_and_functions(self): - self.makepyfile(conftest=""" - import py - class MyFunction(py.test.collect.Function): - pass - class Directory(py.test.collect.Directory): - def consider_file(self, path, usefilters=True): - if path.check(fnmatch="check_*.py"): - return self.Module(path, parent=self) - return super(Directory, self).consider_file(path, usefilters=usefilters) - class myfuncmixin: - Function = MyFunction - def funcnamefilter(self, name): - return name.startswith('check_') - class Module(myfuncmixin, py.test.collect.Module): - def classnamefilter(self, name): - return name.startswith('CustomTestClass') - class Instance(myfuncmixin, py.test.collect.Instance): - pass - """) - checkfile = self.makepyfile(check_file=""" - def check_func(): - assert 42 == 42 - class CustomTestClass: - def check_method(self): - assert 23 == 23 - """) - # check that directory collects "check_" files - config = self.parseconfig() - col = config.getfsnode(checkfile.dirpath()) - colitems = col.collect() - assert len(colitems) == 1 - assert isinstance(colitems[0], py.test.collect.Module) - - # check that module collects "check_" functions and methods - config = self.parseconfig(checkfile) - col = config.getfsnode(checkfile) - assert isinstance(col, py.test.collect.Module) - colitems = col.collect() - assert len(colitems) == 2 - funccol = colitems[0] - assert isinstance(funccol, py.test.collect.Function) - assert funccol.name == "check_func" - clscol = colitems[1] - assert isinstance(clscol, py.test.collect.Class) - colitems = clscol.collect()[0].collect() - assert len(colitems) == 1 - assert colitems[0].name == "check_method" - - def test_non_python_files(self): - self.makepyfile(conftest=""" +class TestCollectPluginHooks: + def test_pytest_collect_file(self, testdir): + tmpdir = testdir.tmpdir + wascalled = [] + class Plugin: + def pytest_collect_file(self, path, parent): + wascalled.append(path) + config = testdir.Config() + config.pytestplugins.register(Plugin()) + config.parse([tmpdir]) + col = config.getfsnode(tmpdir) + testdir.makefile(".abc", "xyz") + res = col.collect() + assert len(wascalled) == 1 + assert wascalled[0].ext == '.abc' + +class TestCustomConftests: + def test_non_python_files(self, testdir): + testdir.makepyfile(conftest=""" import py class CustomItem(py.test.collect.Item): def run(self): pass class Directory(py.test.collect.Directory): - def consider_file(self, fspath, usefilters=True): + def consider_file(self, fspath): if fspath.ext == ".xxx": return CustomItem(fspath.basename, parent=self) """) - checkfile = self._makefile(ext="xxx", hello="world") - self.makepyfile(x="") - self.maketxtfile(x="") - config = self.parseconfig() + checkfile = testdir.makefile(ext="xxx", hello="world") + testdir.makepyfile(x="") + testdir.maketxtfile(x="") + config = testdir.parseconfig() dircol = config.getfsnode(checkfile.dirpath()) colitems = dircol.collect() assert len(colitems) == 1 @@ -358,216 +179,9 @@ assert item.name == "hello.xxx" assert item.__class__.__name__ == "CustomItem" -def test_module_file_not_found(): - fn = tmpdir.join('nada','no') - col = py.test.collect.Module(fn, config=dummyconfig) - py.test.raises(py.error.ENOENT, col.collect) - - -def test_order_of_execution_generator_same_codeline(): - o = tmpdir.ensure('genorder1', dir=1) - o.join("test_order1.py").write(py.code.Source(""" - def test_generative_order_of_execution(): - test_list = [] - expected_list = range(6) - - def list_append(item): - test_list.append(item) - - def assert_order_of_execution(): - print 'expected order', expected_list - print 'but got ', test_list - assert test_list == expected_list - - for i in expected_list: - yield list_append, i - yield assert_order_of_execution - """)) - sorter = suptest.events_from_cmdline([o]) - passed, skipped, failed = sorter.countoutcomes() - assert passed == 7 - assert not skipped and not failed - -def test_order_of_execution_generator_different_codeline(): - o = tmpdir.ensure('genorder2', dir=2) - o.join("test_genorder2.py").write(py.code.Source(""" - def test_generative_tests_different_codeline(): - test_list = [] - expected_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(): - print 'expected order', expected_list - 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 - """)) - sorter = suptest.events_from_cmdline([o]) - passed, skipped, failed = sorter.countoutcomes() - assert passed == 4 - assert not skipped and not failed - -def test_function_equality(): - config = py.test.config._reparse([tmpdir]) - f1 = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance) - f2 = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=callable) - assert not f1 == f2 - assert f1 != f2 - f3 = py.test.collect.Function(name="name", config=config, - args=(1,2), callobj=callable) - assert not f3 == f2 - assert f3 != f2 - - assert not f3 == f1 - assert f3 != f1 - - f1_b = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance) - assert f1 == f1_b - assert not f1 != f1_b - -class Testgenitems: - def setup_class(cls): - cls.classtemp = py.test.ensuretemp(cls.__name__) - - def setup_method(self, method): - self.tmp = self.classtemp.mkdir(method.func_name) - - def _genitems(self, tmp=None): - if tmp is None: - tmp = self.tmp - print "using tempdir", tmp - config = py.test.config._reparse([tmp]) - session = config.initsession() - l = suptest.eventappender(session) - items = list(session.genitems(getcolitems(config))) - return items, l - - def test_check_collect_hashes(self): - one = self.tmp.ensure("test_check_collect_hashes.py") - one.write(py.code.Source(""" - def test_1(): - pass - - def test_2(): - pass - """)) - one.copy(self.tmp.join("test_check_collect_hashes_2.py")) - items, events = self._genitems() - assert len(items) == 4 - for numi, i in enumerate(items): - for numj, j in enumerate(items): - if numj != numi: - assert hash(i) != hash(j) - assert i != j - - def test_root_conftest_syntax_error(self): - # do we want to unify behaviour with - # test_subdir_conftest_error? - self.tmp.ensure("conftest.py").write("raise SyntaxError\n") - py.test.raises(SyntaxError, self._genitems) - - def test_subdir_conftest_error(self): - self.tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") - items, events = self._genitems() - failures = [x for x in events - if isinstance(x, event.CollectionReport) - and x.failed] - assert len(failures) == 1 - ev = failures[0] - assert ev.outcome.longrepr.reprcrash.message.startswith("SyntaxError") - - def test_example_items1(self): - self.tmp.ensure("test_example.py").write(py.code.Source(''' - def testone(): - pass - - class TestX: - def testmethod_one(self): - pass - - class TestY(TestX): - pass - ''')) - items, events = self._genitems() - assert len(items) == 3 - assert items[0].name == 'testone' - assert items[1].name == 'testmethod_one' - assert items[2].name == 'testmethod_one' - - # let's also test getmodpath here - assert items[0].getmodpath() == "testone" - assert items[1].getmodpath() == "TestX.testmethod_one" - assert items[2].getmodpath() == "TestY.testmethod_one" - - s = items[0].getmodpath(stopatmodule=False) - assert s == "test_example_items1.test_example.testone" - print s - - def test_collect_doctest_files_with_test_prefix(self): - self.tmp.ensure("whatever.txt") - checkfile = self.tmp.ensure("test_something.txt") - checkfile.write(py.code.Source(""" - alskdjalsdk - >>> i = 5 - >>> i-1 - 4 - """)) - for x in (self.tmp, checkfile): - #print "checking that %s returns custom items" % (x,) - items, events = self._genitems(x) - assert len(items) == 1 - assert isinstance(items[0], DoctestFileContent) - -class TestCollector: - def setup_method(self, method): - self.tmpdir = py.test.ensuretemp("%s_%s" % - (self.__class__.__name__, method.__name__)) - - def test_totrail_and_back(self): - a = self.tmpdir.ensure("a", dir=1) - self.tmpdir.ensure("a", "__init__.py") - x = self.tmpdir.ensure("a", "trail.py") - config = py.test.config._reparse([x]) - col = config.getfsnode(x) - trail = col._totrail() - assert len(trail) == 2 - assert trail[0] == a.relto(config.topdir) - assert trail[1] == ('trail.py',) - col2 = py.test.collect.Collector._fromtrail(trail, config) - assert col2.listnames() == col.listnames() - - def test_totrail_topdir_and_beyond(self): - config = py.test.config._reparse([self.tmpdir]) - col = config.getfsnode(config.topdir) - trail = col._totrail() - assert len(trail) == 2 - assert trail[0] == '.' - assert trail[1] == () - col2 = py.test.collect.Collector._fromtrail(trail, config) - assert col2.fspath == config.topdir - assert len(col2.listchain()) == 1 - col3 = config.getfsnode(config.topdir.dirpath()) - py.test.raises(ValueError, - "col3._totrail()") - -class TestCollectorReprs(suptest.InlineCollection): - def test_repr_metainfo_basic_item(self): - modcol = self.getmodulecol("") +class TestCollectorReprs: + def test_repr_metainfo_basic_item(self, testdir): + modcol = testdir.getmodulecol("") Item = py.test.collect.Item item = Item("virtual", parent=modcol) info = item.repr_metainfo() @@ -575,15 +189,15 @@ assert not info.lineno assert info.modpath == "Item" - def test_repr_metainfo_func(self): - item = self.getitem("def test_func(): pass") + def test_repr_metainfo_func(self, testdir): + item = testdir.getitem("def test_func(): pass") info = item.repr_metainfo() assert info.fspath == item.fspath assert info.lineno == 0 assert info.modpath == "test_func" - def test_repr_metainfo_class(self): - modcol = self.getmodulecol(""" + def test_repr_metainfo_class(self, testdir): + modcol = testdir.getmodulecol(""" # lineno 0 class TestClass: def test_hello(self): pass @@ -594,8 +208,8 @@ assert info.lineno == 1 assert info.modpath == "TestClass" - def test_repr_metainfo_generator(self): - modcol = self.getmodulecol(""" + def test_repr_metainfo_generator(self, testdir): + modcol = testdir.getmodulecol(""" # lineno 0 def test_gen(): def check(x): @@ -624,56 +238,3 @@ def test_method(self): pass """ - -from py.__.test.dsession.mypickle import ImmutablePickler -class PickleTransport: - def __init__(self): - self.p1 = ImmutablePickler(uneven=0) - self.p2 = ImmutablePickler(uneven=1) - - def p1_to_p2(self, obj): - return self.p2.loads(self.p1.dumps(obj)) - - def p2_to_p1(self, obj): - return self.p1.loads(self.p2.dumps(obj)) - -class TestPickling(suptest.InlineCollection): - def setup_method(self, method): - super(TestPickling, self).setup_method(method) - pt = PickleTransport() - self.p1_to_p2 = pt.p1_to_p2 - self.p2_to_p1 = pt.p2_to_p1 - - def unifyconfig(self, config): - p2config = self.p1_to_p2(config) - p2config._initafterpickle(config.topdir) - return p2config - - def test_pickle_config(self): - config1 = py.test.config._reparse([]) - p2config = self.unifyconfig(config1) - assert p2config.topdir == config1.topdir - config_back = self.p2_to_p1(p2config) - assert config_back is config1 - - def test_pickle_module(self): - modcol1 = self.getmodulecol("def test_one(): pass") - self.unifyconfig(modcol1._config) - - modcol2a = self.p1_to_p2(modcol1) - modcol2b = self.p1_to_p2(modcol1) - assert modcol2a is modcol2b - - modcol1_back = self.p2_to_p1(modcol2a) - assert modcol1_back - - def test_pickle_func(self): - modcol1 = self.getmodulecol("def test_one(): pass") - self.unifyconfig(modcol1._config) - item = modcol1.collect_by_name("test_one") - item2a = self.p1_to_p2(item) - assert item is not item2a # of course - assert item2a.name == item.name - modback = self.p2_to_p1(item2a.parent) - assert modback is modcol1 - Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Fri Feb 27 11:18:27 2009 @@ -1,344 +1,63 @@ -from __future__ import generators import py -from py.__.test.config import gettopdir -from py.__.test.testing import suptest -from py.__.test import event +pytest_plugins = 'pytest_iocapture' -def getcolitems(config): - return [config.getfsnode(arg) for arg in config.args] - -def test_tmpdir(): - d1 = py.test.ensuretemp('hello') - d2 = py.test.ensuretemp('hello') - assert d1 == d2 - assert d1.check(dir=1) - -def test_config_cmdline_options(): - o = py.test.ensuretemp('configoptions') - o.ensure("conftest.py").write(py.code.Source(""" - import py - def _callback(option, opt_str, value, parser, *args, **kwargs): - option.tdest = True - Option = py.test.config.Option - option = py.test.config.addoptions("testing group", - Option('-G', '--glong', action="store", default=42, - type="int", dest="gdest", help="g value."), - # XXX note: special case, option without a destination - Option('-T', '--tlong', action="callback", callback=_callback, - help='t value'), - ) - """)) - old = o.chdir() - try: +class TestConfigCmdlineParsing: + @py.test.keywords(xfail="commit parser") + def test_config_addoption(self, stdcapture): + from py.__.test.config import Config + config = Config() + config.addoption("cat1", "--option1", action="store_true") + config.addoption("cat1", "--option2", action="store_true") + config.parse(["-h"]) + out, err = stdcapture.reset() + assert out.count("cat1") == 1 + assert out.find("option1") != -1 + assert out.find("option2") != -1 + + def test_config_cmdline_options(self, testdir): + testdir.makepyfile(conftest=""" + import py + def _callback(option, opt_str, value, parser, *args, **kwargs): + option.tdest = True + Option = py.test.config.Option + option = py.test.config.addoptions("testing group", + Option('-G', '--glong', action="store", default=42, + type="int", dest="gdest", help="g value."), + # XXX note: special case, option without a destination + Option('-T', '--tlong', action="callback", callback=_callback, + help='t value'), + ) + """) + testdir.chdir() config = py.test.config._reparse(['-G', '17']) - finally: - old.chdir() - assert config.option.gdest == 17 - -def test_config_cmdline_options_only_lowercase(): - o = py.test.ensuretemp('test_config_cmdline_options_only_lowercase') - o = o.mkdir("onemore") # neccessary because collection of o.dirpath() - # could see our conftest.py - o.ensure("conftest.py").write(py.code.Source(""" - import py - Option = py.test.config.Option - options = py.test.config.addoptions("testing group", - Option('-g', '--glong', action="store", default=42, - type="int", dest="gdest", help="g value."), - ) - """)) - old = o.chdir() - try: - py.test.raises(ValueError, """ - py.test.config._reparse(['-g', '17']) - """) - finally: - old.chdir() - -def test_parsing_again_fails(): - dir = py.test.ensuretemp("parsing_again_fails") - config = py.test.config._reparse([str(dir)]) - py.test.raises(AssertionError, "config.parse([])") - -def test_config_getvalue_honours_conftest(): - o = py.test.ensuretemp('testconfigget') - o.ensure("conftest.py").write("x=1") - o.ensure("sub", "conftest.py").write("x=2 ; y = 3") - config = py.test.config._reparse([str(o)]) - assert config.getvalue("x") == 1 - assert config.getvalue("x", o.join('sub')) == 2 - py.test.raises(KeyError, "config.getvalue('y')") - config = py.test.config._reparse([str(o.join('sub'))]) - assert config.getvalue("x") == 2 - assert config.getvalue("y") == 3 - assert config.getvalue("x", o) == 1 - py.test.raises(KeyError, 'config.getvalue("y", o)') - - -def test_config_overwrite(): - o = py.test.ensuretemp('testconfigget') - o.ensure("conftest.py").write("x=1") - config = py.test.config._reparse([str(o)]) - assert config.getvalue('x') == 1 - config.option.x = 2 - assert config.getvalue('x') == 2 - config = py.test.config._reparse([str(o)]) - assert config.getvalue('x') == 1 - -def test_gettopdir(): - tmp = py.test.ensuretemp("topdir") - assert gettopdir([tmp]) == tmp - topdir =gettopdir([tmp.join("hello"), tmp.join("world")]) - assert topdir == tmp - somefile = tmp.ensure("somefile.py") - assert gettopdir([somefile]) == tmp - -def test_gettopdir_pypkg(): - tmp = py.test.ensuretemp("topdir2") - a = tmp.ensure('a', dir=1) - b = tmp.ensure('a', 'b', '__init__.py') - c = tmp.ensure('a', 'b', 'c.py') - Z = tmp.ensure('Z', dir=1) - assert gettopdir([c]) == a - assert gettopdir([c, Z]) == tmp - - -def test_config_initafterpickle_some(): - tmp = py.test.ensuretemp("test_config_initafterpickle_some") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write("x=1 ; y=2") - hello = tmp.ensure("test_hello.py") - config = py.test.config._reparse([hello]) - config2 = py.test.config._reparse([tmp.dirpath()]) - config2._initialized = False # we have to do that from tests - config2._repr = config._makerepr() - config2._initafterpickle(topdir=tmp.dirpath()) - for col1, col2 in zip(getcolitems(config), getcolitems(config2)): - assert col1.fspath == col2.fspath - cols = getcolitems(config2) - assert len(cols) == 1 - col = cols[0] - assert col.name == 'test_hello.py' - assert col.parent.name == tmp.basename - assert col.parent.parent is None - -def test_config_make_and__mergerepr(): - tmp = py.test.ensuretemp("reprconfig1") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write("x=1") - config = py.test.config._reparse([tmp]) - repr = config._makerepr() - config.option.verbose = 42 - repr2 = config._makerepr() - config = py.test.config._reparse([tmp.dirpath()]) - py.test.raises(KeyError, "config.getvalue('x')") - config._mergerepr(repr) - assert config.getvalue('x') == 1 - config._mergerepr(repr2) - assert config.option.verbose == 42 + assert config.option.gdest == 17 - -def test_config_rconfig(): - tmp = py.test.ensuretemp("rconfigopt") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write(py.code.Source(""" - import py - Option = py.test.config.Option - option = py.test.config.addoptions("testing group", - Option('-G', '--glong', action="store", default=42, - type="int", dest="gdest", help="g value.")) - """)) - config = py.test.config._reparse([tmp, "-G", "11"]) - assert config.option.gdest == 11 - repr = config._makerepr() - config = py.test.config._reparse([tmp.dirpath()]) - py.test.raises(AttributeError, "config.option.gdest") - config._mergerepr(repr) - option = config.addoptions("testing group", - config.Option('-G', '--glong', action="store", default=42, - type="int", dest="gdest", help="g value.")) - assert config.option.gdest == 11 - assert option.gdest == 11 - -class TestSessionAndOptions(suptest.FileCreation): - def exampletestfile(self): - return self.makepyfile(file_test=""" - def test_one(): - assert 42 == 43 - - class TestClass(object): - def test_method_one(self): - assert 42 == 43 + def test_config_cmdline_options_only_lowercase(self, testdir): + testdir.makepyfile(conftest=""" + import py + Option = py.test.config.Option + options = py.test.config.addoptions("testing group", + Option('-g', '--glong', action="store", default=42, + type="int", dest="gdest", help="g value."), + ) """) + old = testdir.chdir() + try: + py.test.raises(ValueError, """ + py.test.config._reparse(['-g', '17']) + """) + finally: + old.chdir() - def test_session_eventlog(self): - eventlog = self.tmpdir.join("test_session_eventlog") - config = py.test.config._reparse([self.tmpdir, - '--eventlog=%s' % eventlog]) - session = config.initsession() - session.bus.notify(event.TestrunStart()) - s = eventlog.read() - assert s.find("TestrunStart") != -1 - - def test_session_resultlog(self): - from py.__.test.collect import Item - from py.__.test.runner import OutcomeRepr - - resultlog = self.tmpdir.join("test_session_resultlog") - config = py.test.config._reparse([self.tmpdir, - '--resultlog=%s' % resultlog]) - - session = config.initsession() - - item = Item("a", config=config) - outcome = OutcomeRepr('execute', '.', '') - rep_ev = event.ItemTestReport(item, passed=outcome) - - session.bus.notify(rep_ev) - - s = resultlog.read() - assert s.find(". a") != -1 - - def test_tracedir_tracer(self): - tracedir = self.tmpdir.join("tracedir") - config = py.test.config._reparse([self.tmpdir, - '--tracedir=%s' % tracedir]) - assert config.gettracedir() == tracedir - - trace = config.maketrace("trace1.log") # flush=True by default - trace("hello", "world") - class A: pass - trace(A()) - p = tracedir.join("trace1.log") - lines = p.readlines(cr=0) - assert lines[0].endswith("hello world") - assert lines[1].find("A") != -1 - trace.close() - - def test_trace_null(self): - config = py.test.config._reparse([self.tmpdir]) - assert config.gettracedir() is None - trace = config.maketrace("hello", flush=True) - trace("hello", "world") - trace.close() - - def test_implied_dsession(self): - for x in 'startserver runbrowser rest'.split(): - config = py.test.config._reparse([self.tmpdir, '--dist', '--%s' % x]) - assert config._getsessionname() == 'DSession' - - def test_implied_different_sessions(self): - config = py.test.config._reparse([self.tmpdir]) - assert config._getsessionname() == 'Session' - config = py.test.config._reparse([self.tmpdir, '--dist']) - assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([self.tmpdir, '-n3']) - assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([self.tmpdir, '--looponfailing']) - assert config._getsessionname() == 'LooponfailingSession' - config = py.test.config._reparse([self.tmpdir, '--exec=x']) - assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([self.tmpdir, '--dist', '--exec=x']) - assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([self.tmpdir, '-f', - '--dist', '--exec=x']) - assert config._getsessionname() == 'LooponfailingSession' - config = py.test.config._reparse([self.tmpdir, '-f', '-n3', - '--dist', '--exec=x', - '--collectonly']) - assert config._getsessionname() == 'Session' - - def test_sessionname_lookup_custom(self): - self.tmpdir.join("conftest.py").write(py.code.Source(""" - from py.__.test.session import Session - class MySession(Session): - pass - """)) - config = py.test.config._reparse(["--session=MySession", self.tmpdir]) - session = config.initsession() - assert session.__class__.__name__ == 'MySession' - - def test_initsession(self): - config = py.test.config._reparse([self.tmpdir]) - session = config.initsession() - assert session.config is config - - def test_boxed_option_default(self): - self.tmpdir.join("conftest.py").write("dist_hosts=[]") - tmpdir = self.tmpdir.ensure("subdir", dir=1) - config = py.test.config._reparse([tmpdir]) - config.initsession() - assert not config.option.boxed - config = py.test.config._reparse(['--dist', tmpdir]) - config.initsession() - assert not config.option.boxed - - def test_boxed_option_from_conftest(self): - self.tmpdir.join("conftest.py").write("dist_hosts=[]") - tmpdir = self.tmpdir.ensure("subdir", dir=1) - tmpdir.join("conftest.py").write(py.code.Source(""" - dist_hosts = [] - dist_boxed = True - """)) - config = py.test.config._reparse(['--dist', tmpdir]) - config.initsession() - assert config.option.boxed - - def test_boxed_option_from_conftest(self): - tmpdir = self.tmpdir - tmpdir.join("conftest.py").write(py.code.Source(""" - dist_boxed = False - """)) - config = py.test.config._reparse([tmpdir, '--box']) - assert config.option.boxed - config.initsession() - assert config.option.boxed - - def test_getvalue_pathlist(self): - tmpdir = self.tmpdir - somepath = tmpdir.join("x", "y", "z") - p = tmpdir.join("conftest.py") - p.write("pathlist = ['.', %r]" % str(somepath)) - config = py.test.config._reparse([p]) - assert config.getvalue_pathlist('notexist') is None - pl = config.getvalue_pathlist('pathlist') - print pl - assert len(pl) == 2 - assert pl[0] == tmpdir - assert pl[1] == somepath - - config.option.mypathlist = [py.path.local()] - pl = config.getvalue_pathlist('mypathlist') - assert pl == [py.path.local()] - - def test_config_iocapturing(self): - config = py.test.config._reparse([self.tmpdir]) - assert config.getvalue("conf_iocapture") - tmpdir = self.tmpdir.ensure("sub-with-conftest", dir=1) - tmpdir.join("conftest.py").write(py.code.Source(""" - conf_iocapture = "no" - """)) + def test_parsing_again_fails(self, tmpdir): config = py.test.config._reparse([tmpdir]) - assert config.getvalue("conf_iocapture") == "no" - capture = config._getcapture() - assert isinstance(capture, py.io.StdCapture) - assert not capture._out - assert not capture._err - assert not capture._in - assert isinstance(capture, py.io.StdCapture) - for opt, cls in (("sys", py.io.StdCapture), - ("fd", py.io.StdCaptureFD), - ): - config.option.conf_iocapture = opt - capture = config._getcapture() - assert isinstance(capture, cls) + py.test.raises(AssertionError, "config.parse([])") def test_conflict_options(self): def check_conflict_option(opts): print "testing if options conflict:", " ".join(opts) - path = self.exampletestfile() - config = py.test.config._reparse(opts + [path]) + config = py.test.config._reparse(opts) py.test.raises((ValueError, SystemExit), """ config.initsession() """) @@ -351,89 +70,99 @@ for spec in conflict_options: opts = spec.split() yield check_conflict_option, opts - - def test_implied_options(self): - def check_implied_option(opts, expr): - path = self.exampletestfile() - config = py.test.config._reparse(opts + [path]) - session = config.initsession() - assert eval(expr, session.config.option.__dict__) - - implied_options = { - '-v': 'verbose', - '-l': 'showlocals', - #'--runbrowser': 'startserver and runbrowser', XXX starts browser - } - for key, expr in implied_options.items(): - yield check_implied_option, [key], expr - def test_default_session_options(self): - def runfiletest(opts): - sorter = suptest.events_from_cmdline(opts) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 2 - assert skipped == passed == 0 - path = self.exampletestfile() - for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], - ['--tb=long'], ['--fulltrace'], ['--nomagic'], - ['--traceconfig'], ['-v'], ['-v', '-v']): - yield runfiletest, opts + [path] - def test_is_not_boxed_by_default(self): - path = self.exampletestfile() - config = py.test.config._reparse([path]) - assert not config.option.boxed +class TestConfigAPI: + @py.test.keywords(issue="ensuretemp should call config.maketemp(basename)") + def test_tmpdir(self): + d1 = py.test.ensuretemp('hello') + d2 = py.test.ensuretemp('hello') + assert d1 == d2 + assert d1.check(dir=1) + + def test_config_getvalue_honours_conftest(self, testdir): + testdir.makepyfile(conftest="x=1") + testdir.mkdir("sub").join("conftest.py").write("x=2 ; y = 3") + config = testdir.parseconfig() + o = testdir.tmpdir + assert config.getvalue("x") == 1 + assert config.getvalue("x", o.join('sub')) == 2 + py.test.raises(KeyError, "config.getvalue('y')") + config = py.test.config._reparse([str(o.join('sub'))]) + assert config.getvalue("x") == 2 + assert config.getvalue("y") == 3 + assert config.getvalue("x", o) == 1 + py.test.raises(KeyError, 'config.getvalue("y", o)') + + def test_config_overwrite(self, testdir): + o = testdir.tmpdir + o.ensure("conftest.py").write("x=1") + config = py.test.config._reparse([str(o)]) + assert config.getvalue('x') == 1 + config.option.x = 2 + assert config.getvalue('x') == 2 + config = py.test.config._reparse([str(o)]) + assert config.getvalue('x') == 1 + def test_getvalue_pathlist(self, tmpdir): + somepath = tmpdir.join("x", "y", "z") + p = tmpdir.join("conftest.py") + p.write("pathlist = ['.', %r]" % str(somepath)) + config = py.test.config._reparse([p]) + assert config.getvalue_pathlist('notexist') is None + pl = config.getvalue_pathlist('pathlist') + print pl + assert len(pl) == 2 + assert pl[0] == tmpdir + assert pl[1] == somepath -class TestConfigColitems(suptest.FileCreation): - def setup_class(cls): - cls.tmproot = py.test.ensuretemp(cls.__name__) + config.option.mypathlist = [py.path.local()] + pl = config.getvalue_pathlist('mypathlist') + assert pl == [py.path.local()] - def setup_method(self, method): - self.tmpdir = self.tmproot.mkdir(method.__name__) - - def test_getcolitems_onedir(self): - config = py.test.config._reparse([self.tmpdir]) - colitems = getcolitems(config) +class TestConfigApi_getcolitems: + def test_getcolitems_onedir(self, tmpdir): + config = py.test.config._reparse([tmpdir]) + colitems = config.getcolitems() assert len(colitems) == 1 col = colitems[0] assert isinstance(col, py.test.collect.Directory) for col in col.listchain(): assert col._config is config - def test_getcolitems_twodirs(self): - config = py.test.config._reparse([self.tmpdir, self.tmpdir]) - colitems = getcolitems(config) + def test_getcolitems_twodirs(self, tmpdir): + config = py.test.config._reparse([tmpdir, tmpdir]) + colitems = config.getcolitems() assert len(colitems) == 2 col1, col2 = colitems assert col1.name == col2.name assert col1.parent == col2.parent - def test_getcolitems_curdir_and_subdir(self): - a = self.tmpdir.ensure("a", dir=1) - config = py.test.config._reparse([self.tmpdir, a]) - colitems = getcolitems(config) + def test_getcolitems_curdir_and_subdir(self, tmpdir): + a = tmpdir.ensure("a", dir=1) + config = py.test.config._reparse([tmpdir, a]) + colitems = config.getcolitems() assert len(colitems) == 2 col1, col2 = colitems - assert col1.name == self.tmpdir.basename + assert col1.name == tmpdir.basename assert col2.name == 'a' for col in colitems: for subcol in col.listchain(): assert col._config is config - def test__getcol_global_file(self): - x = self.tmpdir.ensure("x.py") + def test__getcol_global_file(self, tmpdir): + x = tmpdir.ensure("x.py") config = py.test.config._reparse([x]) col = config.getfsnode(x) assert isinstance(col, py.test.collect.Module) assert col.name == 'x.py' - assert col.parent.name == self.tmpdir.basename + assert col.parent.name == tmpdir.basename assert col.parent.parent is None for col in col.listchain(): assert col._config is config - def test__getcol_global_dir(self): - x = self.tmpdir.ensure("a", dir=1) + def test__getcol_global_dir(self, tmpdir): + x = tmpdir.ensure("a", dir=1) config = py.test.config._reparse([x]) col = config.getfsnode(x) assert isinstance(col, py.test.collect.Directory) @@ -442,9 +171,9 @@ assert col.parent is None assert col._config is config - def test__getcol_pkgfile(self): - x = self.tmpdir.ensure("x.py") - self.tmpdir.ensure("__init__.py") + def test__getcol_pkgfile(self, tmpdir): + x = tmpdir.ensure("x.py") + tmpdir.ensure("__init__.py") config = py.test.config._reparse([x]) col = config.getfsnode(x) assert isinstance(col, py.test.collect.Module) @@ -455,9 +184,162 @@ assert col._config is config - def test_config_picklability(self): + + +class TestOptionEffects: + def test_boxed_option_default(self, testdir): + testdir.makepyfile(conftest="dist_hosts=[]") + tmpdir = testdir.tmpdir.ensure("subdir", dir=1) + config = py.test.config._reparse([tmpdir]) + config.initsession() + assert not config.option.boxed + config = py.test.config._reparse(['--dist', tmpdir]) + config.initsession() + assert not config.option.boxed + + def test_is_not_boxed_by_default(self, testdir): + config = py.test.config._reparse([testdir.tmpdir]) + assert not config.option.boxed + + def test_boxed_option_from_conftest(self, testdir): + testdir.makepyfile(conftest="dist_hosts=[]") + tmpdir = testdir.tmpdir.ensure("subdir", dir=1) + tmpdir.join("conftest.py").write(py.code.Source(""" + dist_hosts = [] + dist_boxed = True + """)) + config = py.test.config._reparse(['--dist', tmpdir]) + config.initsession() + assert config.option.boxed + + def test_boxed_option_from_conftest(self, testdir): + testdir.makepyfile(conftest="dist_boxed=False") + config = py.test.config._reparse([testdir.tmpdir, '--box']) + assert config.option.boxed + config.initsession() + assert config.option.boxed + + def test_config_iocapturing(self, testdir): + config = testdir.parseconfig(testdir.tmpdir) + assert config.getvalue("conf_iocapture") + tmpdir = testdir.tmpdir.ensure("sub-with-conftest", dir=1) + tmpdir.join("conftest.py").write(py.code.Source(""" + conf_iocapture = "no" + """)) + config = py.test.config._reparse([tmpdir]) + assert config.getvalue("conf_iocapture") == "no" + capture = config._getcapture() + assert isinstance(capture, py.io.StdCapture) + assert not capture._out + assert not capture._err + assert not capture._in + assert isinstance(capture, py.io.StdCapture) + for opt, cls in (("sys", py.io.StdCapture), + ("fd", py.io.StdCaptureFD), + ): + config.option.conf_iocapture = opt + capture = config._getcapture() + assert isinstance(capture, cls) + + +class TestConfig_gettopdir: + def test_gettopdir(self, testdir): + from py.__.test.config import gettopdir + tmp = testdir.tmpdir + assert gettopdir([tmp]) == tmp + topdir = gettopdir([tmp.join("hello"), tmp.join("world")]) + assert topdir == tmp + somefile = tmp.ensure("somefile.py") + assert gettopdir([somefile]) == tmp + + def test_gettopdir_pypkg(self, testdir): + from py.__.test.config import gettopdir + tmp = testdir.tmpdir + a = tmp.ensure('a', dir=1) + b = tmp.ensure('a', 'b', '__init__.py') + c = tmp.ensure('a', 'b', 'c.py') + Z = tmp.ensure('Z', dir=1) + assert gettopdir([c]) == a + assert gettopdir([c, Z]) == tmp + +class TestConfigPickling: + @py.test.keywords(xfail=True, issue="config's pytestplugins/bus initialization") + def test_config_initafterpickle_plugin(self, testdir): + testdir.makepyfile(__init__="", conftest="x=1; y=2") + hello = testdir.makepyfile(hello="") + tmp = testdir.tmpdir + config = py.test.config._reparse([hello]) + config2 = py.test.config._reparse([tmp.dirpath()]) + config2._initialized = False # we have to do that from tests + config2._repr = config._makerepr() + config2._initafterpickle(topdir=tmp.dirpath()) + # we check that config "remote" config objects + # have correct plugin initialization + #XXX assert config2.pytestplugins.pm._plugins + #XXX assert config2.bus.isregistered(config2.pytestplugins.forward_event) + assert config2.bus == py._com.pyplugins + assert config2.pytestplugins.pm == py._com.pyplugins + + def test_config_initafterpickle_some(self, testdir): + tmp = testdir.tmpdir + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write("x=1 ; y=2") + hello = tmp.ensure("test_hello.py") + config = py.test.config._reparse([hello]) + config2 = py.test.config._reparse([tmp.dirpath()]) + config2._initialized = False # we have to do that from tests + config2._repr = config._makerepr() + config2._initafterpickle(topdir=tmp.dirpath()) + + for col1, col2 in zip(config.getcolitems(), config2.getcolitems()): + assert col1.fspath == col2.fspath + cols = config2.getcolitems() + assert len(cols) == 1 + col = cols[0] + assert col.name == 'test_hello.py' + assert col.parent.name == tmp.basename + assert col.parent.parent is None + + def test_config_make_and__mergerepr(self, testdir): + tmp = testdir.tmpdir + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write("x=1") + config = py.test.config._reparse([tmp]) + repr = config._makerepr() + config.option.verbose = 42 + repr2 = config._makerepr() + config = py.test.config._reparse([tmp.dirpath()]) + py.test.raises(KeyError, "config.getvalue('x')") + config._mergerepr(repr) + assert config.getvalue('x') == 1 + config._mergerepr(repr2) + assert config.option.verbose == 42 + + def test_config_rconfig(self, testdir): + tmp = testdir.tmpdir + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write(py.code.Source(""" + import py + Option = py.test.config.Option + option = py.test.config.addoptions("testing group", + Option('-G', '--glong', action="store", default=42, + type="int", dest="gdest", help="g value.")) + """)) + config = py.test.config._reparse([tmp, "-G", "11"]) + assert config.option.gdest == 11 + repr = config._makerepr() + config = py.test.config._reparse([tmp.dirpath()]) + py.test.raises(AttributeError, "config.option.gdest") + config._mergerepr(repr) + option = config.addoptions("testing group", + config.Option('-G', '--glong', action="store", default=42, + type="int", dest="gdest", help="g value.")) + assert config.option.gdest == 11 + assert option.gdest == 11 + + def test_config_picklability(self, tmpdir): import cPickle - config = py.test.config._reparse([self.tmpdir]) + config = py.test.config._reparse([tmpdir]) s = cPickle.dumps(config) newconfig = cPickle.loads(s) assert not hasattr(newconfig, "topdir") @@ -466,11 +348,11 @@ newconfig._initafterpickle(config.topdir) assert newconfig.topdir == config.topdir assert newconfig._initialized - assert newconfig.args == [self.tmpdir] + assert newconfig.args == [tmpdir] - def test_config_and_collector_pickling_missing_initafter(self): + def test_config_and_collector_pickling_missing_initafter(self, tmpdir): from cPickle import Pickler, Unpickler - config = py.test.config._reparse([self.tmpdir]) + config = py.test.config._reparse([tmpdir]) col = config.getfsnode(config.topdir) io = py.std.cStringIO.StringIO() pickler = Pickler(io) @@ -482,10 +364,10 @@ # we don't call _initafterpickle ... so py.test.raises(ValueError, "unpickler.load()") - def test_config_and_collector_pickling(self): + def test_config_and_collector_pickling(self, tmpdir): from cPickle import Pickler, Unpickler - dir1 = self.tmpdir.ensure("somedir", dir=1) - config = py.test.config._reparse([self.tmpdir]) + dir1 = tmpdir.ensure("somedir", dir=1) + config = py.test.config._reparse([tmpdir]) col = config.getfsnode(config.topdir) col1 = col.join(dir1.basename) assert col1.parent is col @@ -498,7 +380,7 @@ io.seek(0) unpickler = Unpickler(io) newconfig = unpickler.load() - topdir = self.tmpdir.ensure("newtopdir", dir=1) + topdir = tmpdir.ensure("newtopdir", dir=1) newconfig._initafterpickle(topdir) topdir.ensure("somedir", dir=1) newcol = unpickler.load() @@ -513,3 +395,64 @@ assert newcol2.fspath.basename == dir1.basename assert newcol2.fspath.relto(topdir) +class TestSessionAndOptions: + def test_implied_dsession(self, testdir): + for x in 'startserver runbrowser rest'.split(): + config = testdir.parseconfig(testdir.tmpdir, '--dist', '--%s' % x) + assert config._getestdirname() == 'DSession' + + def test_implied_different_sessions(self, tmpdir): + config = py.test.config._reparse([tmpdir]) + assert config._getestdirname() == 'Session' + config = py.test.config._reparse([tmpdir, '--dist']) + assert config._getestdirname() == 'DSession' + config = py.test.config._reparse([tmpdir, '-n3']) + assert config._getestdirname() == 'DSession' + config = py.test.config._reparse([tmpdir, '--looponfailing']) + assert config._getestdirname() == 'LooponfailingSession' + config = py.test.config._reparse([tmpdir, '--exec=x']) + assert config._getestdirname() == 'DSession' + config = py.test.config._reparse([tmpdir, '--dist', '--exec=x']) + assert config._getestdirname() == 'DSession' + config = py.test.config._reparse([tmpdir, '-f', + '--dist', '--exec=x']) + assert config._getestdirname() == 'LooponfailingSession' + config = py.test.config._reparse([tmpdir, '-f', '-n3', + '--dist', '--exec=x', + '--collectonly']) + assert config._getestdirname() == 'Session' + + def test_sessionname_lookup_custom(self, testdir): + testdir.makepyfile(conftest=""" + from py.__.test.session import Session + class MySession(Session): + pass + """) + config = testdir.parseconfig("--session=MySession", testdir.tmpdir) + session = config.initsession() + assert session.__class__.__name__ == 'MySession' + + def test_initsession(self, tmpdir): + config = py.test.config._reparse([tmpdir]) + session = config.initsession() + assert session.config is config + + def test_default_session_options(self, testdir): + def runfiletest(opts): + sorter = testdir.inline_run(*opts) + passed, skipped, failed = sorter.countoutcomes() + assert failed == 2 + assert skipped == passed == 0 + path = testdir.makepyfile(""" + def test_f1(): assert 0 + def test_f2(): assert 0 + """) + + for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], + ['--tb=long'], ['--fulltrace'], ['--nomagic'], + ['--traceconfig'], ['-v'], ['-v', '-v']): + runfiletest(opts + [path]) + +def test_default_bus(): + assert py.test.config.bus is py._com.pyplugins + Modified: py/trunk/py/test/testing/test_conftesthandle.py ============================================================================== --- py/trunk/py/test/testing/test_conftesthandle.py (original) +++ py/trunk/py/test/testing/test_conftesthandle.py Fri Feb 27 11:18:27 2009 @@ -16,6 +16,15 @@ conftest.setinitial([self.basedir.join("adir")]) assert conftest.rget("a") == 1 + def test_onimport(self): + l = [] + conftest = Conftest(onimport=l.append) + conftest.setinitial([self.basedir.join("adir")]) + assert len(l) == 2 # default + the one + assert conftest.rget("a") == 1 + assert conftest.rget("b", self.basedir.join("adir", "b")) == 2 + assert len(l) == 3 + def test_immediate_initialiation_and_incremental_are_the_same(self): conftest = Conftest() snap0 = len(conftest._path2confmods) Modified: py/trunk/py/test/testing/test_deprecated_api.py ============================================================================== --- py/trunk/py/test/testing/test_deprecated_api.py (original) +++ py/trunk/py/test/testing/test_deprecated_api.py Fri Feb 27 11:18:27 2009 @@ -1,18 +1,17 @@ import py -from py.__.test.testing import suptest -class TestCollectDeprecated(suptest.InlineCollection): - def test_directory_run_join_warnings(self): - p = self.makepyfile(test_one="") - config = self.parseconfig() +class TestCollectDeprecated: + def test_directory_run_join_warnings(self, testdir): + p = testdir.makepyfile(test_one="") + config = testdir.parseconfig(p) dirnode = config.getfsnode(p.dirpath()) py.test.deprecated_call(dirnode.run) # XXX for directories we still have join() #py.test.deprecated_call(dirnode.join, 'test_one') - def test_collect_with_deprecated_run_and_join(self): - self.makepyfile(conftest=""" + def test_collect_with_deprecated_run_and_join(self, testdir): + testdir.makepyfile(conftest=""" import py class MyInstance(py.test.collect.Instance): @@ -46,12 +45,12 @@ return self.Module(self.fspath.join(name), parent=self) Directory = MyDirectory """) - p = self.makepyfile(somefile=""" + p = testdir.makepyfile(somefile=""" def check(): pass class Cls: def check2(self): pass """) - config = self.parseconfig() + config = testdir.parseconfig() dirnode = config.getfsnode(p.dirpath()) colitems = py.test.deprecated_call(dirnode.collect) assert len(colitems) == 1 @@ -69,8 +68,8 @@ assert len(colitems) == 1 assert colitems[0].name == 'check2' - def test_collect_with_deprecated_join_but_no_run(self): - self.makepyfile(conftest=""" + def test_collect_with_deprecated_join_but_no_run(self, testdir): + testdir.makepyfile(conftest=""" import py class Module(py.test.collect.Module): @@ -83,7 +82,7 @@ return self.Function(name, parent=self) assert name != "SomeClass", "join should not be called with this name" """) - col = self.getmodulecol(""" + col = testdir.getmodulecol(""" def somefunc(): pass def check_one(): pass class SomeClass: pass @@ -93,62 +92,62 @@ funcitem = colitems[0] assert funcitem.name == "check_one" - def test_function_custom_run(self): - self.makepyfile(conftest=""" + def test_function_custom_run(self, testdir): + testdir.makepyfile(conftest=""" import py class MyFunction(py.test.collect.Function): def run(self): pass Function=MyFunction """) - modcol = self.getmodulecol("def test_func(): pass") + modcol = testdir.getmodulecol("def test_func(): pass") funcitem = modcol.collect()[0] assert funcitem.name == 'test_func' py.test.deprecated_call(funcitem.runtest) - def test_function_custom_execute(self): - self.makepyfile(conftest=""" + def test_function_custom_execute(self, testdir): + testdir.makepyfile(conftest=""" import py class MyFunction(py.test.collect.Function): def execute(self, obj, *args): pass Function=MyFunction """) - modcol = self.getmodulecol("def test_func(): pass") + modcol = testdir.getmodulecol("def test_func(): pass") funcitem = modcol.collect()[0] assert funcitem.name == 'test_func' py.test.deprecated_call(funcitem.runtest) - def test_function_deprecated_run_execute(self): - modcol = self.getmodulecol("def test_some(): pass") + def test_function_deprecated_run_execute(self, testdir): + modcol = testdir.getmodulecol("def test_some(): pass") funcitem = modcol.collect()[0] py.test.deprecated_call(funcitem.run) py.test.deprecated_call(funcitem.execute, funcitem.obj) - def test_function_deprecated_run_recursive(self): - self.makepyfile(conftest=""" + def test_function_deprecated_run_recursive(self, testdir): + testdir.makepyfile(conftest=""" import py class Module(py.test.collect.Module): def run(self): return super(Module, self).run() """) - modcol = self.getmodulecol("def test_some(): pass") + modcol = testdir.getmodulecol("def test_some(): pass") colitems = py.test.deprecated_call(modcol.collect) funcitem = colitems[0] - def test_conftest_subclasses_Module_with_non_pyfile(self): - self.makepyfile(conftest=""" + def test_conftest_subclasses_Module_with_non_pyfile(self, testdir): + testdir.makepyfile(conftest=""" import py class Module(py.test.collect.Module): def run(self): return [] class Directory(py.test.collect.Directory): - def consider_file(self, path, usefilters=True): + def consider_file(self, path): if path.basename == "testme.xxx": return Module(path, parent=self) - return super(Directory, self).consider_file(path, usefilters=usefilters) + return super(Directory, self).consider_file(path) """) - testme = self._makefile('xxx', testme="hello") - config = self.parseconfig(testme) + testme = testdir.makefile('xxx', testme="hello") + config = testdir.parseconfig(testme) col = config.getfsnode(testme) assert col.collect() == [] Deleted: /py/trunk/py/test/testing/test_doctest.py ============================================================================== --- /py/trunk/py/test/testing/test_doctest.py Fri Feb 27 11:18:27 2009 +++ (empty file) @@ -1,34 +0,0 @@ - -import py -from py.__.test.outcome import Failed -from py.__.test.testing.suptest import InlineCollection - -def setup_module(mod): - mod.tmp = py.test.ensuretemp(__name__) - -class TestDoctests(InlineCollection): - def test_simple_docteststring(self): - txtfile = self.maketxtfile(test_doc=""" - >>> i = 0 - >>> i + 1 - 1 - """) - config = self.parseconfig(txtfile) - col = config.getfsnode(txtfile) - testitem = col.join(txtfile.basename) - res = testitem.runtest() - assert res is None - - - def test_doctest_unexpected_exception(self): - py.test.skip("implement nice doctest repr for unexpected exceptions") - p = tmp.join("test_doctest_unexpected_exception") - p.write(py.code.Source(""" - >>> i = 0 - >>> x - 2 - """)) - testitem = py.test.collect.DoctestFile(p).join(p.basename) - excinfo = py.test.raises(Failed, "testitem.runtest()") - repr = testitem.repr_failure(excinfo, ("", "")) - assert repr.reprlocation Deleted: /py/trunk/py/test/testing/test_event.py ============================================================================== --- /py/trunk/py/test/testing/test_event.py Fri Feb 27 11:18:27 2009 +++ (empty file) @@ -1,61 +0,0 @@ -import py -from py.__.test.event import EventBus -from py.__.test import event -import suptest -from py.__.code.testing.test_excinfo import TWMock - -class TestEventBus: - def test_simple(self): - bus = EventBus() - l = [] - bus.subscribe(l.append) - bus.notify(1) - bus.notify(2) - bus.notify(3) - assert l == [1,2,3] - - def test_multi_sub(self): - bus = EventBus() - l1 = [] - l2 = [] - bus.subscribe(l1.append) - bus.subscribe(l2.append) - bus.notify(1) - bus.notify(2) - bus.notify(3) - assert l1 == [1,2,3] - assert l2 == [1,2,3] - - def test_remove(self): - bus = EventBus() - l = [] - bus.subscribe(l.append) - bus.notify(1) - bus.unsubscribe(l.append) - bus.notify(2) - assert l == [1] - - -class TestItemTestReport(suptest.InlineCollection): - def test_toterminal(self): - sorter = suptest.events_from_runsource(""" - def test_one(): - assert 42 == 43 - """) - reports = sorter.get(event.ItemTestReport) - ev = reports[0] - assert ev.failed - twmock = TWMock() - ev.toterminal(twmock) - assert twmock.lines - twmock = TWMock() - ev.outcome.longrepr = "hello" - ev.toterminal(twmock) - assert twmock.lines[0] == "hello" - assert not twmock.lines[1:] - - ##assert ev.repr_run.find("AssertionError") != -1 - filepath = ev.colitem.fspath - #filepath , modpath = ev.itemrepr_path - assert str(filepath).endswith(".py") - #assert modpath.endswith("file_test.test_one") Modified: py/trunk/py/test/testing/test_recording.py ============================================================================== --- py/trunk/py/test/testing/test_recording.py (original) +++ py/trunk/py/test/testing/test_recording.py Fri Feb 27 11:18:27 2009 @@ -1,12 +1,9 @@ import py,sys py.test.skip("implementation missing: recording") -from py.__.test.testing import suptest -from py.__.test.acceptance_test import AcceptBase - -class TestRecordingAccept(AcceptBase): - def test_recording_and_back(self): - p = self.makepyfile(test_one=""" +class TestRecordingAccept: + def test_recording_and_back(self, testdir): + p = testdir.makepyfile(""" import py def test_fail(): assert x Deleted: /py/trunk/py/test/testing/test_resultlog.py ============================================================================== --- /py/trunk/py/test/testing/test_resultlog.py Fri Feb 27 11:18:27 2009 +++ (empty file) @@ -1,184 +0,0 @@ -import os, StringIO - -import py - -from py.__.test import resultlog -from py.__.test.collect import Node, Item, FSCollector -from py.__.test.event import EventBus -from py.__.test.event import ItemTestReport, CollectionReport -from py.__.test.event import InternalException -from py.__.test.runner import OutcomeRepr - - -class Fake(object): - def __init__(self, **kwds): - self.__dict__.update(kwds) - - -def test_generic_path(): - p1 = Node('a', config='dummy') - assert p1.fspath is None - p2 = Node('B', parent=p1) - p3 = Node('()', parent = p2) - item = Item('c', parent = p3) - - res = resultlog.generic_path(item) - assert res == 'a.B().c' - - p0 = FSCollector('proj/test', config='dummy') - p1 = FSCollector('proj/test/a', parent=p0) - p2 = Node('B', parent=p1) - p3 = Node('()', parent = p2) - p4 = Node('c', parent=p3) - item = Item('[1]', parent = p4) - - res = resultlog.generic_path(item) - assert res == 'test/a:B().c[1]' - - -def make_item(*names): - node = None - config = "dummy" - for name in names[:-1]: - if '/' in name: - node = FSCollector(name, parent=node, config=config) - else: - node = Node(name, parent=node, config=config) - if names[-1] is None: - return node - return Item(names[-1], parent=node) - -class TestResultLog(object): - - def test_create(self): - bus = EventBus() - logfile = object() - - reslog = resultlog.ResultLog(bus, logfile) - assert len(bus._subscribers) == 1 - assert reslog.logfile is logfile - - def test_write_log_entry(self): - reslog = resultlog.ResultLog(EventBus(), None) - - reslog.logfile = StringIO.StringIO() - reslog.write_log_entry('.', 'name', '') - entry = reslog.logfile.getvalue() - assert entry[-1] == '\n' - entry_lines = entry.splitlines() - assert len(entry_lines) == 1 - assert entry_lines[0] == '. name' - - reslog.logfile = StringIO.StringIO() - reslog.write_log_entry('s', 'name', 'Skipped') - entry = reslog.logfile.getvalue() - assert entry[-1] == '\n' - entry_lines = entry.splitlines() - assert len(entry_lines) == 2 - assert entry_lines[0] == 's name' - assert entry_lines[1] == ' Skipped' - - reslog.logfile = StringIO.StringIO() - reslog.write_log_entry('s', 'name', 'Skipped\n') - entry = reslog.logfile.getvalue() - assert entry[-1] == '\n' - entry_lines = entry.splitlines() - assert len(entry_lines) == 2 - assert entry_lines[0] == 's name' - assert entry_lines[1] == ' Skipped' - - reslog.logfile = StringIO.StringIO() - longrepr = ' tb1\n tb 2\nE tb3\nSome Error' - reslog.write_log_entry('F', 'name', longrepr) - entry = reslog.logfile.getvalue() - assert entry[-1] == '\n' - entry_lines = entry.splitlines() - assert len(entry_lines) == 5 - assert entry_lines[0] == 'F name' - assert entry_lines[1:] == [' '+line for line in longrepr.splitlines()] - - def test_log_outcome(self): - reslog = resultlog.ResultLog(EventBus(), StringIO.StringIO()) - - colitem = make_item('some', 'path', 'a', 'b') - - try: - raise ValueError - except ValueError: - the_repr = py.code.ExceptionInfo().getrepr() - - outcome=OutcomeRepr('execute', 'F', the_repr) - ev = Fake(colitem=colitem, outcome=outcome) - - reslog.log_outcome(ev) - - entry = reslog.logfile.getvalue() - entry_lines = entry.splitlines() - - assert entry_lines[0] == 'F some.path.a.b' - assert entry_lines[-1][0] == ' ' - assert 'ValueError' in entry - - def test_item_test_passed(self): - bus = EventBus() - reslog = resultlog.ResultLog(bus, StringIO.StringIO()) - - colitem = make_item('proj/test', 'proj/test/mod', 'a', 'b') - - outcome=OutcomeRepr('execute', '.', '') - rep_ev = ItemTestReport(colitem, passed=outcome) - - bus.notify(rep_ev) - - lines = reslog.logfile.getvalue().splitlines() - assert len(lines) == 1 - line = lines[0] - assert line.startswith(". ") - assert line[2:] == 'test/mod:a.b' - - def test_collection_report(self): - bus = EventBus() - reslog = resultlog.ResultLog(bus, None) - - reslog.logfile = StringIO.StringIO() - colitem = make_item('proj/test', 'proj/test/mod', 'A', None) - outcome=OutcomeRepr('execute', '', '') - rep_ev = CollectionReport(colitem, object(), passed=outcome) - - bus.notify(rep_ev) - - entry = reslog.logfile.getvalue() - assert not entry - - reslog.logfile = StringIO.StringIO() - outcome=OutcomeRepr('execute', 'F', 'Some Error') - rep_ev = CollectionReport(colitem, object(), failed=outcome) - - bus.notify(rep_ev) - - lines = reslog.logfile.getvalue().splitlines() - assert len(lines) == 2 - assert lines[0] == 'F test/mod:A' - - def test_internal_exception(self): - # they are produced for example by a teardown failing - # at the end of the run - bus = EventBus() - reslog = resultlog.ResultLog(bus, StringIO.StringIO()) - - try: - raise ValueError - except ValueError: - excinfo = py.code.ExceptionInfo() - - internal = InternalException(excinfo) - - bus.notify(internal) - - entry = reslog.logfile.getvalue() - entry_lines = entry.splitlines() - - assert entry_lines[0].startswith('! ') - assert os.path.basename(__file__)[:-1] in entry_lines[0] #.py/.pyc - assert entry_lines[-1][0] == ' ' - assert 'ValueError' in entry Modified: py/trunk/py/test/testing/test_runner_functional.py ============================================================================== --- py/trunk/py/test/testing/test_runner_functional.py (original) +++ py/trunk/py/test/testing/test_runner_functional.py Fri Feb 27 11:18:27 2009 @@ -1,34 +1,42 @@ import py -from py.__.test.testing.suptest import InlineCollection from py.__.test.runner import basic_run_report, forked_run_report, basic_collect_report from py.__.test.runner import RobustRun from py.__.code.excinfo import ReprExceptionInfo -class BaseTests(InlineCollection): - def test_passfunction(self): - ev = self.runitem(""" +class BaseTests: + def test_funcattr(self, testdir): + ev = testdir.runitem(""" + import py + @py.test.keywords(xfail="needs refactoring") + def test_func(): + raise Exit() + """) + assert ev.keywords['xfail'] == "needs refactoring" + + def test_passfunction(self, testdir): + ev = testdir.runitem(""" def test_func(): pass """) assert ev.passed assert not ev.failed - assert ev.outcome.shortrepr == "." - assert not ev.outcome.longrepr + assert ev.shortrepr == "." + assert not hasattr(ev, 'longrepr') - def test_failfunction(self): - ev = self.runitem(""" + def test_failfunction(self, testdir): + ev = testdir.runitem(""" def test_func(): assert 0 """) assert not ev.passed assert not ev.skipped assert ev.failed - assert ev.outcome.when == "execute" - assert isinstance(ev.outcome.longrepr, ReprExceptionInfo) - assert str(ev.outcome.shortrepr) == "F" + assert ev.when == "execute" + assert isinstance(ev.longrepr, ReprExceptionInfo) + assert str(ev.shortrepr) == "F" - def test_skipfunction(self): - ev = self.runitem(""" + def test_skipfunction(self, testdir): + ev = testdir.runitem(""" import py def test_func(): py.test.skip("hello") @@ -43,8 +51,8 @@ #assert ev.skipped.location.path #assert not ev.skipped.failurerepr - def test_skip_in_setup_function(self): - ev = self.runitem(""" + def test_skip_in_setup_function(self, testdir): + ev = testdir.runitem(""" import py def setup_function(func): py.test.skip("hello") @@ -59,8 +67,8 @@ #assert ev.skipped.location.lineno == 3 #assert ev.skipped.location.lineno == 3 - def test_failure_in_setup_function(self): - ev = self.runitem(""" + def test_failure_in_setup_function(self, testdir): + ev = testdir.runitem(""" import py def setup_function(func): raise ValueError(42) @@ -71,10 +79,10 @@ assert not ev.skipped assert not ev.passed assert ev.failed - assert ev.outcome.when == "setup" + assert ev.when == "setup" - def test_failure_in_teardown_function(self): - ev = self.runitem(""" + def test_failure_in_teardown_function(self, testdir): + ev = testdir.runitem(""" import py def teardown_function(func): raise ValueError(42) @@ -85,18 +93,18 @@ assert not ev.skipped assert not ev.passed assert ev.failed - #assert ev.outcome.when == "teardown" - #assert ev.outcome.where.lineno == 3 - #assert ev.outcome.entries + assert ev.when == "teardown" + assert ev.longrepr.reprcrash.lineno == 3 + assert ev.longrepr.reprtraceback.reprentries - def test_custom_failure_repr(self): - self.makepyfile(conftest=""" + def test_custom_failure_repr(self, testdir): + testdir.makepyfile(conftest=""" import py class Function(py.test.collect.Function): def repr_failure(self, excinfo, outerr): return "hello" """) - ev = self.runitem(""" + ev = testdir.runitem(""" import py def test_func(): assert 0 @@ -109,14 +117,14 @@ #assert ev.failed.where.path.basename == "test_func.py" #assert ev.failed.failurerepr == "hello" - def test_failure_in_setup_function_ignores_custom_failure_repr(self): - self.makepyfile(conftest=""" + def test_failure_in_setup_function_ignores_custom_failure_repr(self, testdir): + testdir.makepyfile(conftest=""" import py class Function(py.test.collect.Function): def repr_failure(self, excinfo): assert 0 """) - ev = self.runitem(""" + ev = testdir.runitem(""" import py def setup_function(func): raise ValueError(42) @@ -132,8 +140,8 @@ #assert ev.outcome.where.path.basename == "test_func.py" #assert instanace(ev.failed.failurerepr, PythonFailureRepr) - def test_capture_in_func(self): - ev = self.runitem(""" + def test_capture_in_func(self, testdir): + ev = testdir.runitem(""" import py def setup_function(func): print >>py.std.sys.stderr, "in setup" @@ -148,21 +156,21 @@ # assert out == ['in function\nin teardown\n'] # assert err == ['in setup\n'] - def test_systemexit_does_not_bail_out(self): + def test_systemexit_does_not_bail_out(self, testdir): try: - ev = self.runitem(""" + ev = testdir.runitem(""" def test_func(): raise SystemExit(42) """) except SystemExit: py.test.fail("runner did not catch SystemExit") assert ev.failed - assert ev.outcome.when == "execute" + assert ev.when == "execute" - def test_exit_propagates(self): + def test_exit_propagates(self, testdir): from py.__.test.outcome import Exit try: - self.runitem(""" + testdir.runitem(""" from py.__.test.outcome import Exit def test_func(): raise Exit() @@ -172,14 +180,15 @@ else: py.test.fail("did not raise") + class TestExecutionNonForked(BaseTests): def getrunner(self): return basic_run_report - def test_keyboardinterrupt_propagates(self): + def test_keyboardinterrupt_propagates(self, testdir): from py.__.test.outcome import Exit try: - self.runitem(""" + testdir.runitem(""" def test_func(): raise KeyboardInterrupt("fake") """) @@ -188,19 +197,19 @@ else: py.test.fail("did not raise") - def test_pdb_on_fail(self): + def test_pdb_on_fail(self, testdir): l = [] - ev = self.runitem(""" + ev = testdir.runitem(""" def test_func(): assert 0 """, pdb=l.append) assert ev.failed - assert ev.outcome.when == "execute" + assert ev.when == "execute" assert len(l) == 1 - def test_pdb_on_skip(self): + def test_pdb_on_skip(self, testdir): l = [] - ev = self.runitem(""" + ev = testdir.runitem(""" import py def test_func(): py.test.skip("hello") @@ -214,18 +223,18 @@ py.test.skip("no os.fork available") return forked_run_report - def test_suicide(self): - ev = self.runitem(""" + def test_suicide(self, testdir): + ev = testdir.runitem(""" def test_func(): import os os.kill(os.getpid(), 15) """) assert ev.failed - assert ev.outcome.when == "???" + assert ev.when == "???" -class TestCollectionEvent(InlineCollection): - def test_collect_result(self): - col = self.getmodulecol(""" +class TestCollectionEvent: + def test_collect_result(self, testdir): + col = testdir.getmodulecol(""" def test_func1(): pass class TestClass: @@ -240,8 +249,8 @@ assert res[0].name == "test_func1" assert res[1].name == "TestClass" - def test_skip_at_module_scope(self): - col = self.getmodulecol(""" + def test_skip_at_module_scope(self, testdir): + col = testdir.getmodulecol(""" import py py.test.skip("hello") def test_func(): @@ -253,9 +262,9 @@ assert ev.skipped -class TestRunnerRepr(InlineCollection): - def test_runner_repr(self): - item = self.getitem("def test_func(): pass") +class TestRunnerRepr: + def test_runner_repr(self, testdir): + item = testdir.getitem("def test_func(): pass") robustrun = RobustRun(item) r = repr(robustrun) assert r Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Fri Feb 27 11:18:27 2009 @@ -1,91 +1,8 @@ import py -from py.__.test import event -from py.__.test.testing import suptest -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(mod.__name__) - -class TestKeywordSelection(suptest.InlineSession): - def test_select_simple(self): - file_test = self.makepyfile(file_test=""" - def test_one(): assert 0 - class TestClass(object): - def test_method_one(self): - assert 42 == 43 - """) - def check(keyword, name): - sorter = self.parse_and_run("-s", "-k", keyword, file_test) - passed, skipped, failed = sorter.listoutcomes() - assert len(failed) == 1 - assert failed[0].colitem.name == name - assert len(sorter.get(event.Deselected)) == 1 - - for keyword in ['test_one', 'est_on']: - yield check, keyword, 'test_one' - yield check, 'TestClass.test', 'test_method_one' - - def test_select_extra_keywords(self): - o = self.tmpdir - tfile = o.join('test_select.py').write(py.code.Source(""" - def test_1(): - pass - class TestClass: - def test_2(self): - pass - """)) - conftest = o.join('conftest.py').write(py.code.Source(""" - import py - class Class(py.test.collect.Class): - def _keywords(self): - return ['xxx', self.name] - """)) - for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', - 'TestClass test_2', 'xxx TestClass test_2',): - sorter = suptest.events_from_cmdline([o, '-s', '-k', keyword]) - print "keyword", repr(keyword) - passed, skipped, failed = sorter.listoutcomes() - assert len(passed) == 1 - assert passed[0].colitem.name == "test_2" - dlist = sorter.get(event.Deselected) - assert len(dlist) == 1 - assert dlist[0].items[0].name == 'test_1' - - def test_select_starton(self): - threepass = self.makepyfile(test_threepass=""" - def test_one(): assert 1 - def test_two(): assert 1 - def test_three(): assert 1 - """) - sorter = self.parse_and_run("-k", "test_two:", threepass) - passed, skipped, failed = sorter.listoutcomes() - assert len(passed) == 2 - assert not failed - dlist = sorter.get(event.Deselected) - assert len(dlist) == 1 - item = dlist[0].items[0] - assert item.name == "test_one" - -class SessionTests(suptest.InlineCollection): - def events_from_cmdline(self, *args): - paths = [p for p in args if isinstance(p, py.path.local)] - if not paths: - args = (self.tmpdir,) + args - config = self.parseconfig(*args) - self.session = config.initsession() - self.sorter = suptest.EventSorter(config, self.session) - self.session.main() - return self.sorter - - def events_from_runsource(self, source, *args): - p = self.makepyfile(test_source=source) - return self.events_from_cmdline(p, *args) - - def makepyfile(self, *args, **kw): - self.tmpdir.ensure('__init__.py') - return super(SessionTests, self).makepyfile(*args, **kw) - - def test_basic_testitem_events(self): - tfile = self.makepyfile(test_one=""" +class SessionTests: + def test_basic_testitem_events(self, testdir): + tfile = testdir.makepyfile(""" def test_one(): pass def test_one_one(): @@ -95,7 +12,7 @@ def test_two(someargs): pass """) - sorter = self.events_from_cmdline(tfile) + sorter = testdir.inline_run(tfile) passed, skipped, failed = sorter.listoutcomes() assert len(skipped) == 0 assert len(passed) == 1 @@ -103,15 +20,15 @@ assert failed[0].colitem.name == "test_one_one" assert failed[1].colitem.name == "test_other" assert failed[2].colitem.name == "test_two" - itemstarted = sorter.get(event.ItemStart) + itemstarted = sorter.getnamed("itemstart") assert len(itemstarted) == 4 - colstarted = sorter.get(event.CollectionStart) + colstarted = sorter.getnamed("collectionstart") assert len(colstarted) == 1 col = colstarted[0].collector assert isinstance(col, py.test.collect.Module) - def test_nested_import_error(self): - tfile = self.makepyfile(test_one=""" + def test_nested_import_error(self, testdir): + tfile = testdir.makepyfile(""" import import_fails def test_this(): assert import_fails.a == 1 @@ -119,45 +36,44 @@ import does_not_work a = 1 """) - sorter = self.events_from_cmdline() + sorter = testdir.inline_run(tfile) l = sorter.getfailedcollections() assert len(l) == 1 - out = l[0].outcome.longrepr.reprcrash.message + out = l[0].longrepr.reprcrash.message assert out.find('does_not_work') != -1 - def test_raises_output(self): - self.makepyfile(test_one=""" + def test_raises_output(self, testdir): + sorter = testdir.inline_runsource(""" import py def test_raises_doesnt(): py.test.raises(ValueError, int, "3") """) - sorter = self.events_from_cmdline() passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 1 - out = failed[0].outcome.longrepr.reprcrash.message + out = failed[0].longrepr.reprcrash.message if not out.find("DID NOT RAISE") != -1: print out py.test.fail("incorrect raises() output") - def test_generator_yields_None(self): - sorter = self.events_from_runsource(""" + def test_generator_yields_None(self, testdir): + sorter = testdir.inline_runsource(""" def test_1(): yield None """) failures = sorter.getfailedcollections() - out = failures[0].outcome.longrepr.reprcrash.message + out = failures[0].longrepr.reprcrash.message i = out.find('TypeError') assert i != -1 - def test_syntax_error_module(self): - sorter = self.events_from_runsource("this is really not python") + def test_syntax_error_module(self, testdir): + sorter = testdir.inline_runsource("this is really not python") l = sorter.getfailedcollections() assert len(l) == 1 - out = l[0].outcome.longrepr.reprcrash.message + out = l[0].longrepr.reprcrash.message assert out.find(str('not python')) != -1 - def test_exit_first_problem(self): - sorter = self.events_from_runsource(""" + def test_exit_first_problem(self, testdir): + sorter = testdir.inline_runsource(""" def test_one(): assert 0 def test_two(): assert 0 """, '--exitfirst') @@ -165,8 +81,8 @@ assert failed == 1 assert passed == skipped == 0 - def test_broken_repr(self): - self.makepyfile(test_broken=""" + def test_broken_repr(self, testdir): + p = testdir.makepyfile(""" import py class BrokenRepr1: foo=0 @@ -190,18 +106,17 @@ t = BrokenRepr2() assert t.foo == 1 """) - sorter = self.events_from_cmdline() + sorter = testdir.inline_run(p) passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 2 - out = failed[0].outcome.longrepr.reprcrash.message + out = failed[0].longrepr.reprcrash.message assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 #' - out = failed[1].outcome.longrepr.reprcrash.message + out = failed[1].longrepr.reprcrash.message assert (out.find("[unknown exception raised in repr()]") != -1 or out.find("TypeError") != -1) - def test_skip_by_conftest_directory(self): - from py.__.test import outcome - self.makepyfile(conftest=""" + def test_skip_by_conftest_directory(self, testdir): + testdir.makepyfile(conftest=""" import py class Directory(py.test.collect.Directory): def collect(self): @@ -209,14 +124,14 @@ """, test_file=""" def test_one(): pass """) - sorter = self.events_from_cmdline() - skips = sorter.get(event.CollectionReport) + sorter = testdir.inline_run(testdir.tmpdir) + skips = sorter.getnamed("collectionreport") assert len(skips) == 1 assert skips[0].skipped class TestNewSession(SessionTests): - def test_pdb_run(self): - tfile = self.makepyfile(test_one=""" + def test_pdb_run(self, testdir): + tfile = testdir.makepyfile(""" def test_usepdb(): assert 0 """) @@ -225,7 +140,7 @@ l.append(args) py.magic.patch(py.__.test.custompdb, 'post_mortem', mypdb) try: - sorter = self.events_from_cmdline('--pdb') + sorter = testdir.inline_run('--pdb', tfile) finally: py.magic.revert(py.__.test.custompdb, 'post_mortem') rep = sorter.getreport("test_usepdb") @@ -234,8 +149,8 @@ tb = py.code.Traceback(l[0][0]) assert tb[-1].name == "test_usepdb" - def test_order_of_execution(self): - sorter = self.events_from_runsource(""" + def test_order_of_execution(self, testdir): + sorter = testdir.inline_runsource(""" l = [] def test_1(): l.append(1) @@ -259,8 +174,8 @@ assert passed == 7 # also test listnames() here ... - def test_collect_only_with_various_situations(self): - p = self.makepyfile( + def test_collect_only_with_various_situations(self, testdir): + p = testdir.makepyfile( test_one=""" def test_one(): raise ValueError() @@ -276,15 +191,16 @@ import py py.test.skip('xxx') """, - test_three="xxxdsadsadsadsa" + test_three="xxxdsadsadsadsa", + __init__="" ) - sorter = self.events_from_cmdline('--collectonly') + sorter = testdir.inline_run('--collectonly', p.dirpath()) - itemstarted = sorter.get(event.ItemStart) + itemstarted = sorter.getnamed("itemstart") assert len(itemstarted) == 3 - assert not sorter.get(event.ItemTestReport) - started = sorter.get(event.CollectionStart) - finished = sorter.get(event.CollectionReport) + assert not sorter.getnamed("itemtestreport") + started = sorter.getnamed("collectionstart") + finished = sorter.getnamed("collectionreport") assert len(started) == len(finished) assert len(started) == 8 colfail = [x for x in finished if x.failed] Modified: py/trunk/py/test/testing/test_setup_nested.py ============================================================================== --- py/trunk/py/test/testing/test_setup_nested.py (original) +++ py/trunk/py/test/testing/test_setup_nested.py Fri Feb 27 11:18:27 2009 @@ -2,11 +2,8 @@ # test correct setup/teardowns at # module, class, and instance level -import py -import suptest - -def test_module_and_function_setup(): - sorter = suptest.events_from_runsource(""" +def test_module_and_function_setup(testdir): + sorter = testdir.inline_runsource(""" modlevel = [] def setup_module(module): assert not modlevel @@ -35,8 +32,8 @@ rep = sorter.getreport("test_module") assert rep.passed -def test_class_setup(): - sorter = suptest.events_from_runsource(""" +def test_class_setup(testdir): + sorter = testdir.inline_runsource(""" class TestSimpleClassSetup: clslevel = [] def setup_class(cls): @@ -58,8 +55,8 @@ """) sorter.assertoutcome(passed=1+2+1) -def test_method_setup(): - sorter = suptest.events_from_runsource(""" +def test_method_setup(testdir): + sorter = testdir.inline_runsource(""" class TestSetupMethod: def setup_method(self, meth): self.methsetup = meth @@ -74,8 +71,8 @@ """) sorter.assertoutcome(passed=2) -def test_method_generator_setup(): - sorter = suptest.events_from_runsource(""" +def test_method_generator_setup(testdir): + sorter = testdir.inline_runsource(""" class TestSetupTeardownOnInstance: def setup_class(cls): cls.classsetup = True @@ -96,8 +93,8 @@ """) sorter.assertoutcome(passed=1, failed=1) -def test_func_generator_setup(): - sorter = suptest.events_from_runsource(""" +def test_func_generator_setup(testdir): + sorter = testdir.inline_runsource(""" import sys def setup_module(mod): @@ -124,8 +121,8 @@ rep = sorter.getreport("test_one") assert rep.passed -def test_method_setup_uses_fresh_instances(): - sorter = suptest.events_from_runsource(""" +def test_method_setup_uses_fresh_instances(testdir): + sorter = testdir.inline_runsource(""" class TestSelfState1: def __init__(self): self.hello = 42 From hpk at codespeak.net Fri Feb 27 16:45:31 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 27 Feb 2009 16:45:31 +0100 (CET) Subject: [py-svn] r62244 - py/trunk/py/test/testing Message-ID: <20090227154531.D21A416852C@codespeak.net> Author: hpk Date: Fri Feb 27 16:45:31 2009 New Revision: 62244 Modified: py/trunk/py/test/testing/test_config.py Log: fixed wrongly renamed methods Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Fri Feb 27 16:45:31 2009 @@ -399,28 +399,28 @@ def test_implied_dsession(self, testdir): for x in 'startserver runbrowser rest'.split(): config = testdir.parseconfig(testdir.tmpdir, '--dist', '--%s' % x) - assert config._getestdirname() == 'DSession' + assert config._getsessionname() == 'DSession' def test_implied_different_sessions(self, tmpdir): config = py.test.config._reparse([tmpdir]) - assert config._getestdirname() == 'Session' + assert config._getsessionname() == 'Session' config = py.test.config._reparse([tmpdir, '--dist']) - assert config._getestdirname() == 'DSession' + assert config._getsessionname() == 'DSession' config = py.test.config._reparse([tmpdir, '-n3']) - assert config._getestdirname() == 'DSession' + assert config._getsessionname() == 'DSession' config = py.test.config._reparse([tmpdir, '--looponfailing']) - assert config._getestdirname() == 'LooponfailingSession' + assert config._getsessionname() == 'LooponfailingSession' config = py.test.config._reparse([tmpdir, '--exec=x']) - assert config._getestdirname() == 'DSession' + assert config._getsessionname() == 'DSession' config = py.test.config._reparse([tmpdir, '--dist', '--exec=x']) - assert config._getestdirname() == 'DSession' + assert config._getsessionname() == 'DSession' config = py.test.config._reparse([tmpdir, '-f', '--dist', '--exec=x']) - assert config._getestdirname() == 'LooponfailingSession' + assert config._getsessionname() == 'LooponfailingSession' config = py.test.config._reparse([tmpdir, '-f', '-n3', '--dist', '--exec=x', '--collectonly']) - assert config._getestdirname() == 'Session' + assert config._getsessionname() == 'Session' def test_sessionname_lookup_custom(self, testdir): testdir.makepyfile(conftest=""" From hpk at codespeak.net Fri Feb 27 16:46:00 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 27 Feb 2009 16:46:00 +0100 (CET) Subject: [py-svn] r62245 - in py/trunk/py/test: . looponfail Message-ID: <20090227154600.95ED716852C@codespeak.net> Author: hpk Date: Fri Feb 27 16:46:00 2009 New Revision: 62245 Modified: py/trunk/py/test/config.py py/trunk/py/test/looponfail/remote.py py/trunk/py/test/pytestplugin.py Log: fixed more wrong renames, fine-tuning Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Fri Feb 27 16:46:00 2009 @@ -1,5 +1,3 @@ -from __future__ import generators - import py from conftesthandle import Conftest @@ -159,19 +157,19 @@ def initsession(self): """ return an initialized session object. """ - cls = self._getestdirclass() + cls = self._getsessionclass() session = cls(self) session.fixoptions() return session - def _getestdirclass(self): + def _getsessionclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. """ if self.option.session is not None: return self._conftest.rget(self.option.session) else: - name = self._getestdirname() + name = self._getsessionname() try: return self._conftest.rget(name) except KeyError: @@ -180,7 +178,7 @@ mod = __import__(importpath, None, None, '__doc__') return getattr(mod, name) - def _getestdirname(self): + def _getsessionname(self): """ return default session name as determined from options. """ if self.option.collectonly: name = 'Session' Modified: py/trunk/py/test/looponfail/remote.py ============================================================================== --- py/trunk/py/test/looponfail/remote.py (original) +++ py/trunk/py/test/looponfail/remote.py Fri Feb 27 16:46:00 2009 @@ -31,11 +31,10 @@ while 1: self.loop_once(loopstate) if not loopstate.colitems and loopstate.wasfailing: - continue # rerun immediately + continue # the last failures passed, let's rerun all self.statrecorder.waitonchange(checkinterval=2.0) except KeyboardInterrupt: print - pass def loop_once(self, loopstate): colitems = loopstate.colitems Modified: py/trunk/py/test/pytestplugin.py ============================================================================== --- py/trunk/py/test/pytestplugin.py (original) +++ py/trunk/py/test/pytestplugin.py Fri Feb 27 16:46:00 2009 @@ -120,4 +120,4 @@ try: return __import__("py.__.test.plugin.%s" %(importspec), None, None, '__doc__') except ImportError: - raise ImportError(importspec) + return __import__(importspec) # show the original exception From hpk at codespeak.net Fri Feb 27 20:36:28 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 27 Feb 2009 20:36:28 +0100 (CET) Subject: [py-svn] r62251 - py/trunk/py/bin Message-ID: <20090227193628.74625168551@codespeak.net> Author: hpk Date: Fri Feb 27 20:36:28 2009 New Revision: 62251 Modified: py/trunk/py/bin/py.countloc (props changed) py/trunk/py/bin/py.lookup (props changed) py/trunk/py/bin/py.rest (props changed) py/trunk/py/bin/py.test (props changed) Log: setting svn:executable From hpk at codespeak.net Fri Feb 27 20:56:53 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 27 Feb 2009 20:56:53 +0100 (CET) Subject: [py-svn] r62252 - in py/trunk/py: . doc misc/testing test test/plugin test/testing Message-ID: <20090227195653.BC799168531@codespeak.net> Author: hpk Date: Fri Feb 27 20:56:51 2009 New Revision: 62252 Modified: py/trunk/py/__init__.py py/trunk/py/doc/test.txt py/trunk/py/misc/testing/test_com.py py/trunk/py/test/outcome.py py/trunk/py/test/plugin/pytest_xfail.py py/trunk/py/test/testing/test_config.py py/trunk/py/test/testing/test_outcome.py py/trunk/py/test/testing/test_pytestplugin.py py/trunk/py/test/testing/test_runner_functional.py Log: renaming "py.test.keywords" to "py.test.mark". Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Fri Feb 27 20:56:51 2009 @@ -73,7 +73,7 @@ 'test.__doc__' : ('./test/__init__.py', '__doc__'), 'test._PytestPlugins' : ('./test/pytestplugin.py', 'PytestPlugins'), 'test.raises' : ('./test/outcome.py', 'raises'), - 'test.keywords' : ('./test/outcome.py', 'keywords',), + 'test.mark' : ('./test/outcome.py', 'mark',), 'test.deprecated_call' : ('./test/outcome.py', 'deprecated_call'), 'test.skip' : ('./test/outcome.py', 'skip'), 'test.importorskip' : ('./test/outcome.py', 'importorskip'), Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Fri Feb 27 20:56:51 2009 @@ -166,7 +166,7 @@ of keywords for a given test. You may specify additional kewords like this:: - @py.test.keywords("webtest") + @py.test.mark(webtest=True) def test_send_http(): ... Modified: py/trunk/py/misc/testing/test_com.py ============================================================================== --- py/trunk/py/misc/testing/test_com.py (original) +++ py/trunk/py/misc/testing/test_com.py Fri Feb 27 20:56:51 2009 @@ -72,7 +72,7 @@ assert not plugins.isregistered(my) assert plugins.getplugins() == [my2] - #@py.test.keywords(xfail=True) + #@py.test.mark.xfail def test_onregister(self): py.test.skip("implement exitfirst plugin and " "modify xfail plugin to override exitfirst behaviour?") Modified: py/trunk/py/test/outcome.py ============================================================================== --- py/trunk/py/test/outcome.py (original) +++ py/trunk/py/test/outcome.py Fri Feb 27 20:56:51 2009 @@ -139,14 +139,34 @@ raise AssertionError("%r did not produce DeprecationWarning" %(func,)) return ret -class keywords: +class KeywordDecorator: """ decorator for setting function attributes. """ - def __init__(self, **kw): - self.kw = kw - def __call__(self, func): - func.func_dict.update(self.kw) + 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): + kw = self._keywords.copy() + kw[name] = True + return self.__class__(kw, lastname=name) + +mark = KeywordDecorator({}) + # exitcodes for the command line EXIT_OK = 0 EXIT_TESTSFAILED = 1 Modified: py/trunk/py/test/plugin/pytest_xfail.py ============================================================================== --- py/trunk/py/test/plugin/pytest_xfail.py (original) +++ py/trunk/py/test/plugin/pytest_xfail.py Fri Feb 27 20:56:51 2009 @@ -1,6 +1,6 @@ """ for marking and reporting "expected to fail" tests. - @py.test.keywords(xfail="needs refactoring") + @py.test.mark(xfail="needs refactoring") def test_hello(): ... assert 0 @@ -51,7 +51,7 @@ p = testdir.makepyfile(test_one=""" import py pytest_plugins="pytest_xfail", - @py.test.keywords(xfail=True) + @py.test.mark.xfail def test_this(): assert 0 """) Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Fri Feb 27 20:56:51 2009 @@ -3,7 +3,7 @@ pytest_plugins = 'pytest_iocapture' class TestConfigCmdlineParsing: - @py.test.keywords(xfail="commit parser") + @py.test.mark(xfail="commit parser") def test_config_addoption(self, stdcapture): from py.__.test.config import Config config = Config() @@ -73,7 +73,7 @@ class TestConfigAPI: - @py.test.keywords(issue="ensuretemp should call config.maketemp(basename)") + @py.test.mark(issue="ensuretemp should call config.maketemp(basename)") def test_tmpdir(self): d1 = py.test.ensuretemp('hello') d2 = py.test.ensuretemp('hello') @@ -263,7 +263,7 @@ assert gettopdir([c, Z]) == tmp class TestConfigPickling: - @py.test.keywords(xfail=True, issue="config's pytestplugins/bus initialization") + @py.test.mark(xfail=True, issue="config's pytestplugins/bus initialization") def test_config_initafterpickle_plugin(self, testdir): testdir.makepyfile(__init__="", conftest="x=1; y=2") hello = testdir.makepyfile(hello="") Modified: py/trunk/py/test/testing/test_outcome.py ============================================================================== --- py/trunk/py/test/testing/test_outcome.py (original) +++ py/trunk/py/test/testing/test_outcome.py Fri Feb 27 20:56:51 2009 @@ -81,3 +81,22 @@ print py.code.ExceptionInfo() py.test.fail("spurious skip") + +def test_pytest_mark(): + from py.__.test.outcome import mark + 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" + Modified: py/trunk/py/test/testing/test_pytestplugin.py ============================================================================== --- py/trunk/py/test/testing/test_pytestplugin.py (original) +++ py/trunk/py/test/testing/test_pytestplugin.py Fri Feb 27 20:56:51 2009 @@ -248,7 +248,7 @@ assert not plugins.listattr("hello") assert plugins.listattr("x") == [42] - @py.test.keywords(xfail="implement setupcall") + @py.test.mark(xfail="implement setupcall") def test_call_setup_participants(self, testdir): testdir.makepyfile( conftest=""" Modified: py/trunk/py/test/testing/test_runner_functional.py ============================================================================== --- py/trunk/py/test/testing/test_runner_functional.py (original) +++ py/trunk/py/test/testing/test_runner_functional.py Fri Feb 27 20:56:51 2009 @@ -7,7 +7,7 @@ def test_funcattr(self, testdir): ev = testdir.runitem(""" import py - @py.test.keywords(xfail="needs refactoring") + @py.test.mark(xfail="needs refactoring") def test_func(): raise Exit() """) From hpk at codespeak.net Fri Feb 27 22:32:50 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 27 Feb 2009 22:32:50 +0100 (CET) Subject: [py-svn] r62256 - in py/trunk/py/test: . dsession dsession/testing looponfail plugin testing Message-ID: <20090227213250.87BDB168494@codespeak.net> Author: hpk Date: Fri Feb 27 22:32:49 2009 New Revision: 62256 Modified: py/trunk/py/test/config.py py/trunk/py/test/dsession/dsession.py py/trunk/py/test/dsession/masterslave.py py/trunk/py/test/dsession/testing/test_dsession.py py/trunk/py/test/dsession/testing/test_functional_dsession.py py/trunk/py/test/looponfail/remote.py py/trunk/py/test/plugin/pytest_default.py py/trunk/py/test/testing/test_config.py py/trunk/py/test/testing/test_session.py Log: getting rid of "--session", you can do it by creating a plugin and calling setsessionclass from "pytest_configure". Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Fri Feb 27 22:32:49 2009 @@ -26,6 +26,7 @@ """ central bus for dealing with configuration/initialization data. """ Option = py.compat.optparse.Option # deprecated _initialized = False + _sessionclass = None def __init__(self, pytestplugins=None): self.option = CmdOptions() @@ -79,7 +80,6 @@ self.topdir = py.path.local(topdir) self._mergerepr(self._repr) del self._repr - self.pytestplugins.configure(config=self) def _makerepr(self): l = [] @@ -155,40 +155,19 @@ except AttributeError: return self._conftest.rget(name, path) + def setsessionclass(self, cls): + if self._sessionclass is not None: + raise ValueError("sessionclass already set to: %r" %( + self._sessionclass)) + self._sessionclass = cls + def initsession(self): """ return an initialized session object. """ - cls = self._getsessionclass() - session = cls(self) - session.fixoptions() - return session - - def _getsessionclass(self): - """ return Session class determined from cmdline options - and looked up in initial config modules. - """ - if self.option.session is not None: - return self._conftest.rget(self.option.session) - else: - name = self._getsessionname() - try: - return self._conftest.rget(name) - except KeyError: - pass - importpath = globals()[name] - mod = __import__(importpath, None, None, '__doc__') - return getattr(mod, name) - - def _getsessionname(self): - """ return default session name as determined from options. """ - if self.option.collectonly: - name = 'Session' - elif self.option.looponfailing: - name = 'LooponfailingSession' - elif self.option.numprocesses or self.option.dist or self.option.executable: - name = 'DSession' - else: - name = 'Session' - return name + cls = self._sessionclass + if cls is None: + from py.__.test.session import Session + cls = Session + return cls(self) def _reparse(self, args): """ this is used from tests that want to re-invoke parse(). """ @@ -222,12 +201,6 @@ pytestplugins=py.test._PytestPlugins(py._com.pyplugins) ) -# default import paths for sessions - -Session = 'py.__.test.session' -LooponfailingSession = 'py.__.test.looponfail.remote' -DSession = 'py.__.test.dsession.dsession' - # # helpers # Modified: py/trunk/py/test/dsession/dsession.py ============================================================================== --- py/trunk/py/test/dsession/dsession.py (original) +++ py/trunk/py/test/dsession/dsession.py Fri Feb 27 22:32:49 2009 @@ -41,6 +41,9 @@ self.host2pending = {} self.item2host = {} self._testsfailed = False + if self.config.getvalue("executable") and \ + not self.config.getvalue("numprocesses"): + self.config.option.numprocesses = 1 def fixoptions(self): """ check, fix and determine conflicting options. """ Modified: py/trunk/py/test/dsession/masterslave.py ============================================================================== --- py/trunk/py/test/dsession/masterslave.py (original) +++ py/trunk/py/test/dsession/masterslave.py Fri Feb 27 22:32:49 2009 @@ -113,6 +113,7 @@ self.channel.send((eventname, args, kwargs)) def run(self): + self.config.pytestplugins.configure(self.config) from py.__.test.dsession.hostmanage import makehostup channel = self.channel self.host = host = channel.receive() Modified: py/trunk/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/trunk/py/test/dsession/testing/test_dsession.py (original) +++ py/trunk/py/test/dsession/testing/test_dsession.py Fri Feb 27 22:32:49 2009 @@ -26,6 +26,7 @@ class TestDSession: def test_fixoptions(self, testdir): config = testdir.parseconfig("--exec=xxx") + config.pytestplugins.configure(config) config.initsession().fixoptions() assert config.option.numprocesses == 1 config = testdir.parseconfig("--exec=xxx", '-n3') Modified: py/trunk/py/test/dsession/testing/test_functional_dsession.py ============================================================================== --- py/trunk/py/test/dsession/testing/test_functional_dsession.py (original) +++ py/trunk/py/test/dsession/testing/test_functional_dsession.py Fri Feb 27 22:32:49 2009 @@ -11,10 +11,6 @@ class TestAsyncFunctional: - def test_dist_no_disthost(self, testdir): - config = testdir.parseconfig(testdir.tmpdir, '-d') - py.test.raises(SystemExit, "config.initsession()") - def test_conftest_options(self, testdir): testdir.makepyfile(conftest=""" print "importing conftest" Modified: py/trunk/py/test/looponfail/remote.py ============================================================================== --- py/trunk/py/test/looponfail/remote.py (original) +++ py/trunk/py/test/looponfail/remote.py Fri Feb 27 22:32:49 2009 @@ -129,7 +129,7 @@ config.option.usepdb = False config.option.executable = None trails = channel.receive() - + config.pytestplugins.configure(config) DEBUG("SLAVE: initsession()") session = config.initsession() # XXX configure the reporter object's terminal writer more directly Modified: py/trunk/py/test/plugin/pytest_default.py ============================================================================== --- py/trunk/py/test/plugin/pytest_default.py (original) +++ py/trunk/py/test/plugin/pytest_default.py Fri Feb 27 22:32:49 2009 @@ -1,3 +1,5 @@ +import py + class DefaultPlugin: """ Plugin implementing defaults and general options. """ @@ -72,12 +74,40 @@ action="store_true", dest="runbrowser", default=False, help="run browser (implies --startserver)." ), - group._addoption('', '--boxed', + group._addoption('--boxed', action="store_true", dest="boxed", default=False, help="box each test run in a separate process"), - group._addoption('', '--rest', + group._addoption('--rest', action='store_true', dest="restreport", default=False, help="restructured text output reporting."), - group._addoption('', '--session', - action="store", dest="session", default=None, - help="lookup given sessioname in conftest.py files and use it."), + + def pytest_configure(self, config): + self.setsession(config) + + def setsession(self, config): + val = config.getvalue + if val("collectonly"): + from py.__.test.session import Session + config.setsessionclass(Session) + elif val("looponfailing"): + from py.__.test.looponfail.remote import LooponfailingSession + config.setsessionclass(LooponfailingSession) + elif val("numprocesses") or val("dist") or val("executable"): + from py.__.test.dsession.dsession import DSession + config.setsessionclass(DSession) + +def test_implied_different_sessions(tmpdir): + def x(*args): + config = py.test.config._reparse([tmpdir] + list(args)) + try: + config.pytestplugins.configure(config) + except ValueError: + return Exception + return getattr(config._sessionclass, '__name__', None) + assert x() == None + assert x('--dist') == 'DSession' + assert x('-n3') == 'DSession' + assert x('-f') == 'LooponfailingSession' + assert x('--exec=x') == 'DSession' + assert x('-f', '--exec=x') == 'LooponfailingSession' + assert x('--dist', '--exec=x', '--collectonly') == 'Session' Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Fri Feb 27 22:32:49 2009 @@ -1,20 +1,6 @@ import py -pytest_plugins = 'pytest_iocapture' - class TestConfigCmdlineParsing: - @py.test.mark(xfail="commit parser") - def test_config_addoption(self, stdcapture): - from py.__.test.config import Config - config = Config() - config.addoption("cat1", "--option1", action="store_true") - config.addoption("cat1", "--option2", action="store_true") - config.parse(["-h"]) - out, err = stdcapture.reset() - assert out.count("cat1") == 1 - assert out.find("option1") != -1 - assert out.find("option2") != -1 - def test_config_cmdline_options(self, testdir): testdir.makepyfile(conftest=""" import py @@ -71,9 +57,8 @@ opts = spec.split() yield check_conflict_option, opts - class TestConfigAPI: - @py.test.mark(issue="ensuretemp should call config.maketemp(basename)") + @py.test.mark.issue("ensuretemp should call config.maketemp(basename)") def test_tmpdir(self): d1 = py.test.ensuretemp('hello') d2 = py.test.ensuretemp('hello') @@ -120,6 +105,20 @@ pl = config.getvalue_pathlist('mypathlist') assert pl == [py.path.local()] + def test_setsessionclass_and_initsession(self, testdir): + from py.__.test.config import Config + config = Config() + class Session1: + def __init__(self, config): + self.config = config + config.setsessionclass(Session1) + session = config.initsession() + assert isinstance(session, Session1) + assert session.config is config + py.test.raises(ValueError, "config.setsessionclass(Session1)") + + + class TestConfigApi_getcolitems: def test_getcolitems_onedir(self, tmpdir): config = py.test.config._reparse([tmpdir]) @@ -395,63 +394,21 @@ assert newcol2.fspath.basename == dir1.basename assert newcol2.fspath.relto(topdir) -class TestSessionAndOptions: - def test_implied_dsession(self, testdir): - for x in 'startserver runbrowser rest'.split(): - config = testdir.parseconfig(testdir.tmpdir, '--dist', '--%s' % x) - assert config._getsessionname() == 'DSession' - - def test_implied_different_sessions(self, tmpdir): - config = py.test.config._reparse([tmpdir]) - assert config._getsessionname() == 'Session' - config = py.test.config._reparse([tmpdir, '--dist']) - assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([tmpdir, '-n3']) - assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([tmpdir, '--looponfailing']) - assert config._getsessionname() == 'LooponfailingSession' - config = py.test.config._reparse([tmpdir, '--exec=x']) - assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([tmpdir, '--dist', '--exec=x']) - assert config._getsessionname() == 'DSession' - config = py.test.config._reparse([tmpdir, '-f', - '--dist', '--exec=x']) - assert config._getsessionname() == 'LooponfailingSession' - config = py.test.config._reparse([tmpdir, '-f', '-n3', - '--dist', '--exec=x', - '--collectonly']) - assert config._getsessionname() == 'Session' - - def test_sessionname_lookup_custom(self, testdir): - testdir.makepyfile(conftest=""" - from py.__.test.session import Session - class MySession(Session): - pass - """) - config = testdir.parseconfig("--session=MySession", testdir.tmpdir) - session = config.initsession() - assert session.__class__.__name__ == 'MySession' - - def test_initsession(self, tmpdir): - config = py.test.config._reparse([tmpdir]) - session = config.initsession() - assert session.config is config - - def test_default_session_options(self, testdir): - def runfiletest(opts): - sorter = testdir.inline_run(*opts) - passed, skipped, failed = sorter.countoutcomes() - assert failed == 2 - assert skipped == passed == 0 - path = testdir.makepyfile(""" - def test_f1(): assert 0 - def test_f2(): assert 0 - """) - - for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], - ['--tb=long'], ['--fulltrace'], ['--nomagic'], - ['--traceconfig'], ['-v'], ['-v', '-v']): - runfiletest(opts + [path]) +def test_options_on_small_file_do_not_blow_up(testdir): + def runfiletest(opts): + sorter = testdir.inline_run(*opts) + passed, skipped, failed = sorter.countoutcomes() + assert failed == 2 + assert skipped == passed == 0 + path = testdir.makepyfile(""" + def test_f1(): assert 0 + def test_f2(): assert 0 + """) + + for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], + ['--tb=long'], ['--fulltrace'], ['--nomagic'], + ['--traceconfig'], ['-v'], ['-v', '-v']): + runfiletest(opts + [path]) def test_default_bus(): assert py.test.config.bus is py._com.pyplugins Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Fri Feb 27 22:32:49 2009 @@ -1,6 +1,11 @@ import py class SessionTests: + def test_initsession(self, tmpdir): + config = py.test.config._reparse([tmpdir]) + session = config.initsession() + assert session.config is config + def test_basic_testitem_events(self, testdir): tfile = testdir.makepyfile(""" def test_one(): From py-svn at codespeak.net Sat Feb 28 13:22:47 2009 From: py-svn at codespeak.net (py-svn at codespeak.net) Date: Sat, 28 Feb 2009 13:22:47 +0100 (CET) Subject: [py-svn] Want your rod to stay? Message-ID: <20090228122247.683B1168446@codespeak.net> An HTML attachment was scrubbed... URL: