# -*- coding: utf-8 -
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.

import unittest
import pytest

aiohttp = pytest.importorskip("aiohttp")
WSGIServerHttpProtocol = pytest.importorskip("aiohttp.wsgi.WSGIServerHttpProtocol")

import asyncio
from gunicorn.workers import gaiohttp
from gunicorn.workers._gaiohttp import _wrp
from gunicorn.config import Config
from unittest import mock


class WorkerTests(unittest.TestCase):

    def setUp(self):
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(None)
        self.worker = gaiohttp.AiohttpWorker('age',
                                             'ppid',
                                             'sockets',
                                             'app',
                                             'timeout',
                                             Config(),
                                             'log')

    def tearDown(self):
        self.loop.close()

    @mock.patch('gunicorn.workers._gaiohttp.asyncio')
    def test_init_process(self, m_asyncio):
        try:
            self.worker.init_process()
        except TypeError:
            # to mask incomplete initialization of AiohttWorker instance:
            # we pass invalid values for ctor args
            pass

        self.assertTrue(m_asyncio.get_event_loop.return_value.close.called)
        self.assertTrue(m_asyncio.new_event_loop.called)
        self.assertTrue(m_asyncio.set_event_loop.called)

    @mock.patch('gunicorn.workers._gaiohttp.asyncio')
    def test_run(self, m_asyncio):
        self.worker.loop = mock.Mock()
        self.worker.run()

        self.assertTrue(m_asyncio.ensure_future.called)
        self.assertTrue(self.worker.loop.run_until_complete.called)
        self.assertTrue(self.worker.loop.close.called)

    def test_factory(self):
        self.worker.wsgi = mock.Mock()
        self.worker.loop = mock.Mock()
        self.worker.log = mock.Mock()
        self.worker.cfg = Config()

        f = self.worker.factory(
            self.worker.wsgi, ('localhost', 8080))
        self.assertIsInstance(f, WSGIServerHttpProtocol)

    @mock.patch('gunicorn.workers._gaiohttp.asyncio')
    def test__run(self, m_asyncio):
        self.worker.ppid = 1
        self.worker.alive = True
        self.worker.servers = []
        sock = mock.Mock()
        sock.cfg_addr = ('localhost', 8080)
        self.worker.sockets = [sock]
        self.worker.wsgi = mock.Mock()
        self.worker.log = mock.Mock()
        self.worker.notify = mock.Mock()
        loop = self.worker.loop = mock.Mock()
        loop.create_server.return_value = asyncio.Future(loop=self.loop)
        loop.create_server.return_value.set_result(sock)

        self.loop.run_until_complete(self.worker._run())

        self.assertTrue(self.worker.log.info.called)
        self.assertTrue(self.worker.notify.called)

    @mock.patch('gunicorn.workers._gaiohttp.asyncio')
    def test__run_unix_socket(self, m_asyncio):
        self.worker.ppid = 1
        self.worker.alive = True
        self.worker.servers = []
        sock = mock.Mock()
        sock.cfg_addr = '/tmp/gunicorn.sock'
        self.worker.sockets = [sock]
        self.worker.wsgi = mock.Mock()
        self.worker.log = mock.Mock()
        self.worker.notify = mock.Mock()
        loop = self.worker.loop = mock.Mock()
        loop.create_server.return_value = asyncio.Future(loop=self.loop)
        loop.create_server.return_value.set_result(sock)

        self.loop.run_until_complete(self.worker._run())

        self.assertTrue(self.worker.log.info.called)
        self.assertTrue(self.worker.notify.called)

    def test__run_connections(self):
        conn = mock.Mock()
        self.worker.ppid = 1
        self.worker.alive = False
        self.worker.servers = [mock.Mock()]
        self.worker.connections = {1: conn}
        self.worker.sockets = []
        self.worker.wsgi = mock.Mock()
        self.worker.log = mock.Mock()
        self.worker.loop = self.loop
        self.worker.loop.create_server = mock.Mock()
        self.worker.notify = mock.Mock()

        def _close_conns():
            self.worker.connections = {}

        self.loop.call_later(0.1, _close_conns)
        self.loop.run_until_complete(self.worker._run())

        self.assertTrue(self.worker.log.info.called)
        self.assertTrue(self.worker.notify.called)
        self.assertFalse(self.worker.servers)
        self.assertTrue(conn.closing.called)

    @mock.patch('gunicorn.workers._gaiohttp.os')
    @mock.patch('gunicorn.workers._gaiohttp.asyncio.sleep')
    def test__run_exc(self, m_sleep, m_os):
        m_os.getpid.return_value = 1
        m_os.getppid.return_value = 1

        self.worker.servers = [mock.Mock()]
        self.worker.ppid = 1
        self.worker.alive = True
        self.worker.sockets = []
        self.worker.log = mock.Mock()
        self.worker.loop = mock.Mock()
        self.worker.notify = mock.Mock()

        slp = asyncio.Future(loop=self.loop)
        slp.set_exception(KeyboardInterrupt)
        m_sleep.return_value = slp

        self.loop.run_until_complete(self.worker._run())
        self.assertTrue(m_sleep.called)
        self.assertTrue(self.worker.servers[0].close.called)

    def test_close_wsgi_app(self):
        self.worker.ppid = 1
        self.worker.alive = False
        self.worker.servers = [mock.Mock()]
        self.worker.connections = {}
        self.worker.sockets = []
        self.worker.log = mock.Mock()
        self.worker.loop = self.loop
        self.worker.loop.create_server = mock.Mock()
        self.worker.notify = mock.Mock()

        self.worker.wsgi = mock.Mock()
        self.worker.wsgi.close.return_value = asyncio.Future(loop=self.loop)
        self.worker.wsgi.close.return_value.set_result(1)

        self.loop.run_until_complete(self.worker._run())
        self.assertTrue(self.worker.wsgi.close.called)

        self.worker.wsgi = mock.Mock()
        self.worker.wsgi.close.return_value = asyncio.Future(loop=self.loop)
        self.worker.wsgi.close.return_value.set_exception(ValueError())

        self.loop.run_until_complete(self.worker._run())
        self.assertTrue(self.worker.wsgi.close.called)

    def test_wrp(self):
        conn = object()
        tracking = {}
        meth = mock.Mock()
        wrp = _wrp(conn, meth, tracking)
        wrp()

        self.assertIn(id(conn), tracking)
        self.assertTrue(meth.called)

        meth = mock.Mock()
        wrp = _wrp(conn, meth, tracking, False)
        wrp()

        self.assertNotIn(1, tracking)
        self.assertTrue(meth.called)
