from __future__ import print_function
# Copyright (c) 2008-2013 Simplistix Ltd
# See license.txt for license details.

from doctest import DocTestSuite
from mock import Mock
from testfixtures import Replacer, LogCapture, compare
from unittest import TestSuite, TestCase, makeSuite

from logging import getLogger

from warnings import catch_warnings

root = getLogger()
one = getLogger('one')
two = getLogger('two')
child = getLogger('one.child')


class DemoLogCapture:

    def test_simple(self):  # pragma: no branch
        """
        >>> root.info('some logging')
        >>> print(log_capture)
        root INFO
          some logging
        >>> log_capture.clear()
        >>> print(log_capture)
        No logging captured
        >>> root.info('some more logging')
        >>> print(log_capture)
        root INFO
          some more logging
        """


class TestLogCapture:

    def test_simple(self):  # pragma: no branch
        """
        >>> root.info('before')
        >>> l = LogCapture()
        >>> root.info('during')
        >>> l.uninstall()
        >>> root.info('after')
        >>> print(l)
        root INFO
          during
        """

    def test_specific_logger(self):  # pragma: no branch
        """
        >>> l = LogCapture('one')
        >>> root.info('1')
        >>> one.info('2')
        >>> two.info('3')
        >>> child.info('4')
        >>> l.uninstall()
        >>> print(l)
        one INFO
          2
        one.child INFO
          4
        """

    def test_multiple_loggers(self):  # pragma: no branch
        """
        >>> l = LogCapture(('one.child','two'))
        >>> root.info('1')
        >>> one.info('2')
        >>> two.info('3')
        >>> child.info('4')
        >>> l.uninstall()
        >>> print(l)
        two INFO
          3
        one.child INFO
          4
        """

    def test_simple_manual_install(self):  # pragma: no branch
        """
        >>> l = LogCapture(install=False)
        >>> root.info('before')
        >>> l.install()
        >>> root.info('during')
        >>> l.uninstall()
        >>> root.info('after')
        >>> print(l)
        root INFO
          during
        """

    def test_uninstall(self):  # pragma: no branch
        """
        Lets start off with a couple of loggers:

        >>> root = getLogger()
        >>> child = getLogger('child')

        Lets also record the handlers for these loggers before
        we start the test:

        >>> before_root = root.handlers[:]
        >>> before_child = child.handlers[:]

        Lets also record the levels for the loggers:

        >>> old_root_level=root.level
        >>> old_child_level=child.level

        Now the test:

        >>> try:
        ...    root.setLevel(49)
        ...    child.setLevel(69)
        ...
        ...    l1 = LogCapture()
        ...    l2 = LogCapture('child')
        ...
        ...    root = getLogger()
        ...    root.info('1')
        ...    print('root level during test:',root.level)
        ...    child = getLogger('child')
        ...    print('child level during test:',child.level)
        ...    child.info('2')
        ...    print('l1 contents:')
        ...    print(l1)
        ...    print('l2 contents:')
        ...    print(l2)
        ...
        ...    l2.uninstall()
        ...    l1.uninstall()
        ...
        ...    print('root level after test:',root.level)
        ...    print('child level after test:',child.level)
        ...
        ... finally:
        ...    root.setLevel(old_root_level)
        ...    child.setLevel(old_child_level)
        root level during test: 1
        child level during test: 1
        l1 contents:
        root INFO
          1
        child INFO
          2
        l2 contents:
        child INFO
          2
        root level after test: 49
        child level after test: 69

        Now we check the handlers are as they were before
        the test:

        >>> root.handlers == before_root
        True
        >>> child.handlers == before_child
        True
        """

    def test_uninstall_all(self):  # pragma: no branch
        """
        For this test, it's better if we don't have any
        LogCaptures around when we start:

        >>> log_capture.uninstall()

        If you create several LogCaptures during a doctest,
        it can create clutter to uninstall them all.
        If this is the case, use the classmethod
        LogCapture.uninstall_all() as a tearDown function
        to remove them all:

        >>> before_handlers_root = root.handlers[:]
        >>> before_handlers_child = child.handlers[:]

        >>> l1 = LogCapture()
        >>> l2 = LogCapture('one.child')

        We can see that the LogCaptures have changed the
        handlers, removing existing ones and installing
        their own:

        >>> len(root.handlers)
        1
        >>> root.handlers==before_handlers_root
        False
        >>> len(child.handlers)
        1
        >>> child.handlers==before_handlers_child
        False

        Now we show the function in action:

        >>> LogCapture.uninstall_all()

        ...and we can see the handlers are back as
        they were beefore:

        >>> before_handlers_root == root.handlers
        True
        >>> before_handlers_child == child.handlers
        True
        """

    def test_two_logcaptures_on_same_logger(self):  # pragma: no branch
        """
        If you create more than one LogCaptures on a single
        logger, the 2nd one installed will stop the first
        one working!

        >>> l1 = LogCapture()
        >>> root.info('1st message')
        >>> print(l1)
        root INFO
          1st message
        >>> l2 = LogCapture()
        >>> root.info('2nd message')

        So, l1 missed this message:

        >>> print(l1)
        root INFO
          1st message

        ...because l2 kicked it out and recorded the message:

        >>> print(l2)
        root INFO
          2nd message
        """

    def test_uninstall_more_than_once(self):  # pragma: no branch
        """
        For this test, it's better if we don't have any
        LogCaptures around when we start:

        >>> log_capture.uninstall()

        There's no problem with uninstalling a LogCapture
        more than once:

        >>> old_level = root.level
        >>> try:
        ...    root.setLevel(49)
        ...
        ...    l = LogCapture()
        ...
        ...    print('root level during test:',root.level)
        ...
        ...    l.uninstall()
        ...
        ...    print('root level after uninstall:',root.level)
        ...
        ...    root.setLevel(69)
        ...
        ...    l.uninstall()
        ...
        ...    print('root level after another uninstall:',root.level)
        ...
        ... finally:
        ...    root.setLevel(old_level)
        root level during test: 1
        root level after uninstall: 49
        root level after another uninstall: 69

        And even when loggers have been uninstalled, there's
        no problem having uninstall_all as a backstop:

        >>> log_capture.uninstall_all()
        """

    def test_with_statement(self):  # pragma: no branch
        """
        >>> root.info('before')
        >>> with LogCapture() as l:
        ...   root.info('during')
        >>> root.info('after')
        >>> print(l)
        root INFO
          during
        """


class LogCaptureTests(TestCase):

    def test_remove_existing_handlers(self):
        logger = getLogger()
        # get original handlers
        original_handlers = logger.handlers
        # put in a stub which will blow up if used
        try:
            logger.handlers = start = [object()]

            with LogCapture() as l:
                logger.info('during')  # pragma: no branch

            l.check(('root', 'INFO', 'during'))

            compare(logger.handlers, start)

        finally:
            # only executed if the test fails
            logger.handlers = original_handlers

    def test_atexit(self):
        # http://bugs.python.org/issue25532
        from mock import call

        m = Mock()
        with Replacer() as r:
            # make sure the marker is false, other tests will
            # probably have set it
            r.replace('testfixtures.LogCapture.atexit_setup', False)
            r.replace('atexit.register', m.register)

            l = LogCapture()

            expected = [call.register(l.atexit)]

            compare(expected, m.mock_calls)

            with catch_warnings(record=True) as w:
                l.atexit()
                self.assertTrue(len(w), 1)
                compare(str(w[0].message), (  # pragma: no branch
                    "LogCapture instances not uninstalled by shutdown, "
                    "loggers captured:\n"
                    "(None,)"
                    ))

            l.uninstall()

            compare(set(), LogCapture.instances)

            # check re-running has no ill effects
            l.atexit()

    def test_numeric_log_level(self):
        with LogCapture() as log:
            getLogger().log(42, 'running in the family')

        log.check(('root', 'Level 42', 'running in the family'))

    def test_enable_disabled_logger(self):
        logger = getLogger('disabled')
        logger.disabled = True
        with LogCapture('disabled') as log:
            logger.info('a log message')
        log.check(('disabled', 'INFO', 'a log message'))
        compare(logger.disabled, True)

    def test_no_propogate(self):
        logger = getLogger('child')
        # paranoid check
        compare(logger.propagate, True)
        with LogCapture() as global_log:
            with LogCapture('child', propagate=False) as child_log:
                logger.info('a log message')
                child_log.check(('child', 'INFO', 'a log message'))
        global_log.check()
        compare(logger.propagate, True)

# using a set up and teardown function
# gets rid of the need for the imports in
# doc tests


def setUp(test):
    test.globs['log_capture'] = LogCapture()


def tearDown(test):
    test.globs['log_capture'].uninstall_all()


def test_suite():
    return TestSuite((
        DocTestSuite(setUp=setUp, tearDown=tearDown),
        makeSuite(LogCaptureTests),
        ))
