plugin cleanups
* make pytest_eventlog.py work again by adding a hack to the registry, rename * disable resultdb hook plugin, it needs merging with resultlog * add some docstrings, streamline bits --HG-- branch : trunk
This commit is contained in:
		
							parent
							
								
									219e627f87
								
							
						
					
					
						commit
						d1f24aa251
					
				|  | @ -63,6 +63,7 @@ class Registry: | ||||||
|     """ |     """ | ||||||
|         Manage Plugins: Load plugins and manage calls to plugins.  |         Manage Plugins: Load plugins and manage calls to plugins.  | ||||||
|     """ |     """ | ||||||
|  |     logfile = None | ||||||
|     MultiCall = MultiCall |     MultiCall = MultiCall | ||||||
| 
 | 
 | ||||||
|     def __init__(self, plugins=None): |     def __init__(self, plugins=None): | ||||||
|  | @ -130,6 +131,11 @@ class HookCall: | ||||||
|                             "for api call to %r" % self.name) |                             "for api call to %r" % self.name) | ||||||
|         attr = self.registry.listattr(self.name, extra=self.extralookup) |         attr = self.registry.listattr(self.name, extra=self.extralookup) | ||||||
|         mc = MultiCall(attr, **kwargs) |         mc = MultiCall(attr, **kwargs) | ||||||
|  |         # XXX this should be doable from a hook impl: | ||||||
|  |         if self.registry.logfile: | ||||||
|  |             self.registry.logfile.write("%s(**%s) # firstresult=%s\n" % | ||||||
|  |                 (self.name, kwargs, self.firstresult)) | ||||||
|  |             self.registry.logfile.flush() | ||||||
|         return mc.execute(firstresult=self.firstresult) |         return mc.execute(firstresult=self.firstresult) | ||||||
| 
 | 
 | ||||||
| comregistry = Registry() | comregistry = Registry() | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ def pytest_funcarg__testdir(request): | ||||||
|     basename = request.module.__name__.split(".")[-1]  |     basename = request.module.__name__.split(".")[-1]  | ||||||
|     if basename.startswith("pytest_"): |     if basename.startswith("pytest_"): | ||||||
|         testdir.plugins.append(vars(request.module)) |         testdir.plugins.append(vars(request.module)) | ||||||
|  |         testdir.plugins.append(basename)  | ||||||
|     else: |     else: | ||||||
|         pass # raise ValueError("need better support code") |         pass # raise ValueError("need better support code") | ||||||
|     return testdir |     return testdir | ||||||
|  |  | ||||||
|  | @ -1,4 +1,9 @@ | ||||||
|  | """ | ||||||
|  | automatically collect and execute doctests.  | ||||||
|  | """ | ||||||
|  | 
 | ||||||
| import py | import py | ||||||
|  | from py.__.code.excinfo import Repr, ReprFileLocation | ||||||
| 
 | 
 | ||||||
| def pytest_addoption(parser): | def pytest_addoption(parser): | ||||||
|     group = parser.addgroup("doctest options") |     group = parser.addgroup("doctest options") | ||||||
|  | @ -13,8 +18,6 @@ def pytest_collect_file(path, parent): | ||||||
|     if path.check(fnmatch="test_*.txt"): |     if path.check(fnmatch="test_*.txt"): | ||||||
|         return DoctestTextfile(path, parent) |         return DoctestTextfile(path, parent) | ||||||
| 
 | 
 | ||||||
| from py.__.code.excinfo import Repr, ReprFileLocation |  | ||||||
| 
 |  | ||||||
| class ReprFailDoctest(Repr): | class ReprFailDoctest(Repr): | ||||||
|     def __init__(self, reprlocation, lines): |     def __init__(self, reprlocation, lines): | ||||||
|         self.reprlocation = reprlocation |         self.reprlocation = reprlocation | ||||||
|  |  | ||||||
|  | @ -1,45 +0,0 @@ | ||||||
| import py |  | ||||||
| 
 |  | ||||||
| """ expected to fail.  |  | ||||||
| """ |  | ||||||
| 
 |  | ||||||
| class EventlogPlugin: |  | ||||||
|     """ log pytest events to a file. """ |  | ||||||
|     def pytest_addoption(self, parser): |  | ||||||
|         parser.addoption("--eventlog", dest="eventlog",  |  | ||||||
|             help="write all pytest events to the given file.") |  | ||||||
| 
 |  | ||||||
|     def pytest_configure(self, config): |  | ||||||
|         eventlog = config.getvalue("eventlog") |  | ||||||
|         if eventlog: |  | ||||||
|             self.eventlogfile = open(eventlog, 'w') |  | ||||||
| 
 |  | ||||||
|     def pytest_unconfigure(self, config): |  | ||||||
|         if hasattr(self, 'eventlogfile'): |  | ||||||
|             self.eventlogfile.close() |  | ||||||
|             del self.eventlogfile |  | ||||||
| 
 |  | ||||||
|     def pyevent(self, eventname, args, kwargs): |  | ||||||
|         if hasattr(self, 'eventlogfile'): |  | ||||||
|             f = self.eventlogfile |  | ||||||
|             print >>f, eventname, args, kwargs |  | ||||||
|             f.flush() |  | ||||||
| 
 |  | ||||||
| # =============================================================================== |  | ||||||
| # plugin tests  |  | ||||||
| # =============================================================================== |  | ||||||
| 
 |  | ||||||
| @py.test.mark.xfail |  | ||||||
| def test_generic(plugintester): |  | ||||||
|     plugintester.hookcheck() |  | ||||||
| 
 |  | ||||||
|     testdir = plugintester.testdir() |  | ||||||
|     testdir.makepyfile(""" |  | ||||||
|         def test_pass(): |  | ||||||
|             pass |  | ||||||
|     """) |  | ||||||
|     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 |  | ||||||
|  | @ -1,3 +1,6 @@ | ||||||
|  | """ | ||||||
|  | cleanup gateways that were instantiated during a test function run.  | ||||||
|  | """ | ||||||
| import py | import py | ||||||
| 
 | 
 | ||||||
| def pytest_configure(config): | def pytest_configure(config): | ||||||
|  |  | ||||||
|  | @ -1,3 +1,6 @@ | ||||||
|  | """ | ||||||
|  | write and report coverage data using the 'figleaf' module.  | ||||||
|  | """ | ||||||
| import py | import py | ||||||
| 
 | 
 | ||||||
| figleaf = py.test.importorskip("figleaf") | figleaf = py.test.importorskip("figleaf") | ||||||
|  |  | ||||||
|  | @ -0,0 +1,36 @@ | ||||||
|  | """ log calling of plugin hooks to a file. """  | ||||||
|  | import py | ||||||
|  | 
 | ||||||
|  | def pytest_addoption(parser): | ||||||
|  |     parser.addoption("--hooklog", dest="hooklog", default=None,  | ||||||
|  |         help="write hook calls to the given file.") | ||||||
|  | 
 | ||||||
|  | def pytest_configure(config): | ||||||
|  |     hooklog = config.getvalue("hooklog") | ||||||
|  |     if hooklog: | ||||||
|  |         assert not config.pluginmanager.comregistry.logfile | ||||||
|  |         config.pluginmanager.comregistry.logfile = open(hooklog, 'w') | ||||||
|  | 
 | ||||||
|  | def pytest_unconfigure(config): | ||||||
|  |     f = config.pluginmanager.comregistry.logfile | ||||||
|  |     if f: | ||||||
|  |         f.close() | ||||||
|  |         config.pluginmanager.comregistry.logfile = None | ||||||
|  | 
 | ||||||
|  | # =============================================================================== | ||||||
|  | # plugin tests  | ||||||
|  | # =============================================================================== | ||||||
|  | 
 | ||||||
|  | def test_generic(plugintester): | ||||||
|  |     plugintester.hookcheck() | ||||||
|  | 
 | ||||||
|  | def test_functional(testdir): | ||||||
|  |     testdir.makepyfile(""" | ||||||
|  |         def test_pass(): | ||||||
|  |             pass | ||||||
|  |     """) | ||||||
|  |     testdir.runpytest("--hooklog=hook.log") | ||||||
|  |     s = testdir.tmpdir.join("hook.log").read() | ||||||
|  |     assert s.find("pytest_testrunstart") != -1 | ||||||
|  |     assert s.find("ItemTestReport") != -1 | ||||||
|  |     assert s.find("testrunfinish") != -1 | ||||||
|  | @ -1,12 +1,22 @@ | ||||||
|  | """ | ||||||
|  | 'capsys' and 'capfd' funcargs for capturing stdout/stderror either | ||||||
|  | by intercepting sys.stdout/stderr or File Descriptors 1/2.  | ||||||
|  | 
 | ||||||
|  | Calling the reset() method of the capture funcargs gives  | ||||||
|  | a out/err tuple of strings representing the captured streams.  | ||||||
|  | You can call reset() multiple times each time getting | ||||||
|  | the chunk of output that was captured between the invocations.  | ||||||
|  | 
 | ||||||
|  | """ | ||||||
| import py | import py | ||||||
| 
 | 
 | ||||||
