## #!/usr/bin/env python
# -*- coding: utf-8 -*-
# <sure - utility belt for automated testing in python>
# Copyright (C) <2010-2023>  Gabriel Falcão <gabriel@nacaolivre.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals

from six import text_type, PY2
from six.moves import xrange

import sure
from sure.deprecated import that
from sure.magic import is_cpython
from sure import VariablesBag, expect
import unittest
from sure.compat import compat_repr, safe_repr, text_type_name

class testOldApi(unittest.TestCase):
    def test_setup_with_context(self):
        "sure.with_context() runs setup before the function itself"

        def setup(context):
            context.name = "John Resig"

        @sure.that_with_context(setup)
        def john_is_within_context(context):
            assert isinstance(context, VariablesBag)
            assert hasattr(context, "name")

        john_is_within_context()
        self.assertEqual(
            john_is_within_context.__name__,
            'john_is_within_context',
        )


    def test_context_is_not_optional(self):
        "sure.that_with_context() when no context is given it fails"

        def setup(context):
            context.name = "John Resig"

        @sure.that_with_context(setup)
        def it_crashes():
            assert True

        assert that(it_crashes).raises(
            TypeError, (
            "the function it_crashes defined at test_old_api.py line 56, is being "
            "decorated by either @that_with_context or @scenario, so it should "
            "take at least 1 parameter, which is the test context"),
        )


    def test_setup_with_context_context_failing(self):
        "sure.that_with_context() in a failing test"

        def setup(context):
            context.name = "John Resig"

        @sure.that_with_context(setup)
        def it_fails(context):
            assert False, 'should fail with this exception'

        assert that(it_fails).raises('should fail with this exception')


    def test_teardown_with_context(self):
        "sure.with_context() runs teardown before the function itself"

        class something:
            pass

        def setup(context):
            something.modified = True

        def teardown(context):
            del something.modified

        @sure.that_with_context(setup, teardown)
        def something_was_modified(context):
            assert hasattr(something, "modified")
            assert something.modified

        something_was_modified()
        assert not hasattr(something, "modified")


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

        something = "something"

        assert that(something).is_a(text_type)
        assert isinstance(something, text_type)


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

        something = "something"

        assert that('something').equals(something)
        assert something == 'something'


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

        something = "something"

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


    def test_that_has(self):
        "that() has(object)"
        class Class:
            name = "some class"
        Object = Class()
        dictionary = {
            'name': 'John',
        }
        name = "john"

        assert hasattr(Class, 'name')
        assert that(Class).has("name")
        assert that(Class).like("name")
        assert "name" in that(Class)

        assert hasattr(Object, 'name')
        assert that(Object).has("name")
        assert that(Object).like("name")
        assert "name" in that(Object)

        assert 'name' in dictionary
        assert that(dictionary).has("name")
        assert that(dictionary).like("name")
        assert "name" in that(dictionary)

        assert that(name).has("john")
        assert that(name).like("john")
        assert "john" in that(name)
        assert that(name).has("hn")
        assert that(name).like("hn")
        assert "hn" in that(name)
        assert that(name).has("jo")
        assert that(name).like("jo")
        assert "jo" in that(name)


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

        class Class:
            name = "some class"
        Object = Class()
        dictionary = {
            'name': 'John',
        }

        assert that(Class).at("name").equals('some class')
        assert that(Object).at("name").equals('some class')
        assert that(dictionary).at("name").equals('John')


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

        lst = range(1000)

        assert that(lst).len_is(1000)
        assert len(lst) == 1000
        assert that(lst).len_is(lst)


    def test_that_len_greater_than(self):
        "that() len_greater_than(number)"

        lst = range(1000)
        lst2 = range(100)

        assert that(lst).len_greater_than(100)
        assert len(lst) == 1000
        assert that(lst).len_greater_than(lst2)


    def test_that_len_greater_than_should_raise_assertion_error(self):
        "that() len_greater_than(number) raise AssertionError"

        lst = list(range(1000))
        try:
            that(lst).len_greater_than(1000)
        except AssertionError as e:
            self.assertEqual(
                str(e),
                'the length of the list should be greater then %d, but is %d'  \
                % (1000, 1000))


    def test_that_len_greater_than_or_equals(self):
        "that() len_greater_than_or_equals(number)"

        lst = list(range(1000))
        lst2 = list(range(100))

        assert that(lst).len_greater_than_or_equals(100)
        assert that(lst).len_greater_than_or_equals(1000)
        assert len(lst) == 1000
        assert that(lst).len_greater_than_or_equals(lst2)
        assert that(lst).len_greater_than_or_equals(lst)


    def test_that_len_greater_than_or_equals_should_raise_assertion_error(self):
        "that() len_greater_than_or_equals(number) raise AssertionError"

        lst = list(range(1000))
        try:
            that(lst).len_greater_than_or_equals(1001)
        except AssertionError as e:
            self.assertEqual(
                str(e),
                'the length of %r should be greater then or equals %d, but is %d' \
                % (lst, 1001, 1000))


    def test_that_len_lower_than(self):
        "that() len_lower_than(number)"

        lst = list(range(100))
        lst2 = list(range(1000))

        assert that(lst).len_lower_than(101)
        assert len(lst) == 100
        assert that(lst).len_lower_than(lst2)


    def test_that_len_lower_than_should_raise_assertion_error(self):
        "that() len_lower_than(number) raise AssertionError"

        lst = list(range(1000))
        try:
            that(lst).len_lower_than(1000)
        except AssertionError as e:
            self.assertEqual(
                str(e),
                'the length of %r should be lower then %d, but is %d' % \
                (lst, 1000, 1000))


    def test_that_len_lower_than_or_equals(self):
        "that() len_lower_than_or_equals(number)"

        lst = list(range(1000))
        lst2 = list(range(1001))

        assert that(lst).len_lower_than_or_equals(1001)
        assert that(lst).len_lower_than_or_equals(1000)
        assert len(lst) == 1000
        assert that(lst).len_lower_than_or_equals(lst2)
        assert that(lst).len_lower_than_or_equals(lst)


    def test_that_len_lower_than_or_equals_should_raise_assertion_error(self):
        "that() len_lower_than_or_equals(number) raise AssertionError"

        lst = list(range(1000))
        try:
            that(lst).len_lower_than_or_equals(100)
        except AssertionError as e:
            self.assertEqual(
                str(e),
                'the length of %r should be lower then or equals %d, but is %d' % \
                (lst, 100, 1000))


    def test_that_checking_all_atributes(self):
        "that(iterable).the_attribute('name').equals('value')"
        class shape(object):
            def __init__(self, name):
                self.kind = 'geometrical form'
                self.name = name

        shapes = [
            shape('circle'),
            shape('square'),
            shape('rectangle'),
            shape('triangle'),
        ]

        assert that(shapes).the_attribute("kind").equals('geometrical form')


    def test_that_checking_all_atributes_of_range(self):
        "that(iterable, within_range=(1, 2)).the_attribute('name').equals('value')"
        class shape(object):
            def __init__(self, name):
                self.kind = 'geometrical form'
                self.name = name

            def __repr__(self):
                return '<%s:%s>' % (self.kind, self.name)

        shapes = [
            shape('circle'),
            shape('square'),
            shape('square'),
            shape('triangle'),
        ]

        assert shapes[0].name != 'square'
        assert shapes[3].name != 'square'

        assert shapes[1].name == 'square'
        assert shapes[2].name == 'square'

        assert that(shapes, within_range=(1, 2)). \
                               the_attribute("name"). \
                               equals('square')


    def test_that_checking_all_elements(self):
        "that(iterable).every_one_is('value')"
        shapes = [
            'cube',
            'ball',
            'ball',
            'piramid',
        ]

        assert shapes[0] != 'ball'
        assert shapes[3] != 'ball'

        assert shapes[1] == 'ball'
        assert shapes[2] == 'ball'

        assert that(shapes, within_range=(1, 2)).every_one_is('ball')


    def test_that_checking_each_matches(self):
        "that(iterable).in_each('').equals('value')"
        class animal(object):
            def __init__(self, kind):
                self.attributes = {
                    'class': 'mammal',
                    'kind': kind,
                }

        animals = [
            animal('dog'),
            animal('cat'),
            animal('cow'),
            animal('cow'),
            animal('cow'),
        ]

        assert animals[0].attributes['kind'] != 'cow'
        assert animals[1].attributes['kind'] != 'cow'

        assert animals[2].attributes['kind'] == 'cow'
        assert animals[3].attributes['kind'] == 'cow'
        assert animals[4].attributes['kind'] == 'cow'

        assert animals[0].attributes['class'] == 'mammal'
        assert animals[1].attributes['class'] == 'mammal'
        assert animals[2].attributes['class'] == 'mammal'
        assert animals[3].attributes['class'] == 'mammal'
        assert animals[4].attributes['class'] == 'mammal'

        assert that(animals).in_each("attributes['class']").matches('mammal')
        assert that(animals).in_each("attributes['class']"). \
               matches(['mammal','mammal','mammal','mammal','mammal'])

        assert that(animals).in_each("attributes['kind']"). \
               matches(['dog','cat','cow','cow','cow'])

        try:
            assert that(animals).in_each("attributes['kind']").matches(['dog'])
            assert False, 'should not reach here'
        except AssertionError as e:
            assert that(text_type(e)).equals(
                '%r has 5 items, but the matching list has 1: %r' % (
                    ['dog','cat','cow','cow','cow'], ['dog'],
                )
            )


    def test_that_raises(self):
        "that(callable, with_args=[arg1], and_kwargs={'arg2': 'value'}).raises(SomeException)"
        global called

        called = False

        def function(arg1=None, arg2=None):
            global called
            called = True
            if arg1 == 1 and arg2 == 2:
                raise RuntimeError('yeah, it failed')

            return "OK"

        try:
            function(1, 2)
            assert False, 'should not reach here'

        except RuntimeError as e:
            assert text_type(e) == 'yeah, it failed'

        except Exception:
            assert False, 'should not reach here'

        finally:
            assert called
            called = False

        self.assertRaises(RuntimeError, function, 1, 2)

        called = False
        self.assertEqual(function(3, 5), 'OK')
        assert called

        called = False
        assert that(function, with_args=[1], and_kwargs={'arg2': 2}). \
               raises(RuntimeError)
        assert called

        called = False
        assert that(function, with_args=[1], and_kwargs={'arg2': 2}). \
               raises(RuntimeError, 'yeah, it failed')
        assert called

        called = False
        assert that(function, with_args=[1], and_kwargs={'arg2': 2}). \
               raises('yeah, it failed')
        assert called

        called = False
        assert that(function, with_kwargs={'arg1': 1, 'arg2': 2}). \
               raises(RuntimeError)
        assert called

        called = False
        assert that(function, with_kwargs={'arg1': 1, 'arg2': 2}). \
               raises(RuntimeError, 'yeah, it failed')
        assert called

        called = False
        assert that(function, with_kwargs={'arg1': 1, 'arg2': 2}). \
               raises('yeah, it failed')
        assert called

        called = False
        assert that(function, with_kwargs={'arg1': 1, 'arg2': 2}). \
               raises(r'it fail')
        assert called

        called = False
        assert that(function, with_kwargs={'arg1': 1, 'arg2': 2}). \
               raises(RuntimeError, r'it fail')
        assert called


    def test_that_looks_like(self):
        "that('String\\n with BREAKLINE').looks_like('string with breakline')"
        assert that('String\n with BREAKLINE').looks_like('string with breakline')


    def test_that_raises_does_raise_for_exception_type_mismatch(self):
        "that(callable(RuntimeError)).raises(TypeError)"

        error = r'.*should raise <class .*FooError.*, but raised <class .*BarError.*'

        class FooError(Exception):
            pass

        class BarError(Exception):
            pass

        def my_function():
            raise BarError('OOps')

        try:
            assert that(my_function).raises(FooError, 'OOps')
            assert False, 'should never reach here'
        except AssertionError as e:
            import re
            assert re.match(error, text_type(e))


    def test_that_raises_with_args(self):
        "that(callable, with_args=['foo']).raises(FooError)"

        class FooError(Exception):
            pass

        def my_function(string):
            if string == 'foo':
                raise FooError('OOps')

        assert that(my_function, with_args=['foo']).raises(FooError, 'OOps')


    def test_that_does_not_raise_with_args(self):
        "that(callable).doesnt_raise(FooError) and does_not_raise"

        class FooError(Exception):
            pass

        def my_function(string):
            if string == 'foo':
                raise FooError('OOps')

        assert that(my_function, with_args=['foo']).raises(FooError, 'OOps')


    def test_that_contains_string(self):
        "that('foobar').contains('foo')"

        assert 'foo' in 'foobar'
        assert that('foobar').contains('foo')


    def test_that_doesnt_contain_string(self):
        "that('foobar').does_not_contain('123'), .doesnt_contain"

        assert '123' not in 'foobar'
        assert that('foobar').doesnt_contain('123')
        assert that('foobar').does_not_contain('123')


    def test_that_contains_none(self):
        "that('foobar').contains(None)"

        def assertions():
            # We can't use unicode in Py2, otherwise it will try to coerce
            assert that(b'foobar' if PY2 else 'foobar').contains(None)

        error_msg = "'in <string>' requires string as left operand, not NoneType" if is_cpython else "'NoneType' does not have the buffer interface"

        assert that(assertions).raises(
            TypeError,
            error_msg
        )


    def test_that_none_contains_string(self):
        "that(None).contains('bungalow')"

        try:
            assert that(None).contains('bungalow')
            assert False, 'should not reach here'
        except Exception as e:
            error_msg = "argument of type 'NoneType' is not iterable" if is_cpython else "'NoneType' object is not iterable"
            self.assertEqual(
                text_type(e),
                error_msg
            )


    def test_that_some_iterable_is_empty(self):
        "that(some_iterable).is_empty and that(something).are_empty"

        assert that([]).is_empty
        assert that([]).are_empty

        assert that(tuple()).is_empty
        assert that({}).are_empty

        def fail_single():
            assert that((1,)).is_empty

        assert that(fail_single).raises('(1,) is not empty, it has 1 item')

        def fail_plural():
            assert that((1, 2)).is_empty

        assert that(fail_plural).raises('(1, 2) is not empty, it has 2 items')


    def test_that_something_is_empty_raises(self):
        "that(something_not_iterable).is_empty and that(something_not_iterable).are_empty raises"

        obj = object()

        def fail():
            assert that(obj).is_empty
            assert False, 'should not reach here'

        assert that(fail).raises('%r is not iterable' % obj)


    def test_that_something_iterable_matches_another(self):
        "that(something_iterable).matches(another_iterable)"

        # types must be unicode in py3, bute bytestrings in py2
        KlassOne = type(b'KlassOne' if PY2 else 'KlassOne', (object,), {})
        KlassTwo = type(b'KlassTwo' if PY2 else 'KlassTwo', (object,), {})
        one = [
            ("/1", KlassOne),
            ("/2", KlassTwo),
        ]

        two = [
            ("/1", KlassOne),
            ("/2", KlassTwo),
        ]

        assert that(one).matches(two)
        assert that(one).equals(two)

        def fail_1():
            assert that([1]).matches(xrange(2))

        class Fail2(object):
            def __init__(self):
                assert that(xrange(1)).matches([2])

        class Fail3(object):
            def __call__(self):
                assert that(xrange(1)).matches([2])

        xrange_name = xrange.__name__
        assert that(fail_1).raises('X is a list and Y is a {0} instead'.format(xrange_name))
        assert that(Fail2).raises('X is a {0} and Y is a list instead'.format(xrange_name))
        assert that(Fail3()).raises('X is a {0} and Y is a list instead'.format(xrange_name))


    def test_within_pass(self):
        "within(five=miliseconds) will pass"
        from sure import within, miliseconds

        within(five=miliseconds)(lambda *a: None)()


    def test_within_fail(self):
        "within(five=miliseconds) will fail"
        import time
        from sure import within, miliseconds

        def sleepy(*a):
            time.sleep(0.7)

        failed = False
        try:
            within(five=miliseconds)(sleepy)()
        except AssertionError as e:
            failed = True
            self.assertEqual('sleepy did not run within five miliseconds', str(e))

        assert failed, 'within(five=miliseconds)(sleepy) did not fail'


    def test_word_to_number(self):
        self.assertEqual(sure.word_to_number('one'),      1)
        self.assertEqual(sure.word_to_number('two'),      2)
        self.assertEqual(sure.word_to_number('three'),    3)
        self.assertEqual(sure.word_to_number('four'),     4)
        self.assertEqual(sure.word_to_number('five'),     5)
        self.assertEqual(sure.word_to_number('six'),      6)
        self.assertEqual(sure.word_to_number('seven'),    7)
        self.assertEqual(sure.word_to_number('eight'),    8)
        self.assertEqual(sure.word_to_number('nine'),     9)
        self.assertEqual(sure.word_to_number('ten'),     10)
        self.assertEqual(sure.word_to_number('eleven'),  11)
        self.assertEqual(sure.word_to_number('twelve'),  12)


    def test_word_to_number_fail(self):
        failed = False
        try:
            sure.word_to_number('twenty')
        except AssertionError as e:
            failed = True
            self.assertEqual(
                text_type(e),
                'sure supports only literal numbers from one ' \
                'to twelve, you tried the word "twenty"')

        assert failed, 'should raise assertion error'


    def test_microsecond_unit(self):
        "testing microseconds convertion"
        cfrom, cto = sure.UNITS[sure.microsecond]

        self.assertEqual(cfrom(1), 100000)
        self.assertEqual(cto(1), 1)

        cfrom, cto = sure.UNITS[sure.microseconds]

        self.assertEqual(cfrom(1), 100000)
        self.assertEqual(cto(1), 1)


    def test_milisecond_unit(self):
        "testing miliseconds convertion"
        cfrom, cto = sure.UNITS[sure.milisecond]

        self.assertEqual(cfrom(1), 1000)
        self.assertEqual(cto(100), 1)

        cfrom, cto = sure.UNITS[sure.miliseconds]

        self.assertEqual(cfrom(1), 1000)
        self.assertEqual(cto(100), 1)


    def test_second_unit(self):
        "testing seconds convertion"
        cfrom, cto = sure.UNITS[sure.second]

        self.assertEqual(cfrom(1), 1)
        self.assertEqual(cto(100000), 1)

        cfrom, cto = sure.UNITS[sure.seconds]

        self.assertEqual(cfrom(1), 1)
        self.assertEqual(cto(100000), 1)


    def test_minute_unit(self):
        "testing minutes convertion"
        cfrom, cto = sure.UNITS[sure.minute]

        self.assertEqual(cfrom(60), 1)
        self.assertEqual(cto(1), 6000000)

        cfrom, cto = sure.UNITS[sure.minutes]

        self.assertEqual(cfrom(60), 1)
        self.assertEqual(cto(1), 6000000)


    def test_within_pass_utc(self):
        "within(five=miliseconds) gives utc parameter"
        from sure import within, miliseconds
        from datetime import datetime

        def assert_utc(utc):
            assert isinstance(utc, datetime)

        within(five=miliseconds)(assert_utc)()


    def test_that_is_a_matcher_should_absorb_callables_to_be_used_as_matcher(self):
        "that.is_a_matcher should absorb callables to be used as matcher"
        @that.is_a_matcher
        def is_truthful(what):
            assert bool(what), '%s is so untrue' % (what)
            return 'foobar'

        assert that('friend').is_truthful()
        self.assertEqual(that('friend').is_truthful(), 'foobar')


    def test_accepts_setup_list(self):
        "sure.with_context() accepts a list of callbacks for setup"

        def setup1(context):
            context.first_name = "John"

        def setup2(context):
            context.last_name = "Resig"

        @sure.that_with_context([setup1, setup2])
        def john_is_within_context(context):
            assert context.first_name == 'John'
            assert context.last_name == 'Resig'

        john_is_within_context()
        self.assertEqual(
            john_is_within_context.__name__,
            'john_is_within_context',
        )


    def test_accepts_teardown_list(self):
        "sure.with_context() runs teardown before the function itself"

        class something:
            modified = True
            finished = 'nope'

        def setup(context):
            something.modified = False

        def teardown1(context):
            something.modified = True

        def teardown2(context):
            something.finished = 'yep'

        @sure.that_with_context(setup, [teardown1, teardown2])
        def something_was_modified(context):
            assert not something.modified
            assert something.finished == 'nope'

        something_was_modified()
        assert something.modified
        assert something.finished == 'yep'


    def test_scenario_is_alias_for_context_on_setup_and_teardown(self):
        "@scenario aliases @that_with_context for setup and teardown"
        from sure import scenario

        def setup(context):
            context.name = "Robert C Martin"

        def teardown(context):
            self.assertEqual(context.name, "Robert C Martin")

        @scenario([setup], [teardown])
        def robert_is_within_context(context):
            "Robert is within context"
            assert isinstance(context, VariablesBag)
            assert hasattr(context, "name")
            self.assertEqual(context.name, "Robert C Martin")

        robert_is_within_context()
        self.assertEqual(
            robert_is_within_context.__name__,
            'robert_is_within_context',
        )


    def test_actions_returns_context(self):
        "the actions always returns the context"
        from sure import action_for, scenario

        def with_setup(context):
            @action_for(context)
            def action1():
                pass

            @action_for(context)
            def action2():
                pass

        @scenario(with_setup)
        def i_can_use_actions(context):
            assert that(context.action1()).equals(context)
            assert that(context.action2()).equals(context)
            return True

        assert i_can_use_actions()


    def test_actions_providing_variables_in_the_context(self):
        "the actions should be able to declare the variables they provide"
        from sure import action_for, scenario

        def with_setup(context):
            @action_for(context, provides=['var1', 'foobar'])
            def the_context_has_variables():
                context.var1 = 123
                context.foobar = "qwerty"

        @scenario(with_setup)
        def the_providers_are_working(Then):
            Then.the_context_has_variables()
            assert hasattr(Then, 'var1')
            assert hasattr(Then, 'foobar')
            assert hasattr(Then, '__sure_providers_of__')

            providers = Then.__sure_providers_of__
            action = Then.the_context_has_variables.__name__

            providers_of_var1 = [p.__name__ for p in providers['var1']]
            assert that(providers_of_var1).contains(action)

            providers_of_foobar = [p.__name__ for p in providers['foobar']]
            assert that(providers_of_foobar).contains(action)

            return True

        assert the_providers_are_working()


    def test_fails_when_action_doesnt_fulfill_the_agreement_of_provides(self):
        "it fails when an action doesn't fulfill its agreements"
        from sure import action_for, scenario

        error = 'the action "bad_action" was supposed to provide the ' \
            'attribute "two" into the context, but it did not. Please ' \
            'double check its implementation'

        def with_setup(context):
            @action_for(context, provides=['one', 'two'])
            def bad_action():
                context.one = 123

        @scenario(with_setup)
        def the_providers_are_working(the):
            assert that(the.bad_action).raises(AssertionError, error)
            return True

        assert the_providers_are_working()


    def test_depends_on_failing_due_nothing_found(self):
        "it fails when an action depends on some attribute that is not " \
            "provided by any other previous action"
        import os
        from sure import action_for, scenario

        fullpath = os.path.abspath(__file__).replace('.pyc', '.py')
        error = 'the action "lonely_action" defined at %s:926 ' \
            'depends on the attribute "something" to be available in the' \
            ' context. It turns out that there are no actions providing ' \
            'that. Please double-check the implementation' % fullpath

        def with_setup(context):
            @action_for(context, depends_on=['something'])
            def lonely_action():
                pass

        @scenario(with_setup)
        def depends_on_fails(the):
            assert that(the.lonely_action).raises(AssertionError, error)
            return True

        assert depends_on_fails()


    def test_depends_on_failing_due_not_calling_a_previous_action(self):
        "it fails when an action depends on some attribute that is being " \
            "provided by other actions"

        import os
        from sure import action_for, scenario

        fullpath = os.path.abspath(__file__).replace('.pyc', '.py')
        error = 'the action "my_action" defined at {0}:956 ' \
            'depends on the attribute "some_attr" to be available in the context.'\
            ' You need to call one of the following actions beforehand:\n' \
            ' -> dependency_action at {0}:952'.replace('{0}', fullpath)

        def with_setup(context):
            @action_for(context, provides=['some_attr'])
            def dependency_action():
                context.some_attr = True

            @action_for(context, depends_on=['some_attr'])
            def my_action():
                pass

        @scenario(with_setup)
        def depends_on_fails(the):
            assert that(the.my_action).raises(AssertionError, error)
            return True

        assert depends_on_fails()


    def test_that_contains_dictionary_keys(self):
        "that(dict(name='foobar')).contains('name')"

        data = dict(name='foobar')
        assert 'name' in data
        assert 'name' in data.keys()
        assert that(data).contains('name')


    def test_that_contains_list(self):
        "that(['foobar', '123']).contains('foobar')"

        data = ['foobar', '123']
        assert 'foobar' in data
        assert that(data).contains('foobar')


    def test_that_contains_set(self):
        "that(set(['foobar', '123']).contains('foobar')"

        data = set(['foobar', '123'])
        assert 'foobar' in data
        assert that(data).contains('foobar')


    def test_that_contains_tuple(self):
        "that(('foobar', '123')).contains('foobar')"

        data = ('foobar', '123')
        assert 'foobar' in data
        assert that(data).contains('foobar')


    def test_variables_bag_provides_meaningful_error_on_nonexisting_attribute(self):
        "VariablesBag() provides a meaningful error when attr does not exist"

        context = VariablesBag()

        context.name = "John"
        context.foo = "bar"

        assert that(context.name).equals("John")
        assert that(context.foo).equals("bar")

        def access_nonexisting_attr():
            assert context.bleh == 'crash :('

        assert that(access_nonexisting_attr).raises(
            AssertionError,
            'you have tried to access the attribute \'bleh\' from the context ' \
            '(aka VariablesBag), but there is no such attribute assigned to it. ' \
            'Maybe you misspelled it ? Well, here are the options: ' \
            '[\'name\', \'foo\']',
        )


    def test_actions_providing_dinamically_named_variables(self):
        "the actions should be able to declare the variables they provide"
        from sure import action_for, scenario

        def with_setup(context):
            @action_for(context, provides=['var1', '{0}'])
            def the_context_has_variables(first_arg):
                context.var1 = 123
                context[first_arg] = "qwerty"

        @scenario(with_setup)
        def the_providers_are_working(Then):
            Then.the_context_has_variables('JohnDoe')
            assert hasattr(Then, 'var1')
            assert 'JohnDoe' in Then
            assert hasattr(Then, '__sure_providers_of__')

            providers = Then.__sure_providers_of__
            action = Then.the_context_has_variables.__name__

            providers_of_var1 = [p.__name__ for p in providers['var1']]
            assert that(providers_of_var1).contains(action)

            providers_of_JohnDoe = [p.__name__ for p in providers['JohnDoe']]
            assert that(providers_of_JohnDoe).contains(action)

            return True

        assert the_providers_are_working()


    def test_deep_equals_dict_level1_success(self):
        "that() deep_equals(dict) succeeding on level 1"

        something = {
            'one': 'yeah',
        }

        assert that(something).deep_equals({
            'one': 'yeah',
        })


    def test_deep_equals_dict_level1_fail(self):
        "that() deep_equals(dict) failing on level 1"

        something = {
            'one': 'yeah',
        }

        def assertions():
            assert that(something).deep_equals({
                'one': 'oops',
            })

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = {'one': 'yeah'}\n" \
            "    and\n" \
            "Y = {'one': 'oops'}\n" \
            "X['one'] is 'yeah' whereas Y['one'] is 'oops'",
        ))


    def test_deep_equals_list_level1_success(self):
        "that(list) deep_equals(list) succeeding on level 1"

        something = ['one', 'yeah']
        assert that(something).deep_equals(['one', 'yeah'])


    def test_deep_equals_list_level1_fail_by_value(self):
        "that(list) deep_equals(list) failing on level 1"

        something = ['one', 'yeahs']

        def assertions():
            assert that(something).deep_equals(['one', 'yeah'])

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = ['one', 'yeahs']\n" \
            "    and\n" \
            "Y = ['one', 'yeah']\n" \
            "X[1] is 'yeahs' whereas Y[1] is 'yeah'",
        ))


    def test_deep_equals_list_level1_fail_by_length_x_gt_y(self):
        "that(list) deep_equals(list) failing by length (len(X) > len(Y))"

        something = ['one', 'yeah', 'awesome!']

        def assertions():
            assert that(something).deep_equals(['one', 'yeah'])

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = ['one', 'yeah', 'awesome!']\n" \
            "    and\n" \
            "Y = ['one', 'yeah']\n" \
            "X has 3 items whereas Y has only 2",
        ))


    def test_deep_equals_list_level1_fail_by_length_y_gt_x(self):
        "that(list) deep_equals(list) failing by length (len(Y) > len(X))"

        something = ['one', 'yeah']

        def assertions():
            assert that(something).deep_equals(['one', 'yeah', 'damn'])

        assert that(assertions).raises(
            AssertionError, compat_repr(
                "given\n"
                "X = ['one', 'yeah']\n"
                "    and\n"
                "Y = ['one', 'yeah', 'damn']\n"
                "Y has 3 items whereas X has only 2"
            )
        )


    def test_deep_equals_dict_level1_fails_missing_key_on_y(self):
        "that(X) deep_equals(Y) fails when Y is missing a key that X has"

        something = {
            'one': 'yeah',
        }

        def assertions():
            assert that(something).deep_equals({
                'two': 'yeah',
            })

        assert that(assertions).raises(
            AssertionError, compat_repr(
                "given\n"
                "X = {{'one': 'yeah'}}\n"
                "    and\n"
                "Y = {{'two': 'yeah'}}\n"
                "X has the key \"{0}\" whereas Y does not"
            ).format(safe_repr('one'))
        )


    def test_deep_equals_failing_basic_vs_complex(self):
        "that(X) deep_equals(Y) fails with basic vc complex type"

        def assertions():
            assert that('two yeah').deep_equals({
                'two': 'yeah',
            })

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = 'two yeah'\n"
            "    and\n" \
            "Y = {'two': 'yeah'}\n" \
            "X is a %s and Y is a dict instead" % text_type_name,
        ))


    def test_deep_equals_failing_complex_vs_basic(self):
        "that(X) deep_equals(Y) fails with complex vc basic type"

        def assertions():
            assert that({'two': 'yeah'}).deep_equals('two yeah')

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = {'two': 'yeah'}\n" \
            "    and\n" \
            "Y = 'two yeah'\n"
            "X is a dict and Y is a %s instead" % text_type_name,
        ))


    def test_deep_equals_tuple_level1_success(self):
        "that(tuple) deep_equals(tuple) succeeding on level 1"

        something = ('one', 'yeah')
        assert that(something).deep_equals(('one', 'yeah'))


    def test_deep_equals_tuple_level1_fail_by_value(self):
        "that(tuple) deep_equals(tuple) failing on level 1"

        something = ('one', 'yeahs')

        def assertions():
            assert that(something).deep_equals(('one', 'yeah'))

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = ('one', 'yeahs')\n" \
            "    and\n" \
            "Y = ('one', 'yeah')\n" \
            "X[1] is 'yeahs' whereas Y[1] is 'yeah'",
        ))


    def test_deep_equals_tuple_level1_fail_by_length_x_gt_y(self):
        "that(tuple) deep_equals(tuple) failing by length (len(X) > len(Y))"

        something = ('one', 'yeah', 'awesome!')

        def assertions():
            assert that(something).deep_equals(('one', 'yeah'))

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = ('one', 'yeah', 'awesome!')\n" \
            "    and\n" \
            "Y = ('one', 'yeah')\n" \
            "X has 3 items whereas Y has only 2",
        ))


    def test_deep_equals_tuple_level1_fail_by_length_y_gt_x(self):
        "that(tuple) deep_equals(tuple) failing by length (len(Y) > len(X))"

        something = ('one', 'yeah')

        def assertions():
            assert that(something).deep_equals(('one', 'yeah', 'damn'))

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = ('one', 'yeah')\n" \
            "    and\n" \
            "Y = ('one', 'yeah', 'damn')\n" \
            "Y has 3 items whereas X has only 2",
        ))


    def test_deep_equals_fallsback_to_generic_comparator(self):
        "that() deep_equals(dict) falling back to generic comparator"
        from datetime import datetime
        now = datetime.now()
        something = {
            'one': 'yeah',
            'date': now,
        }

        assert that(something).deep_equals({
            'one': 'yeah',
            'date': now,
        })


    def test_deep_equals_fallsback_to_generic_comparator_failing(self):
        "that() deep_equals(dict) with generic comparator failing"
        from datetime import datetime
        now = datetime(2012, 3, 5)
        tomorrow = datetime(2012, 3, 6)
        something = {
            'date': now,
        }

        def assertions():
            assert that(something).deep_equals({
                'date': tomorrow,
            })

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = {'date': datetime.datetime(2012, 3, 5, 0, 0)}\n" \
            "    and\n" \
            "Y = {'date': datetime.datetime(2012, 3, 6, 0, 0)}\n" \
            "X['date'] != Y['date']",
        ))


    def test_deep_equals_fallsback_to_generic_comparator_failing_type(self):
        "that() deep_equals(dict) with generic comparator failing"
        from datetime import datetime
        now = datetime(2012, 3, 5)
        something = {
            'date': now,
        }

        def assertions():
            assert that(something).deep_equals({
                'date': None,
            })

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = {'date': datetime.datetime(2012, 3, 5, 0, 0)}\n" \
            "    and\n" \
            "Y = {'date': None}\n" \
            "X['date'] is a datetime and Y['date'] is a NoneType instead",
        ))


    def test_deep_equals_dict_level2_success(self):
        "that() deep_equals(dict) succeeding on level 2"

        something = {
            'one': 'yeah',
            'another': {
                'two': 'cool',
            },
        }

        assert that(something).deep_equals({
            'one': 'yeah',
            'another': {
                'two': 'cool',
            },
        })


    def test_deep_equals_dict_level2_list_success(self):
        "that() deep_equals(dict) succeeding on level 2"

        something = {
            'one': 'yeah',
            'another': ['one', 'two', 3],
        }

        assert that(something).deep_equals({
            'one': 'yeah',
            'another': ['one', 'two', 3],
        })


    def test_deep_equals_dict_level2_fail(self):
        "that() deep_equals(dict) failing on level 2"

        something = {
            'one': 'yeah',
            'another': {
                'two': '##',
            },
        }

        def assertions():
            assert that(something).deep_equals({
                'one': 'yeah',
                'another': {
                    'two': '$$',
                },
            })
        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = {'another': {'two': '##'}, 'one': 'yeah'}\n" \
            "    and\n" \
            "Y = {'another': {'two': '$$'}, 'one': 'yeah'}\n" \
            "X['another']['two'] is '##' whereas Y['another']['two'] is '$$'",
        ))


    def test_deep_equals_dict_level3_fail_values(self):
        "that() deep_equals(dict) failing on level 3"

        something = {
            'my::all_users': [
                {'name': 'John', 'age': 33},
            ],
        }

        def assertions():
            assert that(something).deep_equals({
                'my::all_users': [
                    {'name': 'John', 'age': 30},
                ],
            })

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = {'my::all_users': [{'age': 33, 'name': 'John'}]}\n" \
            "    and\n" \
            "Y = {'my::all_users': [{'age': 30, 'name': 'John'}]}\n" \
            "X['my::all_users'][0]['age'] is 33 whereas Y['my::all_users'][0]['age'] is 30",
        ))


    def test_deep_equals_dict_level3_fails_missing_key(self):
        "that() deep_equals(dict) failing on level 3 when missing a key"

        something = {
            'my::all_users': [
                {'name': 'John', 'age': 33},
            ],
        }

        def assertions():
            assert that(something).deep_equals({
                'my::all_users': [
                    {'name': 'John', 'age': 30, 'foo': 'bar'},
                ],
            })

        assert that(assertions).raises(
            AssertionError, compat_repr(
                "given\n"
                "X = {{'my::all_users': [{{'age': 33, 'name': 'John'}}]}}\n"
                "    and\n"
                "Y = {{'my::all_users': [{{'age': 30, 'foo': 'bar', 'name': 'John'}}]}}\n"
                "X['my::all_users'][0] does not have the key \"{0}\" whereas Y['my::all_users'][0] has it"
            ).format(safe_repr('foo'))
        )


    def test_deep_equals_dict_level3_fails_extra_key(self):
        "that() deep_equals(dict) failing on level 3 when has an extra key"

        something = {
            'my::all_users': [
                {'name': 'John', 'age': 33, 'foo': 'bar'},
            ],
        }

        def assertions():
            assert that(something).deep_equals({
                'my::all_users': [
                    {'name': 'John', 'age': 30},
                ],
            })

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n"
            "X = {{'my::all_users': [{{'age': 33, 'foo': 'bar', 'name': 'John'}}]}}\n"
            "    and\n"
            "Y = {{'my::all_users': [{{'age': 30, 'name': 'John'}}]}}\n"
            "X['my::all_users'][0] has the key \"{0}\" whereas Y['my::all_users'][0] does not"
        ).format(safe_repr('foo')))


    def test_deep_equals_dict_level3_fails_different_key(self):
        "that() deep_equals(dict) failing on level 3 when has an extra key"

        something = {
            'my::all_users': [
                {'name': 'John', 'age': 33, 'foo': 'bar'},
            ],
        }

        def assertions():
            assert that(something).deep_equals({
                'my::all_users': [
                {'name': 'John', 'age': 33, 'bar': 'foo'},
                ],
            })

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n"
            "X = {{'my::all_users': [{{'age': 33, 'foo': 'bar', 'name': 'John'}}]}}\n"
            "    and\n"
            "Y = {{'my::all_users': [{{'age': 33, 'bar': 'foo', 'name': 'John'}}]}}\n"
            "X['my::all_users'][0] has the key \"{0}\" whereas Y['my::all_users'][0] does not"
        ).format(safe_repr('foo')))


    def test_deep_equals_list_level2_fail_by_length_x_gt_y(self):
        "that(list) deep_equals(list) failing by length (len(X) > len(Y))"

        something = {'iterable': ['one', 'yeah', 'awesome!']}

        def assertions():
            assert that(something).deep_equals({'iterable': ['one', 'yeah']})

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = {'iterable': ['one', 'yeah', 'awesome!']}\n" \
            "    and\n" \
            "Y = {'iterable': ['one', 'yeah']}\n" \
            "X has 3 items whereas Y has only 2",
        ))


    def test_deep_equals_list_level2_fail_by_length_y_gt_x(self):
        "that(list) deep_equals(list) failing by length (len(Y) > len(X))"

        something = ['one', 'yeah']

        def assertions():
            assert that(something).deep_equals(['one', 'yeah', 'damn'])

        assert that(assertions).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = ['one', 'yeah']\n" \
            "    and\n" \
            "Y = ['one', 'yeah', 'damn']\n" \
            "Y has 3 items whereas X has only 2",
        ))


    def test_function_decorated_with_wip_should_set_a_flag(self):
        "@sure.work_in_progress should set an internal flag into `sure`"

        @sure.work_in_progress
        def this_was_called():
            assert sure.it('is_running')
            return True

        assert not sure._registry['is_running']
        assert this_was_called()
        assert not sure._registry['is_running']


    def test_that_equals_fails(self):
        "that() equals(string) when it's supposed to fail"

        something = "else"

        def fail():
            assert that('something').equals(something)

        assert that(fail).raises(
            AssertionError, compat_repr(
            "given\n" \
            "X = 'something'\n" \
            "    and\n" \
            "Y = 'else'\n" \
            "X is 'something' whereas Y is 'else'",
        ))


    def test_deep_equals_weird(self):
        part1 = [
            ('Bootstraping Redis role', []),
            ('Restart scalarizr', []),
            ('Rebundle server', ['rebundle']),
            ('Use new role', ['rebundle']),
            ('Restart scalarizr after bundling', ['rebundle']),
            ('Bundling data', []),
            ('Modifying data', []),
            ('Reboot server', []),
            ('Backuping data on Master', []),
            ('Setup replication', []),
            ('Restart scalarizr in slave', []),
            ('Slave force termination', []),
            ('Slave delete EBS', ['ec2']),
            ('Setup replication for EBS test', ['ec2']),
            ('Writing on Master, reading on Slave', []),
            ('Slave -> Master promotion', []),
            ('Restart farm', ['restart_farm']),
        ]

        part2 = [
            ('Bootstraping Redis role', ['rebundle', 'rebundle', 'rebundle']),
            ('Restart scalarizr', []),
            ('Rebundle server', ['rebundle']),
            ('Use new role', ['rebundle']),
            ('Restart scalarizr after bundling', ['rebundle']),
            ('Bundling data', []),
            ('Modifying data', []),
            ('Reboot server', []),
            ('Backuping data on Master', []),
            ('Setup replication', []),
            ('Restart scalarizr in slave', []),
            ('Slave force termination', []),
            ('Slave delete EBS', ['ec2']),
            ('Setup replication for EBS test', ['ec2']),
            ('Writing on Master, reading on Slave', []),
            ('Slave -> Master promotion', []),
            ('Restart farm', ['restart_farm']),
        ]

        expect(that(part1).equals).when.called_with(part2).should.throw("")
