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, 23, 32, 35), 'testfixtures.LogCapture', 'LogCapture', ({}, {}), '()', False, 'from testfixtures import LogCapture\n'), ((36, 17, 36, 42), 'logging.getLogger', 'logging.getLogger', ({(36, 35, 36, 41): '"""test"""'}, {}), "('test')", False, 'import logging\n'), ((42, 17, 42, 48), 'logging.getLogger', 'logging.getLogger', ({(42, 35, 42, 47): '"""test.test1"""'}, {}), "('test.test1')", False, 'import logging\n'), ((48, 17, 48, 43), 'logging.getLogger', 'logging.getLogger', ({(48, 35, 48, 42): '"""test2"""'}, {}), "('test2')", False, 'import logging\n'), ((54, 17, 54, 47), 'logging.getLogger', 'logging.getLogger', ({(54, 35, 54, 46): '"""different"""'}, {}), "('different')", False, 'import logging\n'), ((67, 22, 67, 47), 'logging.getLogger', 'logging.getLogger', ({(67, 40, 67, 46): '"""test"""'}, {}), "('test')", False, 'import logging\n'), ((70, 23, 70, 58), 'scrapy.utils.test.get_crawler', 'get_crawler', (), '', False, 'from scrapy.utils.test import get_crawler\n'), ((71, 23, 71, 54), 'scrapy.utils.log.LogCounterHandler', 'LogCounterHandler', ({(71, 41, 71, 53): 'self.crawler'}, {}), '(self.crawler)', False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), ((98, 17, 98, 42), 'logging.getLogger', 'logging.getLogger', ({(98, 35, 98, 41): '"""test"""'}, {}), "('test')", False, 'import logging\n'), ((100, 21, 100, 56), 'scrapy.utils.log.StreamLogger', 'StreamLogger', ({(100, 34, 100, 40): 'logger', (100, 42, 100, 55): 'logging.ERROR'}, {}), '(logger, logging.ERROR)', False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), ((23, 40, 23, 68), 'scrapy.utils.log.failure_to_exc_info', 'failure_to_exc_info', ({(23, 60, 23, 67): 'failure'}, {}), '(failure)', False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), ((26, 26, 26, 53), 'scrapy.utils.log.failure_to_exc_info', 'failure_to_exc_info', ({(26, 46, 26, 52): '"""test"""'}, {}), "('test')", False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), ((33, 31, 33, 58), 'scrapy.utils.log.TopLevelFormatter', 'TopLevelFormatter', ({(33, 49, 33, 57): "['test']"}, {}), "(['test'])", False, 'from scrapy.utils.log import failure_to_exc_info, TopLevelFormatter, LogCounterHandler, StreamLogger\n'), ((106, 13, 106, 25), 'testfixtures.LogCapture', 'LogCapture', ({}, {}), '()', False, 'from testfixtures import LogCapture\n'), ((20, 23, 20, 37), 'sys.exc_info', 'sys.exc_info', ({}, {}), '()', False, 'import sys\n'), ((21, 22, 21, 31), 'twisted.python.failure.Failure', 'Failure', ({}, {}), '()', False, 'from twisted.python.failure import Failure\n')]