Skip to content

Commit

Permalink
bumps coverage to 99%
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielfalcao committed Jan 20, 2024
1 parent c7742af commit c08d5cf
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 155 deletions.
5 changes: 5 additions & 0 deletions docs/source/changelog.rst
Expand Up @@ -34,6 +34,11 @@ v3.0.0
decorator no longer receive a :class:`datetime.datetime` object as
first argument.

- Removes methods from :class:`sure.original.AssertionHelper`:
- :meth:`sure.original.AssertionHelper.differs`
- :meth:`sure.original.AssertionHelper.has`
- :meth:`sure.original.AssertionHelper.is_a`


[v2.0.0]
--------
Expand Down
34 changes: 6 additions & 28 deletions sure/__init__.py
Expand Up @@ -737,17 +737,6 @@ def none(self):

return True

def __contains__(self, expectation):
if isinstance(self.actual, dict):
items = self.actual.keys()

if isinstance(self.actual, Iterable):
items = self.actual
else:
items = dir(self.actual)

return expectation in items

@assertionmethod
def contains(self, expectation):
if expectation in self.actual:
Expand Down Expand Up @@ -885,25 +874,17 @@ def different_of(self, expectation):
def a(self, klass):
if isinstance(klass, type):
class_name = klass.__name__
elif isinstance(klass, (str, )):
class_name = klass.strip()
else:
class_name = str(klass)
class_name = str(klass).strip()

is_vowel = class_name.lower()[0] in "aeiou"

if isinstance(klass, (str, )):
if "." in klass:
items = klass.split(".")
first = items.pop(0)
if not items:
items = [first]
first = "_abcoll"
else:
if sys.version_info <= (3, 0, 0):
first = "__builtin__"
else:
first = "builtins"
first = "builtins"
items = [klass]

klass = reduce(getattr, items, __import__(first))
Expand Down Expand Up @@ -1023,7 +1004,7 @@ def called_with(self, *args, **kw):
def throw(self, *args, **kw):
if self.negative:
msg = (
"{0} called with args {1} and keyword-args {2} should "
"`{0}' called with args {1} and keyword-args {2} should "
"not raise {3} but raised {4}"
)

