Esempio n. 1
0
class TestTrampoline(MonadTest):
    @given(trampolines(anything()))
    def test_right_identity_law(self, trampoline):
        assert trampoline.and_then(Done).run() == trampoline.run()

    @given(anything(), unaries(trampolines(anything())))
    def test_left_identity_law(self, value, f):
        assert Done(value).and_then(f).run() == f(value).run()

    @given(trampolines(anything()), unaries(trampolines(anything())),
           unaries(trampolines(anything())))
    def test_associativity_law(self, trampoline, f, g):
        assert trampoline.and_then(f).and_then(g).run() == trampoline.and_then(
            lambda x: f(x).and_then(g)).run()

    @given(anything())
    def test_equality(self, value):
        assert Done(value) == Done(value)

    @given(anything(), anything())
    def test_inequality(self, first, second):
        assume(first != second)
        assert Done(first) != Done(second)

    @given(anything())
    def test_identity_law(self, value):
        assert Done(value).map(identity).run() == Done(value).run()

    @given(unaries(anything()), unaries(anything()), anything())
    def test_composition_law(self, f, g, value):
        h = compose(f, g)
        assert Done(value).map(g).map(f).run() == Done(value).map(h).run()

    def test_sequence(self):
        assert sequence([Done(v) for v in range(3)]).run() == (0, 1, 2)

    def test_stack_safety(self):
        with recursion_limit(100):
            sequence([Done(v) for v in range(500)]).run()

    def test_filter(self):
        assert filter_(lambda v: Done(v % 2 == 0), range(3)).run() == (0, 2)

    def test_for_each(self):
        assert for_each(Done, range(3)).run() == (0, 1, 2)
Esempio n. 2
0
class TestTrampoline(MonadTest):
    @pytest.mark.asyncio
    @given(aio_trampolines(anything()))
    async def test_right_identity_law(self, trampoline):
        assert (await
                trampoline.and_then(Done).run()) == (await trampoline.run())

    @pytest.mark.asyncio
    @given(anything(), unaries(aio_trampolines(anything())))
    async def test_left_identity_law(self, value, f):
        assert (await Done(value).and_then(f).run()) == (await f(value).run())

    @pytest.mark.asyncio
    @given(
        aio_trampolines(anything()),
        unaries(aio_trampolines(anything())),
        unaries(aio_trampolines(anything()))
    )
    async def test_associativity_law(self, trampoline, f, g):
        assert (await trampoline.and_then(f).and_then(g).run(
        )) == (await trampoline.and_then(lambda x: f(x).and_then(g)).run())

    @given(anything())
    def test_equality(self, value):
        assert Done(value) == Done(value)

    @given(anything(), anything())
    def test_inequality(self, first, second):
        assume(first != second)
        assert Done(first) != Done(second)

    @pytest.mark.asyncio
    @given(anything())
    async def test_identity_law(self, value):
        assert (await
                Done(value).map(identity).run()) == (await Done(value).run())

    @pytest.mark.asyncio
    @given(unaries(anything()), unaries(anything()), anything())
    async def test_composition_law(self, f, g, value):
        h = compose(f, g)
        assert (await Done(value).map(g).map(f).run()
                ) == (await Done(value).map(h).run())
Esempio n. 3
0
class TestMaybe(MonadTest):
    def test_equality(self):
        self._test_just_equality()
        self._test_nothing_equality()

    def test_inequality(self):
        self._test_just_inequality()
        self._test_nothing_inequality()

    def test_identity_law(self):
        self._test_just_identity_law()
        self._test_nothing_identity_law()

    @given(maybes(anything()))
    def test_right_identity_law(self, maybe: Maybe):
        assert maybe.and_then(Just) == maybe

    @given(anything(), unaries(maybes(anything())))
    def test_left_identity_law(self, value, f: Unary[Any, Maybe]):
        assert Just(value).and_then(f) == f(value)

    @given(maybes(anything()), unaries(maybes(anything())),
           unaries(maybes(anything())))
    def test_associativity_law(self, maybe: Maybe, f: Unary[Any, Maybe],
                               g: Unary[Any, Maybe]):
        assert maybe.and_then(f).and_then(g) == maybe.and_then(  # type: ignore
            lambda x: f(x).and_then(g))

    @given(anything())
    def _test_just_equality(self, value):
        assert Just(value) == Just(value)

    def _test_nothing_equality(self):
        assert Nothing() == Nothing()

    @given(anything())
    def _test_just_inequality(self, value):
        assert Just(value) != Nothing()

    @given(anything(), anything())
    def _test_nothing_inequality(self, first, second):
        assume(first != second)
        assert Just(first) != Just(second)

    @given(anything())
    def _test_just_identity_law(self, value):
        assert Just(value).map(identity) == Just(value)

    def _test_nothing_identity_law(self):
        assert Nothing().map(identity) == Nothing()

    @given(unaries(anything()), unaries(anything()), anything())
    def test_composition_law(self, f: Unary, g: Unary, value):
        h = compose(f, g)
        assert Just(value).map(h) == Just(value).map(g).map(f)
        assert Nothing().map(h) == Nothing().map(g).map(f)

    @given(anything())
    def test_just_or_else(self, value):
        assert Just(value).or_else(None) == value

    @given(anything())
    def test_nothing_or_else(self, value):
        assert Nothing().or_else(value) is value

    def test_maybe_decorater(self):
        maybe_int = maybe(int)
        assert maybe_int('1') == Just(1)
        assert maybe_int('whoops') == Nothing()

    @given(anything())
    def test_just_bool(self, value):
        assert bool(Just(value))

    def test_nothing_bool(self):
        assert not bool(Nothing())

    @given(lists(maybes(anything())))
    def test_flatten(self, maybe_list):
        assert flatten(maybe_list) == List(m.get for m in maybe_list if m)

    def test_sequence(self):
        assert sequence([Just(v) for v in range(3)]) == Just((0, 1, 2))

    def test_stack_safety(self):
        with recursion_limit(100):
            sequence([Just(v) for v in range(500)])

    def test_filter(self):
        assert filter_(lambda v: Just(v % 2 == 0), range(3)) == Just((0, 2))

    def test_for_each(self):
        assert for_each(Just, range(3)) == Just((0, 1, 2))
