async def test_unfinished_tasks_readded():
    q = TaskQueue()
    await wait(q.add((2, 1, 3)))

    batch, tasks = await wait(q.get())

    q.complete(batch, (2, ))

    batch, tasks = await wait(q.get())

    assert tasks == (1, 3)
async def test_get_nowait(tasks, get_size, expected_tasks):
    q = TaskQueue()
    await q.add(tasks)

    batch, tasks = q.get_nowait(get_size)

    assert tasks == expected_tasks

    q.complete(batch, tasks)

    assert all(task not in q for task in tasks)
async def test_cannot_complete_batch_with_wrong_task():
    q = TaskQueue()

    await wait(q.add((1, 2)))

    batch, tasks = await wait(q.get())

    # cannot complete a valid task with a task it wasn't given
    with pytest.raises(ValidationError):
        q.complete(batch, (3, 4))

    # partially invalid completion calls leave the valid task in an incomplete state
    with pytest.raises(ValidationError):
        q.complete(batch, (1, 3))

    assert 1 in q
async def test_cannot_complete_batch_unless_pending():
    q = TaskQueue()

    await wait(q.add((1, 2)))

    # cannot complete a valid task without a batch id
    with pytest.raises(ValidationError):
        q.complete(None, (1, 2))

    assert 1 in q

    batch, tasks = await wait(q.get())

    # cannot complete a valid task with an invalid batch id
    with pytest.raises(ValidationError):
        q.complete(batch + 1, (1, 2))

    assert 1 in q
async def test_queue_contains_task_until_complete(tasks):
    q = TaskQueue(order_fn=id)

    first_task = tasks[0]

    assert first_task not in q

    await wait(q.add(tasks))

    assert first_task in q

    batch, pending_tasks = await wait(q.get())

    assert first_task in q

    q.complete(batch, pending_tasks)

    assert first_task not in q
async def test_queue_size_reset_after_complete():
    q = TaskQueue(maxsize=2)

    await wait(q.add((1, 2)))

    batch, tasks = await wait(q.get())

    # there should not be room to add another task
    try:
        await wait(q.add((3, )))
    except asyncio.TimeoutError:
        pass
    else:
        assert False, "should not be able to add task past maxsize"

    # do imaginary work here, then complete it all

    q.complete(batch, tasks)

    # there should be room to add more now
    await wait(q.add((3, )))
async def test_two_pending_adds_one_release():
    q = TaskQueue(2)

    asyncio.ensure_future(q.add((3, 1, 2)))

    # wait for ^ to run and pause
    await asyncio.sleep(0)
    # note that the highest-priority items are queued first
    assert 1 in q
    assert 2 in q
    assert 3 not in q

    # two tasks are queued, none are started
    assert len(q) == 2
    assert q.num_in_progress() == 0

    asyncio.ensure_future(q.add((0, 4)))
    # wait for ^ to run and pause
    await asyncio.sleep(0)

    # task consumer 1 completes the first two pending
    batch, tasks = await wait(q.get())
    assert tasks == (1, 2)

    # both tasks started
    assert len(q) == 2
    assert q.num_in_progress() == 2

    q.complete(batch, tasks)

    # tasks are drained, but new ones aren't added yet...
    assert q.num_in_progress() == 0
    assert len(q) == 0

    await asyncio.sleep(0.01)

    # Now the tasks are added
    assert q.num_in_progress() == 0
    assert len(q) == 2

    # task consumer 2 gets the next two, in priority order
    batch, tasks = await wait(q.get())

    assert len(tasks) == 2

    assert tasks == (0, 3)

    assert q.num_in_progress() == 2
    assert len(q) == 2

    # clean up, so the pending get() call can complete
    q.complete(batch, tasks)

    # All current tasks finished
    assert q.num_in_progress() == 0

    await asyncio.sleep(0)

    # only task 4 remains
    assert q.num_in_progress() == 0
    assert len(q) == 1