from __future__ import absolute_import
from __future__ import unicode_literals

import six
from docker.errors import APIError

from compose.parallel import parallel_execute
from compose.parallel import parallel_execute_iter
from compose.parallel import UpstreamError


web = 'web'
db = 'db'
data_volume = 'data_volume'
cache = 'cache'

objects = [web, db, data_volume, cache]

deps = {
    web: [db, cache],
    db: [data_volume],
    data_volume: [],
    cache: [],
}


def get_deps(obj):
    return [(dep, None) for dep in deps[obj]]


def test_parallel_execute():
    results, errors = parallel_execute(
        objects=[1, 2, 3, 4, 5],
        func=lambda x: x * 2,
        get_name=six.text_type,
        msg="Doubling",
    )

    assert sorted(results) == [2, 4, 6, 8, 10]
    assert errors == {}


def test_parallel_execute_with_deps():
    log = []

    def process(x):
        log.append(x)

    parallel_execute(
        objects=objects,
        func=process,
        get_name=lambda obj: obj,
        msg="Processing",
        get_deps=get_deps,
    )

    assert sorted(log) == sorted(objects)

    assert log.index(data_volume) < log.index(db)
    assert log.index(db) < log.index(web)
    assert log.index(cache) < log.index(web)


def test_parallel_execute_with_upstream_errors():
    log = []

    def process(x):
        if x is data_volume:
            raise APIError(None, None, "Something went wrong")
        log.append(x)

    parallel_execute(
        objects=objects,
        func=process,
        get_name=lambda obj: obj,
        msg="Processing",
        get_deps=get_deps,
    )

    assert log == [cache]

    events = [
        (obj, result, type(exception))
        for obj, result, exception
        in parallel_execute_iter(objects, process, get_deps, None)
    ]

    assert (cache, None, type(None)) in events
    assert (data_volume, None, APIError) in events
    assert (db, None, UpstreamError) in events
    assert (web, None, UpstreamError) in events

@@ -73,16 +73,44 @@
 terals%0A%0A
+from threading import Lock%0A%0A
 import s
@@ -823,24 +823,545 @@
 ors == %7B%7D%0A%0A%0A
+def test_parallel_execute_with_limit():%0A    limit = 1%0A    tasks = 20%0A    lock = Lock()%0A%0A    def f(obj):%0A        locked = lock.acquire(False)%0A        # we should always get the lock because we're the only thread running%0A        assert locked%0A        lock.release()%0A        return None%0A%0A    results, errors = parallel_execute(%0A        objects=list(range(tasks)),%0A        func=f,%0A        get_name=six.text_type,%0A        msg=%22Testing%22,%0A        limit=limit,%0A    )%0A%0A    assert results == tasks*%5BNone%5D%0A    assert errors == %7B%7D%0A%0A%0A
 def test_par

Test for parallel_execute with limit