Esempio n. 4
0
class TestList(MonadTest):
    @given(lists(anything()), anything())
    def test_append(self, l1, l2):
        assert l1.append(l2) == l1 + (l2, )

    def test_empty(self):
        assert List().empty() == List()

    @given(lists(anything()))
    def test_left_append_identity_law(self, l):
        assert List() + l == l

    @given(lists_(anything()))
    def test_getitem(self, l):
        assume(l != [])
        assert l[0] == l[0]

    @given(lists(anything()))
    def test_right_append_identity_law(self, l):
        assert l + List() == l

    @given(lists(anything()), lists(anything()), lists(anything()))
    def test_append_associativity_law(self, x, y, z):
        assert (x + y) + z == x + (y + z)

    @given(
        lists(anything()),
        unaries(lists(anything())),
        unaries(lists(anything()))
    )
    def test_associativity_law(self, l: List, f, g):
        assert l.and_then(f).and_then(g) == l.and_then(
            lambda x: f(x).and_then(g)
        )

    @given(lists_(anything()))
    def test_equality(self, t):
        assert List(t) == List(t)

    @given(unaries(anything()), unaries(anything()), lists(anything()))
    def test_composition_law(self, f, g, l):
        h = compose(f, g)
        assert l.map(h) == l.map(g).map(f)

    @given(lists(anything()))
    def test_identity_law(self, l):
        assert l.map(identity) == l

    @given(lists_(anything()), lists_(anything()))
    def test_inequality(self, first, second):
        assume(first != second)
        assert List(first) != List(second)

    @given(anything(), unaries(lists(anything())))
    def test_left_identity_law(self, v, f):
        assert List([v]).and_then(f) == f(v)

    @given(lists(anything()))
    def test_right_identity_law(self, l):
        assert l.and_then(lambda v: List([v])) == l

    @given(lists_(anything()))
    def test_reverse(self, l):
        assert List(l).reverse() == List(reversed(l))

    @given(lists_(anything()))
    def test_filter(self, l):
        def p(v):
            return id(v) % 2 == 0

        assert List(l).filter(p) == List(builtins.filter(p, l))

    @given(lists_(integers()))
    def test_reduce(self, l):
        i = sum(l)
        assert List(l).reduce(lambda a, b: a + b, 0) == i

    @given(lists(anything(), min_size=1), anything())
    def test_setitem(self, l, value):
        index = random.choice(range(len(l)))
        with pytest.raises(TypeError):
            l[index] = value

    @given(lists(anything(), min_size=1))
    def test_delitem(self, l):
        index = random.choice(range(len(l)))
        with pytest.raises(TypeError):
            del l[index]

    @given(lists(anything()), lists_(anything()))
    def test_extend(self, l1, l2):
        assert l1.extend(l2) == l1 + l2

    @given(lists(anything()), lists(anything()))
    def test_zip(self, l1, l2):
        assert List(l1.zip(l2)) == List(zip(l1, l2))

    def test_sequence(self):
        assert sequence([value(v) for v in range(3)]) == value((0, 1, 2))

    def test_stack_safety(self):
        with recursion_limit(100):
            sequence([value(v) for v in range(500)])

    def test_filter_(self):
        assert filter_(lambda v: value(v % 2 == 0), range(3)) == value((0, 2))

    def test_for_each(self):
        assert for_each(value, range(3)) == value((0, 1, 2))
Esempio n. 5
0
from string import printable

from hypothesis import given
from hypothesis.strategies import text

from pfun import functions
from pfun.hypothesis_strategies import anything, dicts, lists, unaries


@given(anything(allow_nan=False))
def test_identity(a):
    assert functions.identity(a) == a


@given(unaries(anything()), unaries(anything()), anything())
def test_compose(f, g, arg):
    h = functions.compose(f, g)
    assert h(arg) == f(g(arg))


@given(anything(), lists(anything()), dicts(text(printable), anything()))
def test_always(value, args, kwargs):
    f = functions.always(value)
    assert f(*args, **kwargs) == value
Esempio n. 6
0
class TestEffect(MonadTest):
    @settings(deadline=None)
    @given(
        effects(anything()),
        unaries(effects(anything())),
        unaries(effects(anything())),
        anything()
    )
    def test_associativity_law(self, e, f, g, env):
        assert (
            e.and_then(f).and_then(g).run(env) ==
            e.and_then(lambda x: f(x).and_then(g)).run(env)
        )

    @given(unaries(anything()), unaries(anything()), anything(), anything())
    def test_composition_law(self, f, g, value, env):
        h = compose(f, g)
        assert (
            effect.success(value).map(h).run(env) ==
            effect.success(value).map(g).map(f).run(env)
        )

    @given(anything(), anything())
    def test_identity_law(self, value, env):
        assert (
            effect.success(value).map(identity).run(env) ==
            effect.success(value).run(env)
        )

    @settings(deadline=None)
    @given(unaries(effects(anything())), anything(), anything())
    def test_left_identity_law(self, f, value, env):
        assert (
            effect.success(value).and_then(f).run(env) == f(value).run(env)
        )

    @given(anything(), anything())
    def test_right_identity_law(self, value, env):
        assert (
            effect.success(value).and_then(
                effect.success
            ).run(env) == effect.success(value).run(env)
        )

    @given(anything(), anything())
    def test_equality(self, value, env):
        assert effect.success(value).run(env) == effect.success(value).run(env)

    @given(anything(), anything(), anything())
    def test_inequality(self, first, second, env):
        assume(first != second)
        assert effect.success(first).run(env) != effect.success(second
                                                                ).run(env)

    def test_depend(self):
        assert effect.depend().run('env') == 'env'

    def test_from_awaitable(self):
        async def f():
            return 1

        assert effect.from_awaitable(f()).run(None) == 1

    def test_sequence(self):
        assert effect.sequence_async([effect.success(v) for v in range(3)]
                                     ).run(None) == (0, 1, 2)

    def test_sequence_generator(self):
        e = effect.sequence_async(effect.success(v) for v in range(3))
        assert e.run(None) == (0, 1, 2)
        assert e.run(None) == (0, 1, 2)

    def test_stack_safety(self):
        with recursion_limit(100):
            effect.sequence_async([effect.success(v)
                                   for v in range(500)]).run(None)

        e = effect.error('')
        for _ in range(500):
            e = e.recover(lambda _: effect.error(''))
        e = e.recover(lambda _: effect.success(''))
        with recursion_limit(100):
            e.run(None)

        e = effect.success('')
        for _ in range(500):
            e = e.either()
        with recursion_limit(100):
            e.run(None)

    def test_filter(self):
        assert effect.filter_(lambda v: effect.success(v % 2 == 0),
                              range(5)).run(None) == (0, 2, 4)

    def test_filter_generator(self):
        e = effect.filter_(
            lambda v: effect.success(v % 2 == 0), (v for v in range(5))
        )
        assert e.run(None) == (0, 2, 4)
        assert e.run(None) == (0, 2, 4)

    def test_for_each(self):
        assert effect.for_each(effect.success, range(3)).run(None) == (0, 1, 2)

    def test_either(self):
        success = effect.success(1)
        error = effect.error('error')
        assert success.either().run(None) == either.Right(1)
        error.either().run(None) == either.Left('error')

    def test_recover(self):
        success = effect.success(1)
        error = effect.error('error')
        assert success.recover(lambda e: effect.success(2)).run(None) == 1
        assert error.recover(lambda e: effect.success(2)).run(None) == 2

    def test_absolve(self):
        right = either.Right(1)
        left = either.Left('error')
        right_effect = effect.success(right)
        left_effect = effect.success(left)
        assert effect.absolve(right_effect).run(None) == 1
        with pytest.raises(Exception):
            # todo
            effect.absolve(left_effect).run(None)

    def test_error(self):
        with pytest.raises(Exception):
            # todo
            effect.error('error').run(None)

    def test_combine(self):
        def f(a, b):
            return a + b

        assert effect.combine(effect.success('a'),
                              effect.success('b'))(f).run(None) == 'ab'

    def test_lift(self):
        def f(a, b):
            return a + b

        assert effect.lift(f)(effect.success(2),
                              effect.success(2)).run(None) == 4

    def test_catch(self):
        def f(fail):
            if fail:
                raise ValueError()
            else:
                return 1

        assert effect.catch(ValueError)(f)(False).run(None) == 1
        catched_error = effect.catch(ValueError)(f)(True)
        with pytest.raises(ValueError):
            catched_error.run(None)

    def test_from_callable(self):
        def f(s: str) -> either.Either[str, str]:
            return either.Right(s)

        async def g(s: str) -> either.Either[str, str]:
            return f(s)

        assert effect.from_callable(f).run('env') == 'env'
        assert effect.from_callable(g).run('env') == 'env'

    def test_memoize(self):
        state = ref.Ref(())
        e = state.modify(lambda t: t + ('modify was called', )
                         ).discard_and_then(effect.success('result')).memoize()
        double_e = e.discard_and_then(e)
        assert double_e.run(None) == 'result'
        assert state.value == ('modify was called', )

    @settings(deadline=None)
    @given(effects(anything()), effects(anything()))
    def test_lift_cpu_bound(self, e1, e2):
        effect.lift_cpu_bound(lambda v1, v2: (v1, v2)
                              )(e1,
                                e2).run(None) == (e1.run(None), e2.run(None))

    @settings(deadline=None)
    @given(effects(anything()), effects(anything()))
    def test_lift_io_bound(self, e1, e2):
        effect.lift_io_bound(lambda v1, v2: (v1, v2)
                             )(e1,
                               e2).run(None) == (e1.run(None), e2.run(None))

    @settings(deadline=None)
    @given(effects(anything()), effects(anything()))
    def test_combine_cpu_bound(self, e1, e2):
        effect.combine_cpu_bound(
            e1, e2
        )(lambda v1, v2: (v1, v2)).run(None) == (e1.run(None), e2.run(None))

    @settings(deadline=None)
    @given(effects(anything()), effects(anything()))
    def test_combine_io_bound(self, e1, e2):
        effect.combine_io_bound(
            e1, e2
        )(lambda v1, v2: (v1, v2)).run(None) == (e1.run(None), e2.run(None))

    @settings(deadline=None)
    @given(unaries(rights(anything())))
    def test_from_callable_cpu_bound(self, f):
        assert effect.from_cpu_bound_callable(f).run(None) == f(None).get

    @given(unaries(rights(anything())))
    def test_from_callable_io_bound(self, f):
        assert effect.from_io_bound_callable(f).run(None) == f(None).get

    @settings(deadline=None)
    @given(unaries(anything()))
    def test_catch_cpu_bound(self, f):
        assert effect.catch_cpu_bound(Exception)(f)(None).run(None) == f(None)

    @given(unaries(anything()))
    def test_catch_io_bound(self, f):
        assert effect.catch_io_bound(Exception)(f)(None).run(None) == f(None)

    def test_success_repr(self):
        assert repr(effect.success('value')) == 'success(\'value\')'

    def test_error_repr(self):
        assert repr(effect.error('reason')) == 'error(\'reason\')'

    def test_and_then_repr(self):
        f = lambda _: _
        assert (
            repr(effect.success('value').and_then(f)
                 ) == f'success(\'value\').and_then({repr(f)})'
        )

    def test_map_repr(self):
        f = lambda _: _
        assert (
            repr(effect.success('value').map(f)
                 ) == f'success(\'value\').map({repr(f)})'
        )

    def test_discard_and_then_repr(self):
        e = effect.success('value')
        assert (
            repr(e.discard_and_then(e)
                 ) == 'success(\'value\').discard_and_then(success(\'value\'))'
        )

    def test_either_repr(self):
        e = effect.success(0)
        assert repr(e.either()) == 'success(0).either()'

    def test_recover_repr(self):
        e = effect.success(0)
        f = lambda _: _
        assert repr(e.recover(f)) == f'success(0).recover({repr(f)})'

    def test_memoize_repr(self):
        e = effect.success(0)
        assert repr(e.memoize()) == 'success(0).memoize()'

    def test_ensure_repr(self):
        e = effect.success(0)
        assert repr(e.ensure(e)) == 'success(0).ensure(success(0))'

    def test_depend_repr(self):
        assert repr(effect.depend()) == 'depend()'

    def test_from_awaitable_repr(self):
        async def f():
            pass

        a = f()
        assert repr(effect.from_awaitable(a)) == f'from_awaitable({repr(a)})'

    def test_from_callable_repr(self):
        f = lambda _: _
        assert repr(effect.from_callable(f)) == f'from_callable({repr(f)})'

    def test_from_io_bound_callable_repr(self):
        f = lambda _: _
        assert (
            repr(effect.from_io_bound_callable(f)
                 ) == f'from_io_bound_callable({repr(f)})'
        )

    def test_from_cpu_bound_callable_repr(self):
        f = lambda _: _
        assert (
            repr(effect.from_cpu_bound_callable(f)
                 ) == f'from_cpu_bound_callable({repr(f)})'
        )

    def test_catch_repr(self):
        f = lambda _: _
        assert (
            repr(effect.catch(Exception)(f)(0)
                 ) == f'catch(<class \'Exception\'>)({repr(f)})(0)'
        )

    def test_catch_io_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.catch_io_bound(Exception)(f)(0)
                 ) == f'catch_io_bound(<class \'Exception\'>)({repr(f)})(0)'
        )

    def test_catch_cpu_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.catch_cpu_bound(Exception)(f)(0)
                 ) == f'catch_cpu_bound(<class \'Exception\'>)({repr(f)})(0)'
        )

    def test_sequence_async_repr(self):
        e = effect.sequence_async([effect.success(0)])
        assert (repr(e)) == 'sequence_async((success(0),))'

    def test_sequence_repr(self):
        e = effect.sequence([effect.success(0)])
        assert (repr(e)) == 'sequence((success(0),))'

    def test_lift_repr(self):
        f = lambda _: _
        assert (
            repr(effect.lift(f)(effect.success(0))
                 ) == f'lift({repr(f)})(success(0))'
        )

    def test_lift_io_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.lift_io_bound(f)(effect.success(0))
                 ) == f'lift_io_bound({repr(f)})(success(0))'
        )

    def test_lift_cpu_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.lift_cpu_bound(f)(effect.success(0))
                 ) == f'lift_cpu_bound({repr(f)})(success(0))'
        )

    def test_combine_repr(self):
        f = lambda _: _
        assert (
            repr(effect.combine(effect.success(0))(f)
                 ) == f'combine(success(0))({repr(f)})'
        )

    def test_combine_io_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.combine_io_bound(effect.success(0))(f)
                 ) == f'combine_io_bound(success(0))({repr(f)})'
        )

    def test_combine_cpu_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.combine_cpu_bound(effect.success(0))(f)
                 ) == f'combine_cpu_bound(success(0))({repr(f)})'
        )

    def test_filter_repr(self):
        f = lambda _: effect.success(True)
        assert repr(effect.filter_(f, [0])) == f'filter_({repr(f)})((0,))'

    def test_for_each_repr(self):
        f = lambda _: effect.success(True)
        assert repr(effect.for_each(f, [0])) == f'for_each({repr(f)})((0,))'

    def test_absolve_repr(self):
        assert repr(effect.absolve(effect.success(0))) == 'absolve(success(0))'
Esempio n. 7
0
class TestEither(MonadTest):
    @given(eithers(anything()))
    def test_right_identity_law(self, either: Either):
        assert either.and_then(Right) == either

    @given(anything(), unaries(eithers(anything())))
    def test_left_identity_law(self, value, f: Unary[Any, Either]):
        assert Right(value).and_then(f) == f(value)

    @given(
        eithers(anything()),
        unaries(eithers(anything())),
        unaries(eithers(anything()))
    )
    def test_associativity_law(
        self, either: Either, f: Unary[Any, Either], g: Unary[Any, Either]
    ):

        assert either.and_then(f).and_then(
            g
        ) == either.and_then(  # type: ignore
            lambda x: f(x).and_then(g)
        )

    @given(anything())
    def test_equality(self, value):
        assert Left(value) == Left(value)
        assert Right(value) == Right(value)

    @given(anything(), anything())
    def test_inequality(self, first, second):
        assume(first != second)
        assert Left(first) != Left(second)
        assert Right(first) != Right(second)
        assert Left(first) != Right(first)

    @given(anything())
    def test_identity_law(self, value):
        assert Left(value).map(identity) == Left(value)
        assert Right(value).map(identity) == Right(value)

    @given(unaries(anything()), unaries(anything()), anything())
    def test_composition_law(self, f: Unary, g: Unary, value):
        h = compose(f, g)
        assert Left(value).map(h) == Left(value).map(g).map(f)
        assert Right(value).map(h) == Right(value).map(g).map(f)

    @given(anything(), anything())
    def test_or_else(self, value, default):
        assert Right(value).or_else(default) == value
        assert Left(value).or_else(default) == default

    @given(anything())
    def test_bool(self, value):
        assert bool(Right(value))
        assert not bool(Left(value))

    def test_either_decorator(self):
        result_int = either(int)
        assert result_int('1') == Right(1)

    def test_gather(self):
        assert gather([Right(v) for v in range(3)]) == Right((0, 1, 2))

    def test_stack_safety(self):
        with recursion_limit(100):
            gather([Right(v) for v in range(500)])

    def test_filter(self):
        assert filter_(lambda v: Right(v % 2 == 0), range(3)) == Right((0, 2))

    def test_for_each(self):
        assert for_each(Right, range(3)) == Right((0, 1, 2))
Esempio n. 8
0
class TestEffect(MonadTest):
    @settings(deadline=None)
    @given(
        effects(anything()),
        unaries(effects(anything())),
        unaries(effects(anything())),
        anything()
    )
    def test_associativity_law(self, e, f, g, env):
        assert (
            e.and_then(f).and_then(g).run(env) ==
            e.and_then(lambda x: f(x).and_then(g)).run(env)
        )

    @given(unaries(anything()), unaries(anything()), anything(), anything())
    def test_composition_law(self, f, g, value, env):
        h = compose(f, g)
        assert (
            effect.success(value).map(h).run(env) ==
            effect.success(value).map(g).map(f).run(env)
        )

    @given(anything(), anything())
    def test_identity_law(self, value, env):
        assert (
            effect.success(value).map(identity).run(env) ==
            effect.success(value).run(env)
        )

    @settings(deadline=None)
    @given(unaries(effects(anything())), anything(), anything())
    def test_left_identity_law(self, f, value, env):
        assert (
            effect.success(value).and_then(f).run(env) == f(value).run(env)
        )

    @given(anything(), anything())
    def test_right_identity_law(self, value, env):
        assert (
            effect.success(value).and_then(
                effect.success
            ).run(env) == effect.success(value).run(env)
        )

    @given(anything(), anything())
    def test_equality(self, value, env):
        assert effect.success(value).run(env) == effect.success(value).run(env)

    @settings(deadline=None)
    @given(anything(), anything(), anything())
    def test_inequality(self, first, second, env):
        assume(first != second)
        assert effect.success(first).run(env) != effect.success(second
                                                                ).run(env)

    def test_depend(self):
        assert effect.depend().run('env') == 'env'

    def test_from_awaitable(self):
        async def f():
            return 1

        assert effect.from_awaitable(f()).run(None) == 1

    def test_gather(self):
        assert effect.gather_async(
            [effect.success(v) for v in range(3)]
        ).run(None) == (0, 1, 2)

        assert effect.gather(
            [effect.success(v) for v in range(3)]
        ).run(None) == (0, 1, 2)

    def test_gather_generator(self):
        e = effect.gather_async(effect.success(v) for v in range(3))
        assert e.run(None) == (0, 1, 2)
        assert e.run(None) == (0, 1, 2)

    def test_stack_safety(self):
        with recursion_limit(100):
            effect.gather_async([effect.success(v)
                                 for v in range(500)]).run(None)

        e = effect.error('')
        for _ in range(500):
            e = e.recover(lambda _: effect.error(''))
        e = e.recover(lambda _: effect.success(''))
        with recursion_limit(100):
            e.run(None)

        e = effect.success('')
        for _ in range(500):
            e = e.either()
        with recursion_limit(100):
            e.run(None)

    def test_filter(self):
        assert effect.filter_(lambda v: effect.success(v % 2 == 0),
                              range(5)).run(None) == (0, 2, 4)

    def test_filter_async(self):
        assert effect.filter_async(lambda v: effect.success(v % 2 == 0),
                              range(5)).run(None) == (0, 2, 4)

    def test_filter_generator(self):
        e = effect.filter_(
            lambda v: effect.success(v % 2 == 0), (v for v in range(5))
        )
        assert e.run(None) == (0, 2, 4)
        assert e.run(None) == (0, 2, 4)

    def test_for_each(self):
        assert effect.for_each(effect.success, range(3)).run(None) == (0, 1, 2)

    def test_for_each_async(self):
        assert (effect.for_each_async(effect.success, range(3)).run(None)
                == (0, 1, 2))

    def test_either(self):
        success = effect.success(1)
        error = effect.error('error')
        assert success.either().run(None) == either.Right(1)
        assert error.either().run(None) == either.Left('error')

    def test_recover(self):
        success = effect.success(1)
        error = effect.error('error')
        assert success.recover(lambda e: effect.success(2)).run(None) == 1
        assert error.recover(lambda e: effect.success(2)).run(None) == 2

    def test_absolve(self):
        right = either.Right(1)
        left = either.Left('error')
        right_effect = effect.success(right)
        left_effect = effect.success(left)
        assert effect.absolve(right_effect).run(None) == 1
        with pytest.raises(Exception):
            # todo
            effect.absolve(left_effect).run(None)

    def test_error(self):
        with pytest.raises(Exception):
            # todo
            effect.error('error').run(None)

    def test_combine(self):
        def f(a, b):
            return a + b

        assert effect.combine(effect.success('a'),
                              effect.success('b'))(f).run(None) == 'ab'

    def test_combine_async(self):
        def f(a, b):
            return a + b

        assert effect.combine_async(effect.success('a'),
                                    effect.success('b'))(f).run(None) == 'ab'

    def test_lift(self):
        def f(a, b):
            return a + b

        assert effect.lift(f)(effect.success(2),
                              effect.success(2)).run(None) == 4

    def test_lift_async(self):
        def f(a, b):
            return a + b

        assert effect.lift_async(f)(effect.success(2),
                                    effect.success(2)).run(None) == 4

    def test_catch(self):
        def f(fail):
            if fail:
                raise ValueError()
            else:
                return 1

        assert effect.catch(ValueError)(f)(False).run(None) == 1
        catched_error = effect.catch(ValueError)(f)(True)
        with pytest.raises(ValueError):
            catched_error.run(None)

    def test_from_callable(self):
        def f(s: str) -> either.Either[str, str]:
            return either.Right(s)

        async def g(s: str) -> either.Either[str, str]:
            return f(s)

        assert effect.from_callable(f).run('env') == 'env'
        assert effect.from_callable(g).run('env') == 'env'

    def test_memoize(self):
        s = state.State(())
        e = s.modify(
            lambda t: t + ('modify was called', )
        ).discard_and_then(effect.success('result')).memoize()
        double_e = e.discard_and_then(e)
        assert double_e.run(None) == 'result'
        assert s.value == ('modify was called', )

    @settings(deadline=None)
    @given(effects(anything()), effects(anything()))
    def test_lift_cpu_bound(self, e1, e2):
        assert effect.lift_cpu_bound(
            lambda v1, v2: (v1, v2)
        )(e1, e2).run(None) == (e1.run(None), e2.run(None))

    @settings(deadline=None)
    @given(effects(anything()), effects(anything()))
    def test_lift_io_bound(self, e1, e2):
        assert effect.lift_io_bound(
            lambda v1, v2: (v1, v2)
        )(e1, e2).run(None) == (e1.run(None), e2.run(None))

    @settings(deadline=None)
    @given(effects(anything()), effects(anything()))
    def test_combine_cpu_bound(self, e1, e2):
        assert effect.combine_cpu_bound(
            e1, e2
        )(lambda v1, v2: (v1, v2)).run(None) == (e1.run(None), e2.run(None))

    @settings(deadline=None)
    @given(effects(anything()), effects(anything()))
    def test_combine_io_bound(self, e1, e2):
        assert effect.combine_io_bound(
            e1, e2
        )(lambda v1, v2: (v1, v2)).run(None) == (e1.run(None), e2.run(None))

    @settings(deadline=None)
    @given(unaries(rights(anything())))
    def test_from_callable_cpu_bound(self, f):
        assert effect.from_cpu_bound_callable(f).run(None) == f(None).get

    @given(unaries(rights(anything())))
    def test_from_callable_io_bound(self, f):
        assert effect.from_io_bound_callable(f).run(None) == f(None).get

    @settings(deadline=None)
    @given(unaries(anything()))
    def test_catch_cpu_bound(self, f):
        assert effect.catch_cpu_bound(Exception)(f)(None).run(None) == f(None)

    @given(unaries(anything()))
    def test_catch_io_bound(self, f):
        assert effect.catch_io_bound(Exception)(f)(None).run(None) == f(None)

    @pytest.mark.asyncio
    @given(anything())
    async def test_process_and_thread_pool_initialized_lazily(self, value):
        with ExitStack() as stack:
            env = effect.RuntimeEnv(None, stack, 1, 1)
            await effect.success(value).do(env)
            assert env.process_executor is None
            assert env.thread_executor is None

    def test_race(self):
        async def f():
            await asyncio.sleep(10)
            return 'f'

        async def g():
            return 'g'

        e1 = effect.from_awaitable(f())
        e2 = effect.from_awaitable(g())

        assert e1.race(e2).run(None) == 'g'

    def test_timeout(self):
        async def f():
            await asyncio.sleep(10)
            return 'f'

        e = effect.from_awaitable(f())
        with pytest.raises(asyncio.TimeoutError):
            e.timeout(timedelta(milliseconds=1)).run(MockModules())

    def test_repeat(self):
        s = schedule.recurs(2, schedule.spaced(timedelta(seconds=1)))
        assert effect.success(0).repeat(s).run(MockModules()) == (0, 0)

    def test_repeat_error(self):
        s = schedule.recurs(2, schedule.spaced(timedelta(seconds=1)))
        assert (effect.error('whoops').repeat(s).either().run(MockModules()) ==
                either.Left('whoops'))

    def test_retry(self):
        s = schedule.recurs(2, schedule.spaced(timedelta(seconds=1)))
        assert (effect.error('whoops').retry(s).either().run(MockModules()) ==
                either.Left(('whoops', 'whoops')))

    def test_retry_success(self):
        s = schedule.recurs(2, schedule.spaced(timedelta(seconds=1)))
        assert effect.success(0).retry(s).run(MockModules()) == 0

    @settings(deadline=None)
    @given(anything(), unaries(anything()))
    def test_purify(self, x, f):
        assert effect.purify(f)(x).run(None) == f(x)
        assert effect.purify_io_bound(f)(x).run(None) == f(x)
        assert effect.purify_cpu_bound(f)(x).run(None) == f(x)

    def test_success_repr(self):
        assert repr(effect.success('value')) == 'success(\'value\')'

    def test_error_repr(self):
        assert repr(effect.error('reason')) == 'error(\'reason\')'

    def test_and_then_repr(self):
        f = lambda _: _
        assert (
            repr(effect.success('value').and_then(f)
                 ) == f'success(\'value\').and_then({repr(f)})'
        )

    def test_map_repr(self):
        f = lambda _: _
        assert (
            repr(effect.success('value').map(f)
                 ) == f'success(\'value\').map({repr(f)})'
        )

    def test_discard_and_then_repr(self):
        e = effect.success('value')
        assert (
            repr(e.discard_and_then(e)
                 ) == 'success(\'value\').discard_and_then(success(\'value\'))'
        )

    def test_either_repr(self):
        e = effect.success(0)
        assert repr(e.either()) == 'success(0).either()'

    def test_recover_repr(self):
        e = effect.success(0)
        f = lambda _: _
        assert repr(e.recover(f)) == f'success(0).recover({repr(f)})'

    def test_memoize_repr(self):
        e = effect.success(0)
        assert repr(e.memoize()) == 'success(0).memoize()'

    def test_ensure_repr(self):
        e = effect.success(0)
        assert repr(e.ensure(e)) == 'success(0).ensure(success(0))'

    def test_depend_repr(self):
        assert repr(effect.depend()) == 'depend()'

    @pytest.mark.filterwarnings("ignore:coroutine .+ was never awaited")
    def test_from_awaitable_repr(self):
        async def f():
            pass

        a = f()
        assert repr(effect.from_awaitable(a)) == f'from_awaitable({repr(a)})'

    def test_from_callable_repr(self):
        f = lambda _: _
        assert repr(effect.from_callable(f)) == f'from_callable({repr(f)})'

    def test_from_io_bound_callable_repr(self):
        f = lambda _: _
        assert (
            repr(effect.from_io_bound_callable(f)
                 ) == f'from_io_bound_callable({repr(f)})'
        )

    def test_from_cpu_bound_callable_repr(self):
        f = lambda _: _
        assert (
            repr(effect.from_cpu_bound_callable(f)
                 ) == f'from_cpu_bound_callable({repr(f)})'
        )

    def test_catch_repr(self):
        f = lambda _: _
        assert (
            repr(effect.catch(Exception)(f)(0)
                 ) == f'catch(<class \'Exception\'>)({repr(f)})(0)'
        )

    def test_catch_io_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.catch_io_bound(Exception)(f)(0)
                 ) == f'catch_io_bound(<class \'Exception\'>)({repr(f)})(0)'
        )

    def test_catch_cpu_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.catch_cpu_bound(Exception)(f)(0)
                 ) == f'catch_cpu_bound(<class \'Exception\'>)({repr(f)})(0)'
        )

    def test_gather_async_repr(self):
        e = effect.gather_async([effect.success(0)])
        assert (repr(e)) == 'gather_async((success(0),))'

    def test_gather_repr(self):
        e = effect.gather([effect.success(0)])
        assert (repr(e)) == 'gather((success(0),))'

    def test_lift_repr(self):
        f = lambda _: _
        assert (
            repr(effect.lift(f)(effect.success(0))
                 ) == f'lift({repr(f)})(success(0))'
        )

    def test_lift_io_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.lift_io_bound(f)(effect.success(0))
                 ) == f'lift_io_bound({repr(f)})(success(0))'
        )

    def test_lift_cpu_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.lift_cpu_bound(f)(effect.success(0))
                 ) == f'lift_cpu_bound({repr(f)})(success(0))'
        )

    def test_combine_repr(self):
        f = lambda _: _
        assert (
            repr(effect.combine(effect.success(0))(f)
                 ) == f'combine(success(0))({repr(f)})'
        )

    def test_combine_io_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.combine_io_bound(effect.success(0))(f)
                 ) == f'combine_io_bound(success(0))({repr(f)})'
        )

    def test_combine_cpu_bound_repr(self):
        f = lambda _: _
        assert (
            repr(effect.combine_cpu_bound(effect.success(0))(f)
                 ) == f'combine_cpu_bound(success(0))({repr(f)})'
        )

    def test_filter_repr(self):
        f = lambda _: effect.success(True)
        assert repr(effect.filter_(f, [0])) == f'filter_({repr(f)})((0,))'

    def test_for_each_repr(self):
        f = lambda _: effect.success(True)
        assert repr(effect.for_each(f, [0])) == f'for_each({repr(f)})((0,))'

    def test_absolve_repr(self):
        assert repr(effect.absolve(effect.success(0))) == 'absolve(success(0))'

    def test_race_repr(self):
        assert (repr(effect.success(0).race(effect.success(0))) ==
                'success(0).race(success(0))')

    def test_timeout_repr(self):
        assert (repr(effect.success(0).timeout(timedelta(seconds=1))) ==
                'success(0).timeout(datetime.timedelta(seconds=1))')