__author__ = 'nickmab'

import nickmab.async_util.threads as thr
from _utils import expect_specific_err as _expect_specific_err

"""To be run by executing py.test in the parent dir"""

def _gen():
    yield 1

def test_funcwrap_no_args():
    f = lambda: 1
    fw = thr.FuncWrap(f)
    fw.run()
    assert fw.result == 1

def test_funcwrap_with_callback():
    f = lambda: 1
    call_result = []
    def cb(fw):
        call_result.append(fw.result)
    fw = thr.FuncWrap(target=f, callback=cb)
    fw.run()
    assert call_result == [1]

def test_funcwrap_args_and_name():
    f = lambda x1, x2: x1 + x2
    fw = thr.FuncWrap(name='test', args=(3, 4), target=f)
    fw.run()
    assert fw.name == 'test'
    assert fw.result == 7

def test_funcwrap_with_kwargs():
    def f(a=0, b=0): return b - a
    fw = thr.FuncWrap(target=f, kwargs={ 'b': 10, 'a': 5 })
    fw.run()
    assert fw.result == 5
    
def test_funcwrap_no_target():
    assert _expect_specific_err(thr.FuncWrap, ValueError)

def test_funcwrap_wrong_type():
    def f(): pass
    assert _expect_specific_err(thr.FuncWrap, TypeError, kwargs={ 'target': f, 'name': 123 })
    assert _expect_specific_err(thr.FuncWrap, TypeError, kwargs={ 'target': 3 })
    assert _expect_specific_err(thr.FuncWrap, TypeError, kwargs={ 'target': f, 'args': 'lol' })
    assert _expect_specific_err(thr.FuncWrap, TypeError, kwargs={ 'target': f, 'kwargs': [1] })

def test_funcwrap_with_exception():
    f = lambda: 1/0
    fw = thr.FuncWrap(target=f)
    fw.run()
    assert fw.result is None
    assert isinstance(fw.exception, ZeroDivisionError)

def test_funcwrap_pool():
    # make some FuncWrap instances
    f = lambda x: 2 * x
    fws = { # this is strange, I know, simply demonstrating that non-list iterables work! 
        thr.FuncWrap(target=f, name='8', args=(4,)): None, 
        thr.FuncWrap(target=f, name='12', args=(6,)): None
    }
    fwp = thr.FuncWrapPool(1, fws)
    fwp.run()
    assert all(int(fw.name) == fw.result for fw in fws)

def test_funcwrap_pool_wrong_type():
    assert _expect_specific_err(thr.FuncWrapPool, ValueError)
    assert _expect_specific_err(thr.FuncWrapPool, TypeError, 
        kwargs={ 'num_workers': 'x', 'func_wrap_instances_iterable': [thr.FuncWrap(target=lambda: None)] })
    assert _expect_specific_err(thr.FuncWrapPool, TypeError, 
        kwargs={ 'func_wrap_instances_iterable': [thr.FuncWrap(target=lambda: None), 55] }) 

def test_threadfunnel():
    def prod(x):
        yield x
    
    output = []
    def cons():
        while True:
            x = yield
            output.append(x)

    tf = thr.ThreadFunnel([prod(i) for i in range(1, 6)], cons, True)
    tf.run()
    assert sum(output) == 15

def test_threadfunnel_wrong_type():
    assert _expect_specific_err(thr.ThreadFunnel, ValueError)
    assert _expect_specific_err(thr.ThreadFunnel, ValueError, args=([_gen()],))
    assert _expect_specific_err(thr.ThreadFunnel, TypeError, 
        args=([1], lambda x: 1, True))
    assert _expect_specific_err(thr.ThreadFunnel, TypeError, 
        args=([_gen()], 10))
    assert _expect_specific_err(thr.ThreadFunnel, TypeError, 
        args=([_gen()], lambda x: 1, 123))
    
def test_threadfunnel_exceptions():
    def bad_gen():
        yield 1
        1/0
    
    def ok_cons():
        while True:
            obj = yield

    def bad_cons():
        while True:
            obj = yield
            1/0

    tf1 = thr.ThreadFunnel([bad_gen()], ok_cons)
    tf2 = thr.ThreadFunnel([_gen()], bad_cons)
    assert _expect_specific_err(tf1.run, thr.ProducerException)
    assert _expect_specific_err(tf2.run, thr.ConsumerException)