Expand All @@ -1033,7 +1014,7 @@ def throw(self, *args, **kw):
return True
except Exception as e:
err = msg.format(
self.actual,
self.actual.__name__,
self._callable_args,
self._callable_kw,
exc,
Expand Down Expand Up @@ -1082,7 +1063,7 @@ def match(self, regex, *args):
if not isinstance(
self.actual, str
):
raise f"{repr(self.actual)} should be a string in order to compare using .match()"
raise WrongUsageError(f"{repr(self.actual)} should be a string in order to compare using .match()")
matched = re.search(regex, self.actual, *args)

modifiers_map = {
Expand Down Expand Up @@ -1143,9 +1124,6 @@ def assure_identical(self, identical):
def __getattr__(self, name):
return getattr(self.assertion_builder, name)

def __repr__(self):
return f"<ObjectIdentityAssertion assertion_builder={repr(self.assertion_builder)}>"


assert_that = AssertionBuilder("assert_that")
it = AssertionBuilder("it")
Expand Down Expand Up @@ -1214,7 +1192,7 @@ def __exit__(self, exc_type, exc_value, traceback):
old_dir = dir


def enable_special_syntax():
def enable_special_syntax(): # pragma: no cover
"""enables :mod:`sure`'s :ref:`Special Syntax`
.. danger:: Enabling the special syntax in production code may cause unintended consequences.
Expand Down
33 changes: 16 additions & 17 deletions sure/cli.py
Expand Up @@ -15,27 +15,26 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os # pragma: no cover
import sys # pragma: no cover
from glob import glob # pragma: no cover
from itertools import chain as flatten # pragma: no cover
from functools import reduce # pragma: no cover
from pathlib import Path # pragma: no cover

import os
import sys
from glob import glob
from itertools import chain as flatten
from functools import reduce
from pathlib import Path
import click # pragma: no cover
import coverage # pragma: no cover
import threading # pragma: no cover
import sure.reporters # pragma: no cover

import click
import coverage
import threading
import sure.reporters
from sure.loader import resolve_path # pragma: no cover
from sure.runner import Runner # pragma: no cover
from sure.runtime import RuntimeOptions # pragma: no cover
from sure.reporters import gather_reporter_names # pragma: no cover
from sure.errors import ExitError, ExitFailure, InternalRuntimeError, treat_error # pragma: no cover

from sure.loader import resolve_path
from sure.runner import Runner
from sure.runtime import RuntimeOptions
from sure.reporters import gather_reporter_names
from sure.errors import ExitError, ExitFailure, InternalRuntimeError, treat_error


@click.command(no_args_is_help=True)
@click.command(no_args_is_help=True) # pragma: no cover
@click.argument("paths", nargs=-1)
@click.option("-c", "--with-coverage", is_flag=True)
@click.option("-s", "--special-syntax", is_flag=True)
Expand Down
32 changes: 0 additions & 32 deletions sure/original.py
Expand Up @@ -64,26 +64,6 @@ def all_integers(obj: typing.Iterable) -> bool:
return True


def explanation(msg: str) -> typing.Callable:
"""Decorator for methods of :class:`~sure.original.AssertionHelper`.
:param msg: message to be interpolated with the operands of the comparison taking place within the decorated method.
:returns: a decorator function
"""
def dec(func):
@wraps(func)
def wrap(self, expectation):
ret = func(self, expectation)
if bool(ret) is True:
return ret
else:
raise AssertionError(msg % (self.actual, expectation))

return wrap

return dec


class AssertionHelper(object):
"""Accompanies :class:`~sure.AssertionBuilder` in performing
assertions.
Expand Down Expand Up @@ -258,14 +238,6 @@ def every_item_is(self, expectation):

return True

@explanation('%r should differ from %r, but is the same thing')
def differs(self, expectation):
return self.actual != expectation

@explanation('%r should be a instance of %r, but is not')
def is_a(self, expectation):
return isinstance(self.actual, expectation)

def at(self, key):
if not self.has(key):
raise AssertionError(f"key {key} not present in {self.actual}")
Expand All @@ -276,10 +248,6 @@ def at(self, key):
else:
return AssertionHelper(getattr(self.actual, key))

@explanation('%r should have %r, but have not')
def has(self, that):
return that in self

def _get_int_or_length(self, obj: Union[int, typing.Iterable]):
if isinstance(obj, Iterable):
return len(obj)
Expand Down
67 changes: 65 additions & 2 deletions tests/test_assertion_builder_assertion_methods.py
Expand Up @@ -16,7 +16,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""tests for :class:`sure.AssertionBuilder` properties defined with the
decorator :func:`sure.assertionmethod`"""

import re
import sys
from sure import expects
from sure.doubles import anything_of_type

Expand Down Expand Up @@ -61,6 +62,68 @@ def test_within():
def test_different_of():
"expects().to.be.different_of()"

expects([]).to.be.different_of.when.called_with([]).should.have.raised(
expects("").to.be.different_of.when.called_with([]).should.have.raised(
".different_of only works for string comparison but in this case is expecting [] (<class 'list'>) instead"
)

expects([]).to.be.different_of.when.called_with("").should.have.raised(
".different_of only works for string comparison but in this case the actual source comparison object is [] (<class 'list'>) instead"
)


def test_is_a():
"expects().to.be.a()"

expects(b"a").to.be.a(bytes)
expects(sys.stdout).to.be.a.when.called_with("io.StringIO").to.have.raised(
"expects(sys.stdout).to.be.a.when.called_with(\"io.StringIO\").to.have.raised( expects `<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>' to be an `io.StringIO'"
)
expects(sys.stdout).to.not_be.a.when.called_with("io.TextIOWrapper").to.have.raised(
"expects(sys.stdout).to.not_be.a.when.called_with(\"io.TextIOWrapper\").to.have.raised( expects `<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>' to not be an `io.TextIOWrapper'"
)


def test_to_be_below():
"expects().to.be.below()"

expects(b"A").to.be.below(b"a")
expects(b"a").to.below.when.called_with(b"A").should.have.raised(
"b'a' should be below b'A'"
)
expects(70).to_not.be.below.when.called_with(83).should.have.raised(
"70 should not be below 83"
)
expects(b"a").to.below.when.called_with(b"A").should_not.have.raised.when.called_with(
"b'a' should be below b'A'"
).should.have.thrown.when.called_with("`below' called with args (b'A',) and keyword-args {} should not raise b'a' should be below b'A' but raised b'a' should be below b'A'").should.return_value(not False)


def test_to_be_above():
"expects().to.be.above()"

expects(b"S").to.be.above(b"B")
expects(b"D").to.be.above.when.called_with(b"S").should.have.raised(
"b'D' should be above b'S'"
)
expects(115).to.not_be.above.when.called_with(102).to.have.raised(
"115 should not be above 102"
)


def test_to_match():
"expects().to.match() REGEX"

expects("ROBSON").to.match(r"(^RO|.OB.{3})")
expects("robson").to.match(r"(^RO|.OB.{3})", re.I)
expects("OM").to.match(r"S?[OU][NM]")
expects("ON").to.match(r"S?[OU][NM]")
expects("SOM").to.match(r"S?[OU][NM]")
expects("SON").to.match(r"S?[OU][NM]")
expects("SUM").to.match(r"S?[OU][MN]")
expects("SUN").to.match(r"S?[OU][MN]")
expects("UM").to.match(r"S?[OU][MN]")
expects("UN").to.match(r"S?[OU][MN]")
expects("NOS").to_not.match(r"S?[OU][MN]")
expects(list("OHMS")).to.match.when.called_with("Ω").should.have.raised(
"['O', 'H', 'M', 'S'] should be a string in order to compare using .match()"
)
80 changes: 4 additions & 76 deletions tests/test_original_api.py
Expand Up @@ -104,15 +104,6 @@ def data_structure_was_modified(context):
assert not hasattr(data_structure, "modified")


def test_that_is_a():
"that() is_a(object)"

data_structure = "data_structure"

assert that(data_structure).is_a(str)
assert isinstance(data_structure, str)


def test_that_equals():
"that() equals(string)"

Expand All @@ -122,69 +113,6 @@ def test_that_equals():
assert data_structure == "data_structure"


def test_that_differs():
"that() differs(object)"

data_structure = "data_structure"

assert that(data_structure).differs("23123%FYTUGIHOfdf")
assert data_structure != "23123%FYTUGIHOfdf"


def test_that_has():
"that() has(object)"

class Class:
value = "some class"

Object = Class()
dictionary = {
"value": "Value",
}
value = "value"

assert hasattr(Class, "value")
expects(Class).has("value")
expects(Class).to.be.like("value")
assert "value" in that(Class)

assert hasattr(Object, "value")
expects(Object).has("value")
expects(Object).to.be.like("value")
assert "value" in that(Object)

assert "value" in dictionary
expects(dictionary).has("value")
expects(dictionary).to.be.like("value")
assert "value" in that(dictionary)

expects(value).has("value")
expects(value).to.be.like("value")
assert "value" in that(value)
expects(value).has("va")
expects(value).to.be.like("va")
assert "val" in that(value)
expects(value).has("val")
expects(value).to.be.like("ue")
assert "ue" in that(value)


def test_that_at_key_equals():
"that().at(object).equals(object)"

class Class:
attribute = "some class"

Object = Class()
dictionary = {
"attribute": "data_structure",
}

assert that(Class).at("attribute").equals("some class")
assert that(Object).at("attribute").equals("some class")
assert that(dictionary).at("attribute").equals("data_structure")


def test_that_len_is():
"that() len_is(number)"

Expand Down Expand Up @@ -669,7 +597,7 @@ def sleepy(*a):
except AssertionError as e:
failed = True
expects(
"sleepy [tests/test_original_api.py line 663] did not run within five miliseconds"
"sleepy [tests/test_original_api.py line 591] did not run within five miliseconds"
).to.equal(str(e))

assert failed, "within(five=miliseconds)(sleepy) did not fail"
Expand Down Expand Up @@ -839,7 +767,7 @@ def test_depends_on_failing_due_to_lack_of_attribute_in_context():

fullpath = collapse_path(os.path.abspath(__file__))
error = (
f'the action "variant_action" defined at {fullpath}:848 '
f'the action "variant_action" defined at {fullpath}:776 '
'depends on the attribute "data_structure" to be available in the'
" current context"
)
Expand All @@ -862,10 +790,10 @@ def test_depends_on_failing_due_not_calling_a_previous_action():

fullpath = collapse_path(os.path.abspath(__file__))
error = (
'the action "my_action" defined at {0}:876 '
'the action "my_action" defined at {0}:804 '
'depends on the attribute "some_attr" to be available in the context.'
" Perhaps one of the following actions might provide that attribute:\n"
" -> dependency_action at {0}:872".replace("{0}", fullpath)
" -> dependency_action at {0}:800".replace("{0}", fullpath)
)

def with_setup(context):
Expand Down

0 comments on commit c08d5cf

Please sign in to comment.