# -*- coding: utf8 -*-
from __future__ import unicode_literals

import sys

import pytest


WARNINGS_SUMMARY_HEADER = 'warnings summary'


@pytest.fixture
def pyfile_with_warnings(testdir, request):
    """
    Create a test file which calls a function in a module which generates warnings.
    """
    testdir.syspathinsert()
    test_name = request.function.__name__
    module_name = test_name.lstrip('test_') + '_module'
    testdir.makepyfile(**{
        module_name: '''
            import warnings
            def foo():
                warnings.warn(UserWarning("user warning"))
                warnings.warn(RuntimeWarning("runtime warning"))
                return 1
        ''',
        test_name: '''
            import {module_name}
            def test_func():
                assert {module_name}.foo() == 1
        '''.format(module_name=module_name)
    })


@pytest.mark.filterwarnings('always')
def test_normal_flow(testdir, pyfile_with_warnings):
    """
    Check that the warnings section is displayed, containing test node ids followed by
    all warnings generated by that test node.
    """
    result = testdir.runpytest()
    result.stdout.fnmatch_lines([
        '*== %s ==*' % WARNINGS_SUMMARY_HEADER,

        '*test_normal_flow.py::test_func',

        '*normal_flow_module.py:3: UserWarning: user warning',
        '*  warnings.warn(UserWarning("user warning"))',

        '*normal_flow_module.py:4: RuntimeWarning: runtime warning',
        '*  warnings.warn(RuntimeWarning("runtime warning"))',
        '* 1 passed, 2 warnings*',
    ])
    assert result.stdout.str().count('test_normal_flow.py::test_func') == 1


@pytest.mark.filterwarnings('always')
def test_setup_teardown_warnings(testdir, pyfile_with_warnings):
    testdir.makepyfile('''
        import warnings
        import pytest

        @pytest.fixture
        def fix():
            warnings.warn(UserWarning("warning during setup"))
            yield
            warnings.warn(UserWarning("warning during teardown"))

        def test_func(fix):
            pass
    ''')
    result = testdir.runpytest()
    result.stdout.fnmatch_lines([
        '*== %s ==*' % WARNINGS_SUMMARY_HEADER,

        '*test_setup_teardown_warnings.py:6: UserWarning: warning during setup',
        '*warnings.warn(UserWarning("warning during setup"))',

        '*test_setup_teardown_warnings.py:8: UserWarning: warning during teardown',
        '*warnings.warn(UserWarning("warning during teardown"))',
        '* 1 passed, 2 warnings*',
    ])


@pytest.mark.parametrize('method', ['cmdline', 'ini'])
def test_as_errors(testdir, pyfile_with_warnings, method):
    args = ('-W', 'error') if method == 'cmdline' else ()
    if method == 'ini':
        testdir.makeini('''
            [pytest]
            filterwarnings= error
            ''')
    result = testdir.runpytest(*args)
    result.stdout.fnmatch_lines([
        'E       UserWarning: user warning',
        'as_errors_module.py:3: UserWarning',
        '* 1 failed in *',
    ])


@pytest.mark.parametrize('method', ['cmdline', 'ini'])
def test_ignore(testdir, pyfile_with_warnings, method):
    args = ('-W', 'ignore') if method == 'cmdline' else ()
    if method == 'ini':
        testdir.makeini('''
        [pytest]
        filterwarnings= ignore
        ''')

    result = testdir.runpytest(*args)
    result.stdout.fnmatch_lines([
        '* 1 passed in *',
    ])
    assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()


@pytest.mark.skipif(sys.version_info < (3, 0),
                    reason='warnings message is unicode is ok in python3')
@pytest.mark.filterwarnings('always')
def test_unicode(testdir, pyfile_with_warnings):
    testdir.makepyfile('''
        # -*- coding: utf8 -*-
        import warnings
        import pytest


        @pytest.fixture
        def fix():
            warnings.warn(u"测试")
            yield

        def test_func(fix):
            pass
    ''')
    result = testdir.runpytest()
    result.stdout.fnmatch_lines([
        '*== %s ==*' % WARNINGS_SUMMARY_HEADER,
        '*test_unicode.py:8: UserWarning: \u6d4b\u8bd5*',
        '* 1 passed, 1 warnings*',
    ])


