Permalink
Browse files

Merge pull request #1 from witsch/pytest

add support for running tests with pytest
  • Loading branch information...
2 parents 02bc1d9 + 0d25cad commit 7887ff0402eedeba91b324b3d86fb889db7925b1 @davisagli committed May 5, 2012
Showing with 63 additions and 25 deletions.
  1. +1 −0 .gitignore
  2. +5 −0 Python pytest with Coverage.sublime-build
  3. +18 −12 README.rst
  4. +39 −13 SublimePythonCoverage.py
View
@@ -0,0 +1 @@
+/coverage/
@@ -0,0 +1,5 @@
+{
+ "target": "pytest_exec",
+ "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
+ "selector": "source.python"
+}
View
@@ -39,26 +39,32 @@ Running tests with coverage
~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you run your tests using the
-`nose <http://readthedocs.org/docs/nose/en/latest/>`_ test runner,
-SublimePythonCoverage also comes with a *build system*
+`nose <http://readthedocs.org/docs/nose/en/latest/>`_ or
+`pytest <http://pytest.org/>`_ test runners,
+SublimePythonCoverage also comes with matching *build systems*
to help produce coverage information.
-Set your build system to ``Python Nose with Coverage``.
+Set your build system to either ``Python Nose with Coverage``
+or ``Python pytest with Coverage``.
Now when you trigger a build in Sublime Text 2,
-it will run ``nosetests --with-coverage`` to generate
-coverage data, and then update the highlighted lines.
+it will run ``nosetests --with-coverage`` or ``py.test` to generate
+coverage data, and then update the highlighted lines. In the
+latter case, your `setup.cfg` or `pytest.ini` is expected to
+provide the options necessary to test your package and generate
+coverage information.
SublimePythonCoverage uses a simple heuristic
-to guess the right ``nosetests`` script to run.
-First it looks in all parent directories for ``bin/nosetests``.
-If that fails, it tries to find nosetests in the PATH.
+to guess the right ``nosetests``/``py.test`` script to run.
+First it looks in all parent directories for ``bin/nosetests``/``bin/py.test``.
+If that fails, it tries to find ``nosetests``/``py.test`` in the PATH.
SublimePythonCoverage uses another heuristic
-to guess what path to pass to nosetests.
+to guess what path to pass to ``nosetests``.
If the directory of the current file is not a package,
(i.e., does not contain an ``__init__.py``),
-it runs nosetests on the current file.
+it runs ``nosetests`` on the current file. This does not work for ``py.test``.
+
Otherwise, it looks in all parent directories for ``setup.py``,
-and if it finds it it runs nosetests on that directory.
-If not, it runs nosetests on the directory of the current file.
+and if it finds it it starts the test runner on that directory.
+If not, it runs the tests on the directory of the current file.
@@ -28,7 +28,8 @@
# end bootstrap
-import sublime, sublime_plugin
+import sublime
+import sublime_plugin
from coverage import coverage
PLUGIN_FILE = os.path.abspath(__file__)
@@ -44,6 +45,7 @@ def find(base, rel, access=os.R_OK):
if not base or base == '/':
return
+
def find_cmd(base, cmd):
return find(base, ('bin', cmd), os.X_OK)
@@ -101,40 +103,64 @@ def run(self, edit):
# update highlighted regions
view.erase_regions('SublimePythonCoverage')
- view.add_regions('SublimePythonCoverage', outlines, 'comment',
+ view.add_regions('SublimePythonCoverage', outlines, 'comment',
sublime.DRAW_EMPTY | sublime.DRAW_OUTLINED)
# manually import the module containing ST2's default build command,
# since it's in a module whose name is a Python keyword :-s
ExecCommand = __import__('exec').ExecCommand
-class NoseExecCommand(ExecCommand):
- """An extension of the default build system which shows coverage at the end.
- Used by the Python Nose build system.
- """
+
+class TestExecCommand(ExecCommand):
+ """An generic extension of the default build system which shows coverage at the end."""
+
+ runner = None
+
+ def cmd(self, runner, testpath):
+ NotImplemented
def run(self, **kw):
if 'cmd' not in kw:
fname = self.window.active_view().file_name()
- # look for a virtualenv with nosetests
- nose = find_cmd(fname, 'nosetests')
- if nose is None:
+ # look for a virtualenv with nosetests, py.test etc
+ runner = find_cmd(fname, self.runner)
+ if runner is None:
# no virtualenv; maybe there's a global one
- nose = 'nosetests'
+ runner = self.runner
testpath = find_tests(fname)
if os.path.isdir(testpath):
kw['working_dir'] = testpath
else:
kw['working_dir'] = os.path.dirname(testpath)
- kw['cmd'] = [nose, '--with-coverage', testpath]
+ kw['cmd'] = self.cmd(runner, testpath)
- super(NoseExecCommand, self).run(**kw)
+ super(TestExecCommand, self).run(**kw)
def finish(self, proc):
- super(NoseExecCommand, self).finish(proc)
+ super(TestExecCommand, self).finish(proc)
for view in self.window.views():
view.run_command('show_python_coverage')
+
+
+class NoseExecCommand(TestExecCommand):
+ """An extension of the default build system using the Python Nose test
+ runner to generate coverage information."""
+
+ runner = 'nosetests'
+
+ def cmd(self, runner, testpath):
+ return [runner, '--with-coverage', testpath]
+
+
+class PytestExecCommand(TestExecCommand):
+ """An extension of the default build system using the py.test test
+ runner to generate coverage information."""
+
+ runner = 'py.test'
+
+ def cmd(self, runner, testpath):
+ return [runner]

0 comments on commit 7887ff0

Please sign in to comment.