| def pytest_funcarg__stdcapture(request): | def pytest_funcarg__capsys(request): | ||||||
|     """ capture writes to sys.stdout/sys.stderr. """  |     """ capture writes to sys.stdout/sys.stderr. """  | ||||||
|     capture = Capture(py.io.StdCapture) |     capture = Capture(py.io.StdCapture) | ||||||
|     request.addfinalizer(capture.finalize) |     request.addfinalizer(capture.finalize) | ||||||
|     return capture  |     return capture  | ||||||
| 
 | 
 | ||||||
| def pytest_funcarg__stdcapturefd(request): | def pytest_funcarg__capfd(request): | ||||||
|     """ capture writes to filedescriptors 1 and 2""" |     """ capture writes to filedescriptors 1 and 2""" | ||||||
|     capture = Capture(py.io.StdCaptureFD) |     capture = Capture(py.io.StdCaptureFD) | ||||||
|     request.addfinalizer(capture.finalize) |     request.addfinalizer(capture.finalize) | ||||||
|  | @ -31,19 +41,19 @@ def test_generic(plugintester): | ||||||
| class TestCapture: | class TestCapture: | ||||||
|     def test_std_functional(self, testdir):         |     def test_std_functional(self, testdir):         | ||||||
|         reprec = testdir.inline_runsource(""" |         reprec = testdir.inline_runsource(""" | ||||||
|             def test_hello(stdcapture): |             def test_hello(capsys): | ||||||
|                 print 42 |                 print 42 | ||||||
|                 out, err = stdcapture.reset() |                 out, err = capsys.reset() | ||||||
|                 assert out.startswith("42") |                 assert out.startswith("42") | ||||||
|         """) |         """) | ||||||
|         reprec.assertoutcome(passed=1) |         reprec.assertoutcome(passed=1) | ||||||
|          |          | ||||||
|     def test_stdfd_functional(self, testdir):         |     def test_stdfd_functional(self, testdir):         | ||||||
|         reprec = testdir.inline_runsource(""" |         reprec = testdir.inline_runsource(""" | ||||||
|             def test_hello(stdcapturefd): |             def test_hello(capfd): | ||||||
|                 import os |                 import os | ||||||
|                 os.write(1, "42") |                 os.write(1, "42") | ||||||
|                 out, err = stdcapturefd.reset() |                 out, err = capfd.reset() | ||||||
|                 assert out.startswith("42") |                 assert out.startswith("42") | ||||||
|         """) |         """) | ||||||
|         reprec.assertoutcome(passed=1) |         reprec.assertoutcome(passed=1) | ||||||
|  |  | ||||||
|  | @ -1,17 +1,15 @@ | ||||||
| import os |  | ||||||
| """ | """ | ||||||
|     provides the "monkeypatch" funcarg for safely patching objects,  |     "monkeypatch" funcarg for safely patching objects,  | ||||||
|     dictionaries and environment variables during the execution of  |     dictionaries and environment variables during the execution of  | ||||||
|     a test.  There are three helper functions:  |     a test.  "monkeypatch" has three helper functions:  | ||||||
| 
 | 
 | ||||||
|         monkeypatch.setattr(obj, name, value)  |         monkeypatch.setattr(obj, name, value)  | ||||||
|         monkeypatch.setitem(obj, name, value)  |         monkeypatch.setitem(obj, name, value)  | ||||||
|         monkeypatch.setenv(name, value)  |         monkeypatch.setenv(name, value)  | ||||||
| 
 | 
 | ||||||
|     After the test run all such modifications will be undone,  |     After the test has run modifications will be undone.  | ||||||
|     which may even mean deleting the attribute or dictionary entry  |  | ||||||
|     if it didn't exist before.  |  | ||||||
| """ | """ | ||||||
|  | import os | ||||||
| 
 | 
 | ||||||
| def pytest_funcarg__monkeypatch(request): | def pytest_funcarg__monkeypatch(request): | ||||||
|     monkeypatch = MonkeyPatch() |     monkeypatch = MonkeyPatch() | ||||||
|  |  | ||||||
|  | @ -1,3 +1,4 @@ | ||||||
|  | """ XXX should be used sometime. """  | ||||||
| from py.__.test.custompdb import post_mortem | from py.__.test.custompdb import post_mortem | ||||||
| 
 | 
 | ||||||
