# -*- 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 *"])