@pytest.mark.skipif(sys.version_info >= (3, 0),
                    reason='warnings message is broken as it is not str instance')
def test_py2_unicode(testdir, pyfile_with_warnings):
    if getattr(sys, "pypy_version_info", ())[:2] == (5, 9) and sys.platform.startswith('win'):
        pytest.xfail("fails with unicode error on PyPy2 5.9 and Windows (#2905)")
    testdir.makepyfile('''
        # -*- coding: utf8 -*-
        import warnings
        import pytest


        @pytest.fixture
        def fix():
            warnings.warn(u"测试")
            yield

        @pytest.mark.filterwarnings('always')
        def test_func(fix):
            pass
    ''')
    result = testdir.runpytest()
    result.stdout.fnmatch_lines([
        '*== %s ==*' % WARNINGS_SUMMARY_HEADER,

        '*test_py2_unicode.py:8: UserWarning: \\u6d4b\\u8bd5',
        '*warnings.warn(u"\u6d4b\u8bd5")',
        '*warnings.py:*: UnicodeWarning: Warning is using unicode non*',
        '* 1 passed, 2 warnings*',
    ])


def test_py2_unicode_ascii(testdir):
    """Ensure that our warning about 'unicode warnings containing non-ascii messages'
    does not trigger with ascii-convertible messages"""
    testdir.makeini('[pytest]')
    testdir.makepyfile('''
        import pytest
        import warnings

        @pytest.mark.filterwarnings('always')
        def test_func():
            warnings.warn(u"hello")
    ''')
    result = testdir.runpytest()
    result.stdout.fnmatch_lines([
        '*== %s ==*' % WARNINGS_SUMMARY_HEADER,
        '*warnings.warn(u"hello")',
        '* 1 passed, 1 warnings in*'
    ])


def test_works_with_filterwarnings(testdir):
    """Ensure our warnings capture does not mess with pre-installed filters (#2430)."""
    testdir.makepyfile('''
        import warnings

        class MyWarning(Warning):
            pass

        warnings.filterwarnings("error", category=MyWarning)

        class TestWarnings(object):
            def test_my_warning(self):
                try:
                    warnings.warn(MyWarning("warn!"))
                    assert False
                except MyWarning:
                    assert True
    ''')
    result = testdir.runpytest()
    result.stdout.fnmatch_lines([
        '*== 1 passed in *',
    ])


@pytest.mark.parametrize('default_config', ['ini', 'cmdline'])
def test_filterwarnings_mark(testdir, default_config):
    """
    Test ``filterwarnings`` mark works and takes precedence over command line and ini options.
    """
    if default_config == 'ini':
        testdir.makeini("""
            [pytest]
            filterwarnings = always
        """)
    testdir.makepyfile("""
        import warnings
        import pytest

        @pytest.mark.filterwarnings('ignore::RuntimeWarning')
        def test_ignore_runtime_warning():
            warnings.warn(RuntimeWarning())

        @pytest.mark.filterwarnings('error')
        def test_warning_error():
            warnings.warn(RuntimeWarning())

        def test_show_warning():
            warnings.warn(RuntimeWarning())
    """)
    result = testdir.runpytest('-W always' if default_config == 'cmdline' else '')
    result.stdout.fnmatch_lines(['*= 1 failed, 2 passed, 1 warnings in *'])


def test_non_string_warning_argument(testdir):
    """Non-str argument passed to warning breaks pytest (#2956)"""
    testdir.makepyfile("""
        import warnings
        import pytest

        def test():
            warnings.warn(UserWarning(1, u'foo'))
    """)
    result = testdir.runpytest('-W', 'always')
    result.stdout.fnmatch_lines(['*= 1 passed, 1 warnings in *'])
