From b1e9d4575de34049beae7b64f3b3916d52355dde Mon Sep 17 00:00:00 2001 From: Osher De Paz Date: Sat, 21 Apr 2018 22:32:09 +0300 Subject: [PATCH 1/2] added more tests and coverage process through coveralls --- README.rst | 9 ++- ipdbugger/__init__.py | 19 ++---- requirements.txt | 10 +++- tests/unit/test_debug.py | 122 ++++++++++++++++++++++++++++++++------- tox.ini | 13 ++++- 5 files changed, 132 insertions(+), 41 deletions(-) diff --git a/README.rst b/README.rst index f58b940..6a099b0 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,5 @@ ipdbugger --------- -.. image:: https://travis-ci.org/gregoil/ipdbugger.svg?branch=master - :target: https://travis-ci.org/gregoil/ipdbugger - .. image:: https://img.shields.io/pypi/v/ipdbugger.svg :alt: PyPI :target: https://pypi.python.org/pypi/ipdbugger/ @@ -11,6 +8,12 @@ ipdbugger :alt: Python versions :target: https://pypi.python.org/pypi/ipdbugger/ +.. image:: https://travis-ci.org/gregoil/ipdbugger.svg?branch=master + :target: https://travis-ci.org/gregoil/ipdbugger + +.. image:: https://coveralls.io/repos/github/gregoil/ipdbugger/badge.svg?branch=master + :target: https://coveralls.io/github/gregoil/ipdbugger + ``ipdbugger`` is a code debugging tool based on ``ipdb``. Use the ``debug`` decorator on functions or classes to debug them. diff --git a/ipdbugger/__init__.py b/ipdbugger/__init__.py index 9d039c4..d665df4 100644 --- a/ipdbugger/__init__.py +++ b/ipdbugger/__init__.py @@ -35,7 +35,7 @@ class IPDBugger(Pdb): - """Deubbger class, adds functionality to the normal pdb.""" + """Debugger class, adds functionality to the normal pdb.""" def do_raise(self, arg): """Raise the last exception caught.""" self.do_continue(arg) @@ -93,15 +93,8 @@ def start_debugging(): class ErrorsCatchTransformer(ast.NodeTransformer): - """Surround each statement with a try/except block to catch errors. - - Attributes: - IGNORED_EXCEPTION (str): name of the base class of the exceptions - to catch, or None to catch all. - """ + """Surround each statement with a try/except block to catch errors.""" def __init__(self, ignore_exceptions=(), catch_exception=None): - raise_cmd = ast.Raise() - if sys.version_info > (3, 0): start_debug_cmd = ast.Expr( value=ast.Call( @@ -133,7 +126,7 @@ def __init__(self, ignore_exceptions=(), catch_exception=None): 0, ast.ExceptHandler(type=ignore_exception_node, name=None, - body=[raise_cmd])) + body=[ast.Raise()])) def generic_visit(self, node): """Surround node statement with a try/except block to catch errors. @@ -283,6 +276,6 @@ def wrapper(*args, **kw): return victim else: - raise RuntimeError("Debugger can only wrap functions and classes") - - return victim + raise TypeError( + "Debugger can only wrap functions and classes. " + "Got object {!r} of type {}".format(victim, type(victim).__name__)) diff --git a/requirements.txt b/requirements.txt index 271135a..6760513 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,14 @@ --e . astroid==1.5.3 +attrs==17.4.0 backports.functools-lru-cache==1.4 backports.shutil-get-terminal-size==1.0.0 colorama==0.3.9 configparser==3.5.0 +coverage==4.5.1 decorator==4.1.2 enum34==1.1.6 flake8==3.4.1 +funcsigs==1.0.2 ipdb==0.10.3 ipython==5.5.0 ipython-genutils==0.2.0 @@ -14,17 +16,21 @@ isort==4.2.15 lazy-object-proxy==1.3.1 mccabe==0.6.1 mock==2.0.0 +more-itertools==4.1.0 pathlib2==2.3.0 +pbr==4.0.2 pexpect==4.2.1 pickleshare==0.7.4 pluggy==0.5.2 prompt-toolkit==1.0.15 ptyprocess==0.5.2 -py==1.4.34 +py==1.5.3 pycodestyle==2.3.1 pyflakes==1.5.0 Pygments==2.2.0 pylint==1.7.2 +pytest==3.5.0 +pytest-cov==2.5.1 scandir==1.5 simplegeneric==0.8.1 singledispatch==3.4.0.3 diff --git a/tests/unit/test_debug.py b/tests/unit/test_debug.py index 1ef6c96..a01ba78 100644 --- a/tests/unit/test_debug.py +++ b/tests/unit/test_debug.py @@ -1,12 +1,10 @@ """Unit tests for the debug decorator in ipdbugger module.""" from __future__ import absolute_import -import sys -from unittest import TestCase, main from IPython.utils.capture import capture_output +import pytest -from ipdbugger import debug - +from ipdbugger import debug, IPDBugger try: from unittest.mock import patch @@ -15,29 +13,111 @@ from mock import patch -class DebugCatchesExpcetionsCase(TestCase): - """Tests that an exception is caught iff one is raised.""" +def test_debugging_raising_function(): + @debug + def should_raise(): + raise Exception() + + with capture_output(), patch('bdb.Bdb.set_trace') as set_trace: + should_raise() + assert set_trace.called + + +def test_debugging_raising_method(): + class A(object): + def should_raise(self): + raise Exception() + + a = A() + a.should_raise = debug(a.should_raise) + + with capture_output(), patch('bdb.Bdb.set_trace') as set_trace: + a.should_raise() + assert set_trace.called + +def test_debugging_function_twice(): @debug - def shouldnt_raise(self): + @debug + def should_raise(): + raise Exception() + + with capture_output(), patch('bdb.Bdb.set_trace') as set_trace: + should_raise() + assert set_trace.called_once + + +def test_debugging_non_raising_function(): + @debug + def non_raising_function(): pass + with capture_output(), patch('bdb.Bdb.set_trace') as set_trace: + non_raising_function() + assert not set_trace.called + + +def test_debugging_class(): @debug - def should_raise(self): - raise Exception("Something bad happened") + class DebuggedClass(object): + def first_method(self): + raise Exception() + + def second_method(self): + raise Exception() + + debugged_object = DebuggedClass() + + with capture_output(), patch('bdb.Bdb.set_trace') as set_trace: + debugged_object.first_method() + assert set_trace.called + + with capture_output(), patch('bdb.Bdb.set_trace') as set_trace: + debugged_object.second_method() + assert set_trace.called + + +def test_debugging_non_compatible_type(): + with pytest.raises(TypeError, + match="Debugger can only wrap functions and classes. " + "Got object 1 of type int"): + debug(1) + + +def test_debugging_when_source_code_is_missing(): + exec("def function(): 1 / 0", locals(), globals()) + func = debug(globals()["function"]) + + with pytest.raises(ArithmeticError): + func() + + +def test_ignoring_exceptions(): + def func(): + raise ValueError() + + func = debug(func, ignore_exceptions=[ValueError]) + + with pytest.raises(ValueError): + func() + + +def test_targeting_specific_exception(): + def func(): + assert False + + func = debug(func, catch_exception=AssertionError) + + with capture_output(), patch('bdb.Bdb.set_trace') as set_trace: + func() + assert set_trace.called - @patch('bdb.Bdb.set_trace') - def test_should_raise(self, set_trace): - with capture_output(): - self.should_raise() - assert set_trace.called - @patch('bdb.Bdb.set_trace') - def test_shouldnt_raise(self, set_trace): - with capture_output(): - self.shouldnt_raise() - assert not set_trace.called +def test_non_targeted_exceptions(): + def func(): + raise ValueError() + func = debug(func, catch_exception=AssertionError) -if __name__ == '__main__': - main() + with pytest.raises(ValueError): + func() diff --git a/tox.ini b/tox.ini index e82aea4..c8b995e 100644 --- a/tox.ini +++ b/tox.ini @@ -3,11 +3,20 @@ envlist = py27,py36 [testenv] - deps = -r{toxinidir}/requirements.txt commands = flake8 setup.py ipdbugger pylint setup.py ipdbugger - python tests/unit/test_debug.py + pytest + +[pytest] +testpaths = tests/ +addopts = --cov-report=html --cov=ipdbugger + +[coverage:run] +branch = True + +[coverage:report] +exclude_lines = if sys.version_info From 05a22d08cfb4c35b110c2ae4844c4395e1efae17 Mon Sep 17 00:00:00 2001 From: Osher De Paz Date: Sat, 21 Apr 2018 22:35:06 +0300 Subject: [PATCH 2/2] added coveralls process at the end of tests --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 99588f0..cb533c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,11 @@ language: python python: - '3.6' -install: pip install tox + +install: pip install tox coveralls script: tox +after_success: coveralls + deploy: provider: pypi on: