# Path: paco/interval.py
# @decorate
# def interval(coro, interval=1, times=None, loop=None):
#     """
#     Schedules the execution of a coroutine function every `x` amount of
#     seconds.
# 
#     The function returns an `asyncio.Task`, which implements also an
#     `asyncio.Future` interface, allowing the user to cancel the execution
#     cycle.
# 
#     This function can be used as decorator.
# 
#     Arguments:
#         coro (coroutinefunction): coroutine function to defer.
#         interval (int/float): number of seconds to repeat the coroutine
#             execution.
#         times (int): optional maximum time of executions. Infinite by default.
#         loop (asyncio.BaseEventLoop, optional): loop to run.
#             Defaults to asyncio.get_event_loop().
# 
#     Raises:
#         TypeError: if coro argument is not a coroutine function.
# 
#     Returns:
#         future (asyncio.Task): coroutine wrapped as task future.
#             Useful for cancellation and state checking.
# 
#     Usage::
# 
#         # Usage as function
#         future = paco.interval(coro, 1)
# 
#         # Cancel it after a while...
#         await asyncio.sleep(5)
#         future.cancel()
# 
#         # Usage as decorator
#         @paco.interval(10)
#         async def metrics():
#             await send_metrics()
# 
#         future = await metrics()
# 
#     """
#     assert_corofunction(coro=coro)
# 
#     # Store maximum allowed number of calls
#     times = int(times or 0) or float('inf')
# 
#     @asyncio.coroutine
#     def schedule(times, *args, **kw):
#         while times > 0:
#             # Decrement times counter
#             times -= 1
# 
#             # Schedule coroutine
#             yield from coro(*args, **kw)
#             yield from asyncio.sleep(interval)
# 
#     def wrapper(*args, **kw):
#         return ensure_future(schedule(times, *args, **kw), loop=loop)
# 
#     return wrapper
# 
# Path: tests/helpers.py
# def run_in_loop(coro, *args, **kw):
#     loop = asyncio.get_event_loop()
#     if asyncio.iscoroutinefunction(coro) or isfunction(coro):
#         coro = coro(*args, **kw)
#     return loop.run_until_complete(coro)


import time
import pytest
import asyncio
from paco import interval
from .helpers import run_in_loop
        except asyncio.CancelledError:
            pass

    loop = asyncio.get_event_loop()
    loop.run_until_complete(runner(loop))

    assert future.cancelled
    assert track['calls'] > 9
    assert time.time() - start > .9


def test_interval_decorator():
    track = {'calls': 0}
    start = time.time()

    task = interval(.1, 3)(coro)
    future = task(track)

    run_in_loop(future)

    assert future.cancelled
    assert track['calls'] == 3
    assert time.time() - start > .2


def test_interval_invalid_input():
    with pytest.raises(TypeError):
        run_in_loop(interval(None))

    with pytest.raises(TypeError):

# Path: paco/interval.py
# @decorate
# def interval(coro, interval=1, times=None, loop=None):
#     """
#     Schedules the execution of a coroutine function every `x` amount of
#     seconds.
# 
#     The function returns an `asyncio.Task`, which implements also an
#     `asyncio.Future` interface, allowing the user to cancel the execution
#     cycle.
# 
#     This function can be used as decorator.
# 
#     Arguments:
#         coro (coroutinefunction): coroutine function to defer.
#         interval (int/float): number of seconds to repeat the coroutine
#             execution.
#         times (int): optional maximum time of executions. Infinite by default.
#         loop (asyncio.BaseEventLoop, optional): loop to run.
#             Defaults to asyncio.get_event_loop().
# 
#     Raises:
#         TypeError: if coro argument is not a coroutine function.
# 
#     Returns:
#         future (asyncio.Task): coroutine wrapped as task future.
#             Useful for cancellation and state checking.
# 
#     Usage::
# 
#         # Usage as function
#         future = paco.interval(coro, 1)
# 
#         # Cancel it after a while...
#         await asyncio.sleep(5)
#         future.cancel()
# 
#         # Usage as decorator
#         @paco.interval(10)
#         async def metrics():
#             await send_metrics()
# 
#         future = await metrics()
# 
#     """
#     assert_corofunction(coro=coro)
# 
#     # Store maximum allowed number of calls
#     times = int(times or 0) or float('inf')
# 
#     @asyncio.coroutine
#     def schedule(times, *args, **kw):
#         while times > 0:
#             # Decrement times counter
#             times -= 1
# 
#             # Schedule coroutine
#             yield from coro(*args, **kw)
#             yield from asyncio.sleep(interval)
# 
#     def wrapper(*args, **kw):
#         return ensure_future(schedule(times, *args, **kw), loop=loop)
# 
#     return wrapper
# 
# Path: tests/helpers.py
# def run_in_loop(coro, *args, **kw):
#     loop = asyncio.get_event_loop()
#     if asyncio.iscoroutinefunction(coro) or isfunction(coro):
#         coro = coro(*args, **kw)
#     return loop.run_until_complete(coro)


# Path: tests/interval_test.py
import time
import pytest
import asyncio
from paco import interval
from .helpers import run_in_loop

        except asyncio.CancelledError:
            pass

    loop = asyncio.get_event_loop()
    loop.run_until_complete(runner(loop))

    assert future.cancelled
    assert track['calls'] > 9
    assert time.time() - start > .9


def test_interval_decorator():
    track = {'calls': 0}
    start = time.time()

    task = interval(.1, 3)(coro)
    future = task(track)

    run_in_loop(future)

    assert future.cancelled
    assert track['calls'] == 3
    assert time.time() - start > .2


def test_interval_invalid_input():
    with pytest.raises(TypeError):
        run_in_loop(interval(None))

    with pytest.raises(TypeError):

        run_in_loop(interval(lambda x: x))