import functools
import unittest
from unittest import mock as um

from tornado import ioloop as ti, gen as tg

from acddl import worker


class TestWorker(unittest.TestCase):

    def setUp(self):
        self._worker = worker.Worker()
        self._worker.start()
        self.assertTrue(self._worker.is_alive())

    def tearDown(self):
        async_call(self._worker.stop)
        self.assertFalse(self._worker.is_alive())

    def testDo(self):
        x = [1]
        def fn():
            x[0] = 2
        async def fn_():
            await self._worker.do(fn)
        async_call(fn_)
        self.assertEqual(x[0], 2)


class TestAsyncWorker(unittest.TestCase):

    def setUp(self):
        self._worker = worker.AsyncWorker()
        self._worker.start()
        self.assertTrue(self._worker.is_alive)

    def tearDown(self):
        async_call(self._worker.stop)
        self.assertFalse(self._worker.is_alive)

    def _createSyncMock(self):
        return um.Mock(return_value=42)

    def testDoWithSync(self):
        fn = self._createSyncMock()
        rv = async_call(self._worker.do, fn)
        fn.assert_called_once_with()
        self.assertEqual(rv, 42)

    def testDoWithAsync(self):
        x = [1]
        async def fn():
            await tg.moment
            x[0] = 2
            return 3
        async def fn_():
            return await self._worker.do(fn)
        rv = async_call(fn_)
        self.assertEqual(x[0], 2)
        self.assertEqual(rv, 3)

    def testDoLaterWithSync(self):
        x = [1]
        def fn():
            x[0] = 2
        self._worker.do_later(fn)
        async_call(functools.partial(tg.sleep, 0.001))
        self.assertEqual(x[0], 2)

    def testDoLaterWithAsync(self):
        x = [1]
        async def fn():
            await tg.moment
            x[0] = 2
        self._worker.do_later(fn)
        async_call(functools.partial(tg.sleep, 0.001))
        self.assertEqual(x[0], 2)

    def testDoWithSyncPartial(self):
        x = [1]
        def fn(rv):
            x[0] = 2
            return rv
        async def fn_():
            return await self._worker.do(functools.partial(fn, 3))
        rv = async_call(fn_)
        self.assertEqual(x[0], 2)
        self.assertEqual(rv, 3)

    def testDoWithAsyncPartial(self):
        x = [1]
        async def fn(rv):
            await tg.moment
            x[0] = 2
            return rv
        async def fn_():
            return await self._worker.do(worker.AsyncTask(fn, 3))
        rv = async_call(fn_)
        self.assertEqual(x[0], 2)
        self.assertEqual(rv, 3)

    def testDoLaterWithSyncPartial(self):
        x = [1]
        def fn(v):
            x[0] = v
        self._worker.do_later(functools.partial(fn, 2))
        async_call(functools.partial(tg.sleep, 0.001))
        self.assertEqual(x[0], 2)

    def testDoLaterWithAsyncPartial(self):
        x = [1]
        async def fn(v):
            await tg.moment
            x[0] = v
        self._worker.do_later(worker.AsyncTask(fn, 2))
        async_call(functools.partial(tg.sleep, 0.001))
        self.assertEqual(x[0], 2)


def async_call(fn, *args, **kwargs):
    return ti.IOLoop.instance().run_sync(functools.partial(fn, *args, **kwargs))

@@ -985,24 +985,99 @@
 _value=42)%0A%0A
+    def _createAsyncMock(self):%0A        return AsyncMock(return_value=42)%0A%0A
     def test
@@ -1086,32 +1086,32 @@
 WithSync(self):%0A
-
         fn = sel
@@ -1289,160 +1289,61 @@
     
-x = %5B1%5D%0A        async def fn():%0A            await tg.moment%0A            x%5B0%5D = 2%0A            return 3%0A        async def fn_():%0A            return await 
+fn = self._createAsyncMock()%0A        rv = async_call(
 self
@@ -1345,33 +1345,34 @@
 (self._worker.do
-(
+, 
 fn)%0A        rv =
@@ -1367,35 +1367,43 @@
         
-rv = async_call(fn_
+fn.assert_called_once_with(
 )%0A      
@@ -1408,32 +1408,26 @@
     
-sel
 f
+n
 .assert
-Equal(x%5B0%5D, 2
+_awaited(
 )%0A  
@@ -1445,33 +1445,34 @@
 assertEqual(rv, 
-3
+42
 )%0A%0A    def testD
@@ -3086,16 +3086,393 @@
 %5D, 2)%0A%0A%0A
+class AsyncMock(um.Mock):%0A%0A    def __init__(self, return_value=None):%0A        super(AsyncMock, self).__init__(return_value=self)%0A%0A        self._return_value = return_value%0A        self._awaited = False%0A%0A    def __await__(self):%0A        yield tg.moment%0A        self._awaited = True%0A        return self._return_value%0A%0A    def assert_awaited(self):%0A        assert self._awaited%0A%0A%0A
 def asyn

use mock