| def pytest_item_runtest_finished(item, excinfo, outerr): | def pytest_item_runtest_finished(item, excinfo, outerr): | ||||||
|  |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| """pylint plugin | """pylint plugin | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| XXX: Currently in progress, NOT IN WORKING STATE. | XXX: Currently in progress, NOT IN WORKING STATE. | ||||||
| """ | """ | ||||||
| import py | import py | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| """ | """ | ||||||
| pytes plugin for easing testing of pytest runs themselves.  | funcargs and support code for testing py.test functionality.  | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import py | import py | ||||||
|  |  | ||||||
|  | @ -1,8 +1,5 @@ | ||||||
| """ | """ | ||||||
| 
 | "recwarn" funcarg for asserting that warnings are shown to a user.   | ||||||
| provides "recwarn" funcarg for asserting warnings to be shown  |  | ||||||
| to a user.  See the test at the bottom for an example.  |  | ||||||
| 
 |  | ||||||
| """ | """ | ||||||
| import py | import py | ||||||
| import os | import os | ||||||
|  |  | ||||||
|  | @ -1,3 +1,7 @@ | ||||||
|  | """ | ||||||
|  | perform ReST specific tests on .txt files, including | ||||||
|  | linkchecks and remote URL checks.  | ||||||
|  | """ | ||||||
| import py | import py | ||||||
| 
 | 
 | ||||||
| def pytest_addoption(parser): | def pytest_addoption(parser): | ||||||
|  |  | ||||||
|  | @ -1,54 +1,51 @@ | ||||||
|  | """XXX in progress: resultdb plugin for database logging of test results.  | ||||||
|  | 
 | ||||||
|  | Saves test results to a datastore. | ||||||
|  | 
 | ||||||
|  | XXX this needs to be merged with resultlog plugin | ||||||
|  | 
 | ||||||
|  | Also mixes in some early ideas about an archive abstraction for test  | ||||||
|  | results. | ||||||
|  | """  | ||||||
| import py | import py | ||||||
|  | 
 | ||||||
|  | py.test.skip("XXX needs to be merged with resultlog") | ||||||
|  | 
 | ||||||
| from pytest_resultlog import ResultLog | from pytest_resultlog import ResultLog | ||||||
| 
 | 
 | ||||||
| class ResultdbPlugin: | def pytest_addoption(parser): | ||||||
|     """XXX in progress: resultdb plugin for database logging of test results.  |     group = parser.addgroup("resultdb", "resultdb plugin options") | ||||||
|  |     group.addoption('--resultdb', action="store", dest="resultdb",  | ||||||
|  |             metavar="path", | ||||||
|  |             help="path to the file to store test results.") | ||||||
|  |     group.addoption('--resultdb_format', action="store",  | ||||||
|  |             dest="resultdbformat", default='json', | ||||||
|  |             help="data format (json, sqlite)") | ||||||
| 
 | 
 | ||||||
|     Saves test results to a datastore. | def pytest_configure(config): | ||||||
| 
 |     # XXX using config.XYZ is not good  | ||||||
|     XXX this needs to be merged with resultlog plugin |     if config.getvalue('resultdb'): | ||||||
| 
 |         if config.option.resultdb: | ||||||
|     Also mixes in some early ideas about an archive abstraction for test  |             # local import so missing module won't crash py.test | ||||||
|     results. |             try: | ||||||
|     """  |                 import sqlite3 | ||||||
|     def pytest_addoption(self, parser): |             except ImportError: | ||||||
|         group = parser.addgroup("resultdb", "resultdb plugin options") |                 raise config.Error('Could not import sqlite3 module') | ||||||
|         group.addoption('--resultdb', action="store", dest="resultdb",  |             try: | ||||||
|                 metavar="path", |                 import simplejson | ||||||
|                 help="path to the file to store test results.") |             except ImportError: | ||||||
|         group.addoption('--resultdb_format', action="store",  |                 raise config.Error('Could not import simplejson module') | ||||||
|                 dest="resultdbformat", default='json', |             if config.option.resultdbformat.lower() == 'json': | ||||||
|                 help="data format (json, sqlite)") |                 resultdb = ResultDB(JSONResultArchive,  | ||||||
|      |                         config.option.resultdb)  | ||||||
|     def pytest_configure(self, config): |             elif config.option.resultdbformat.lower() == 'sqlite': | ||||||
|         if config.getvalue('resultdb'): |                 resultdb = ResultDB(SQLiteResultArchive,  | ||||||
|             if config.option.resultdb: |                         config.option.resultdb)  | ||||||
|                 # local import so missing module won't crash py.test |             else: | ||||||
|                 try: |                 raise config.Error('Unknown --resultdb_format: %s' %  | ||||||
|                     import sqlite3 |                         config.option.resultdbformat)  | ||||||
|                 except ImportError: |  | ||||||
|                     raise config.Error('Could not import sqlite3 module') |  | ||||||
|                 try: |  | ||||||
|                     import simplejson |  | ||||||
|                 except ImportError: |  | ||||||
|                     raise config.Error('Could not import simplejson module') |  | ||||||
|                 if config.option.resultdbformat.lower() == 'json': |  | ||||||
|                     self.resultdb = ResultDB(JSONResultArchive,  |  | ||||||
|                             config.option.resultdb)  |  | ||||||
|                 elif config.option.resultdbformat.lower() == 'sqlite': |  | ||||||
|                     self.resultdb = ResultDB(SQLiteResultArchive,  |  | ||||||
|                             config.option.resultdb)  |  | ||||||
|                 else: |  | ||||||
|                     raise config.Error('Unknown --resultdb_format: %s' %  |  | ||||||
|                             config.option.resultdbformat)  |  | ||||||
| 
 |  | ||||||
