import sys
import logging
import unittest

from testfixtures import LogCapture
from twisted.python.failure import Failure

from scrapy.utils.log import (failure_to_exc_info, TopLevelFormatter,
                              LogCounterHandler, StreamLogger)
from scrapy.utils.test import get_crawler
from scrapy.extensions import telnet


class FailureToExcInfoTest(unittest.TestCase):

    def test_failure(self):
        try:
            0 / 0
        except ZeroDivisionError:
            exc_info = sys.exc_info()
            failure = Failure()

        self.assertTupleEqual(exc_info, failure_to_exc_info(failure))

    def test_non_failure(self):
        self.assertIsNone(failure_to_exc_info('test'))


class TopLevelFormatterTest(unittest.TestCase):

    def setUp(self):
        self.handler = LogCapture()
        self.handler.addFilter(TopLevelFormatter(['test']))

    def test_top_level_logger(self):
        logger = logging.getLogger('test')
        with self.handler as log:
            logger.warning('test log msg')
        log.check(('test', 'WARNING', 'test log msg'))

    def test_children_logger(self):
        logger = logging.getLogger('test.test1')
        with self.handler as log:
            logger.warning('test log msg')
        log.check(('test', 'WARNING', 'test log msg'))

    def test_overlapping_name_logger(self):
        logger = logging.getLogger('test2')
        with self.handler as log:
            logger.warning('test log msg')
        log.check(('test2', 'WARNING', 'test log msg'))

    def test_different_name_logger(self):
        logger = logging.getLogger('different')
        with self.handler as log:
            logger.warning('test log msg')
        log.check(('different', 'WARNING', 'test log msg'))


class LogCounterHandlerTest(unittest.TestCase):

    def setUp(self):
        settings = {'LOG_LEVEL': 'WARNING'}
        if not telnet.TWISTED_CONCH_AVAILABLE:
            # disable it to avoid the extra warning
            settings['TELNETCONSOLE_ENABLED'] = False
        self.logger = logging.getLogger('test')
        self.logger.setLevel(logging.NOTSET)
        self.logger.propagate = False
        self.crawler = get_crawler(settings_dict=settings)
        self.handler = LogCounterHandler(self.crawler)
        self.logger.addHandler(self.handler)

    def tearDown(self):
        self.logger.propagate = True
        self.logger.removeHandler(self.handler)

    def test_init(self):
        self.assertIsNone(self.crawler.stats.get_value('log_count/DEBUG'))
        self.assertIsNone(self.crawler.stats.get_value('log_count/INFO'))
        self.assertIsNone(self.crawler.stats.get_value('log_count/WARNING'))
        self.assertIsNone(self.crawler.stats.get_value('log_count/ERROR'))
        self.assertIsNone(self.crawler.stats.get_value('log_count/CRITICAL'))

    def test_accepted_level(self):
        self.logger.error('test log msg')
        self.assertEqual(self.crawler.stats.get_value('log_count/ERROR'), 1)

    def test_filtered_out_level(self):
        self.logger.debug('test log msg')
        self.assertIsNone(self.crawler.stats.get_value('log_count/INFO'))


class StreamLoggerTest(unittest.TestCase):

    def setUp(self):
        self.stdout = sys.stdout
        logger = logging.getLogger('test')
        logger.setLevel(logging.WARNING)
        sys.stdout = StreamLogger(logger, logging.ERROR)

    def tearDown(self):
        sys.stdout = self.stdout

    def test_redirect(self):
        with LogCapture() as log:
            print('test log msg')
        log.check(('test', 'ERROR', 'test log msg'))

[(32, 'testfixtures.LogCapture', 'LogCapture', False, 'from testfixtures import LogCapture\n'), (36, 'logging.getLogger', 'logging.getLogger', False, 'import logging\n'), (42, 'logging.getLogger', 'logging.getLogger', False, 'import logging\n'), (48, 'logging.getLogger', 'logging.getLogger', False, 'import logging\n'), (54, 'logging.getLogger', 'logging.getLogger', False, 'import logging\n'), (67, 'logging.getLogger', 'logging.getLogger', False, 'import logging\n'), (70, 'scrapy.utils.test.get_crawler', 'get_crawler', False, 'from scrapy.utils.test import get_crawler\n'), (71, 'scrapy.utils.log.LogCounterHandler', 'LogCounterHandler', False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), (98, 'logging.getLogger', 'logging.getLogger', False, 'import logging\n'), (100, 'scrapy.utils.log.StreamLogger', 'StreamLogger', False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), (23, 'scrapy.utils.log.failure_to_exc_info', 'failure_to_exc_info', False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), (26, 'scrapy.utils.log.failure_to_exc_info', 'failure_to_exc_info', False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), (33, 'scrapy.utils.log.TopLevelFormatter', 'TopLevelFormatter', False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), (106, 'testfixtures.LogCapture', 'LogCapture', False, 'from testfixtures import LogCapture\n'), (20, 'sys.exc_info', 'sys.exc_info', False, 'import sys\n'), (21, 'twisted.python.failure.Failure', 'Failure', False, 'from twisted.python.failure import Failure\n')]