|                 config.pluginmanager.register(self.resultdb) |  | ||||||
| 
 |  | ||||||
|     def pytest_unconfigure(self, config): |  | ||||||
|         if hasattr(self, 'resultdb'): |  | ||||||
|             del self.resultdb  |  | ||||||
|             #config.pluginmanager.unregister(self.resultdb) |  | ||||||
| 
 | 
 | ||||||
|  |             config.pluginmanager.register(resultdb) | ||||||
| 
 | 
 | ||||||
| class JSONResultArchive(object): | class JSONResultArchive(object): | ||||||
|     def __init__(self, archive_path): |     def __init__(self, archive_path): | ||||||
|  | @ -328,8 +325,7 @@ class TestWithFunctionIntegration: | ||||||
|             assert x.startswith(" ") |             assert x.startswith(" ") | ||||||
|         assert "XXX" in "".join(lines[1:]) |         assert "XXX" in "".join(lines[1:]) | ||||||
| 
 | 
 | ||||||
|     def test_log_test_outcomes(self, plugintester): |     def test_log_test_outcomes(self, testdir): | ||||||
|         testdir = plugintester.testdir() |  | ||||||
|         mod = testdir.makepyfile(test_mod=""" |         mod = testdir.makepyfile(test_mod=""" | ||||||
|             import py  |             import py  | ||||||
|             def test_pass(): pass |             def test_pass(): pass | ||||||
|  |  | ||||||
|  | @ -10,8 +10,6 @@ this code is somewhat derived from Guido Wesdorps | ||||||
| 
 | 
 | ||||||
|     http://johnnydebris.net/svn/projects/py_unittest |     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 | import py | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,8 @@ | ||||||
| """ | """ | ||||||
| mark and report specially about "expected to fail" tests.  | mark tests as expected-to-fail and report them separately.  | ||||||
|  | 
 | ||||||
|  | example:  | ||||||
| 
 | 
 | ||||||
| for marking and reporting "expected to fail" tests.  |  | ||||||
|     @py.test.mark.xfail("needs refactoring") |     @py.test.mark.xfail("needs refactoring") | ||||||
|     def test_hello(): |     def test_hello(): | ||||||
|         ... |         ... | ||||||
|  | @ -29,7 +30,7 @@ def pytest_report_teststatus(rep): | ||||||
|         elif rep.failed: |         elif rep.failed: | ||||||
|             return "xpassed", "P", "xpass"  |             return "xpassed", "P", "xpass"  | ||||||
| 
 | 
 | ||||||
| # a hook implemented called by the terminalreporter instance/plugin | # called by the terminalreporter instance/plugin | ||||||
| def pytest_terminal_summary(terminalreporter): | def pytest_terminal_summary(terminalreporter): | ||||||
|     tr = terminalreporter |     tr = terminalreporter | ||||||
|     xfailed = tr.stats.get("xfailed") |     xfailed = tr.stats.get("xfailed") | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue