# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
import os
import sys

import six

import _pytest._code
import py
import pytest
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR


class TestGeneralUsage(object):
    def test_config_error(self, testdir):
        testdir.makeconftest("""
            def pytest_configure(config):
                import pytest
                raise pytest.UsageError("hello")
        """)
        result = testdir.runpytest(testdir.tmpdir)
        assert result.ret != 0
        result.stderr.fnmatch_lines([
            '*ERROR: hello'
        ])

    def test_root_conftest_syntax_error(self, testdir):
        testdir.makepyfile(conftest="raise SyntaxError\n")
        result = testdir.runpytest()
        result.stderr.fnmatch_lines(["*raise SyntaxError*"])
        assert result.ret != 0

    def test_early_hook_error_issue38_1(self, testdir):
        testdir.makeconftest("""
            def pytest_sessionstart():
                0 / 0
        """)
        result = testdir.runpytest(testdir.tmpdir)
        assert result.ret != 0
        # tracestyle is native by default for hook failures
        result.stdout.fnmatch_lines([
            '*INTERNALERROR*File*conftest.py*line 2*',
            '*0 / 0*',
        ])
        result = testdir.runpytest(testdir.tmpdir, "--fulltrace")
        assert result.ret != 0
        # tracestyle is native by default for hook failures
        result.stdout.fnmatch_lines([
            '*INTERNALERROR*def pytest_sessionstart():*',
            '*INTERNALERROR*0 / 0*',
        ])

    def test_early_hook_configure_error_issue38(self, testdir):
        testdir.makeconftest("""
            def pytest_configure():
                0 / 0
        """)
        result = testdir.runpytest(testdir.tmpdir)
        assert result.ret != 0
        # here we get it on stderr
        result.stderr.fnmatch_lines([
            '*INTERNALERROR*File*conftest.py*line 2*',
            '*0 / 0*',
        ])

    def test_file_not_found(self, testdir):
        result = testdir.runpytest("asd")
        assert result.ret != 0
        result.stderr.fnmatch_lines(["ERROR: file not found*asd"])

    def test_file_not_found_unconfigure_issue143(self, testdir):
        testdir.makeconftest("""
            def pytest_configure():
                print("---configure")
            def pytest_unconfigure():
                print("---unconfigure")
        """)
        result = testdir.runpytest("-s", "asd")
        assert result.ret == 4  # EXIT_USAGEERROR
        result.stderr.fnmatch_lines(["ERROR: file not found*asd"])
        result.stdout.fnmatch_lines([
            "*---configure",
            "*---unconfigure",
        ])

    def test_config_preparse_plugin_option(self, testdir):
        testdir.makepyfile(pytest_xyz="""
            def pytest_addoption(parser):
                parser.addoption("--xyz", dest="xyz", action="store")
        """)
        testdir.makepyfile(test_one="""
            def test_option(pytestconfig):
                assert pytestconfig.option.xyz == "123"
        """)
        result = testdir.runpytest("-p", "pytest_xyz", "--xyz=123", syspathinsert=True)
        assert result.ret == 0
        result.stdout.fnmatch_lines([
            '*1 passed*',
        ])

    def test_assertion_magic(self, testdir):
        p = testdir.makepyfile("""
            def test_this():
                x = 0
                assert x
        """)
        result = testdir.runpytest(p)
        result.stdout.fnmatch_lines([
            ">       assert x",
            "E       assert 0",
        ])
        assert result.ret == 1

    def test_nested_import_error(self, testdir):
        p = testdir.makepyfile("""
                import import_fails
                def test_this():
                    assert import_fails.a == 1
        """)
        testdir.makepyfile(import_fails="import does_not_work")
        result = testdir.runpytest(p)
        result.stdout.fnmatch_lines([
            # XXX on jython this fails:  ">   import import_fails",
            "ImportError while importing test module*",
            "*No module named *does_not_work*",
        ])
        assert result.ret == 2

    def test_not_collectable_arguments(self, testdir):
        p1 = testdir.makepyfile("")
        p2 = testdir.makefile(".pyc", "123")
        result = testdir.runpytest(p1, p2)
        assert result.ret
        result.stderr.fnmatch_lines([
            "*ERROR: not found:*%s" % (p2.basename,)
        ])

    def test_issue486_better_reporting_on_conftest_load_failure(self, testdir):
        testdir.makepyfile("")
        testdir.makeconftest("import qwerty")
        result = testdir.runpytest("--help")
        result.stdout.fnmatch_lines("""
            *--version*
            *warning*conftest.py*
        """)
        result = testdir.runpytest()
        result.stderr.fnmatch_lines("""
            *ERROR*could not load*conftest.py*
        """)

    def test_early_skip(self, testdir):
        testdir.mkdir("xyz")
        testdir.makeconftest("""
            import pytest
            def pytest_collect_directory():
                pytest.skip("early")
        """)
        result = testdir.runpytest()
        assert result.ret == EXIT_NOTESTSCOLLECTED
        result.stdout.fnmatch_lines([
            "*1 skip*"
        ])

    def test_issue88_initial_file_multinodes(self, testdir):
        testdir.makeconftest("""
            import pytest
            class MyFile(pytest.File):
                def collect(self):
                    return [MyItem("hello", parent=self)]
            def pytest_collect_file(path, parent):
                return MyFile(path, parent)
            class MyItem(pytest.Item):
                pass
        """)
        p = testdir.makepyfile("def test_hello(): pass")
        result = testdir.runpytest(p, "--collect-only")
        result.stdout.fnmatch_lines([
            "*MyFile*test_issue88*",
            "*Module*test_issue88*",
        ])

    def test_issue93_initialnode_importing_capturing(self, testdir):
        testdir.makeconftest("""
            import sys
            print ("should not be seen")
            sys.stderr.write("stder42\\n")
        """)
        result = testdir.runpytest()
        assert result.ret == EXIT_NOTESTSCOLLECTED
        assert "should not be seen" not in result.stdout.str()
        assert "stderr42" not in result.stderr.str()

    def test_conftest_printing_shows_if_error(self, testdir):
        testdir.makeconftest("""
            print ("should be seen")
            assert 0
        """)
        result = testdir.runpytest()
        assert result.ret != 0
        assert "should be seen" in result.stdout.str()

    @pytest.mark.skipif(not hasattr(py.path.local, 'mksymlinkto'),
                        reason="symlink not available on this platform")
    def test_chdir(self, testdir):
        testdir.tmpdir.join("py").mksymlinkto(py._pydir)
        p = testdir.tmpdir.join("main.py")
        p.write(_pytest._code.Source("""
            import sys, os
            sys.path.insert(0, '')
            import py
            print (py.__file__)
            print (py.__path__)
            os.chdir(os.path.dirname(os.getcwd()))
            print (py.log)
        """))
        result = testdir.runpython(p)
        assert not result.ret

    def test_issue109_sibling_conftests_not_loaded(self, testdir):
        sub1 = testdir.mkdir("sub1")
        sub2 = testdir.mkdir("sub2")
        sub1.join("conftest.py").write("assert 0")
        result = testdir.runpytest(sub2)
        assert result.ret == EXIT_NOTESTSCOLLECTED
        sub2.ensure("__init__.py")
        p = sub2.ensure("test_hello.py")
        result = testdir.runpytest(p)
        assert result.ret == EXIT_NOTESTSCOLLECTED
        result = testdir.runpytest(sub1)
        assert result.ret == EXIT_USAGEERROR

    def test_directory_skipped(self, testdir):
        testdir.makeconftest("""
            import pytest
            def pytest_ignore_collect():
                pytest.skip("intentional")
        """)
        testdir.makepyfile("def test_hello(): pass")
        result = testdir.runpytest()
        assert result.ret == EXIT_NOTESTSCOLLECTED
        result.stdout.fnmatch_lines([
            "*1 skipped*"
        ])

    def test_multiple_items_per_collector_byid(self, testdir):
        c = testdir.makeconftest("""
            import pytest
            class MyItem(pytest.Item):
                def runtest(self):
                    pass
            class MyCollector(pytest.File):
                def collect(self):
                    return [MyItem(name="xyz", parent=self)]
            def pytest_collect_file(path, parent):
                if path.basename.startswith("conftest"):
                    return MyCollector(path, parent)
        """)
        result = testdir.runpytest(c.basename + "::" + "xyz")
        assert result.ret == 0
        result.stdout.fnmatch_lines([
            "*1 pass*",
        ])

    def test_skip_on_generated_funcarg_id(self, testdir):
        testdir.makeconftest("""
            import pytest
            def pytest_generate_tests(metafunc):
                metafunc.addcall({'x': 3}, id='hello-123')
            def pytest_runtest_setup(item):
                print (item.keywords)
                if 'hello-123' in item.keywords:
                    pytest.skip("hello")
                assert 0
        """)
        p = testdir.makepyfile("""def test_func(x): pass""")
        res = testdir.runpytest(p)
        assert res.ret == 0
        res.stdout.fnmatch_lines(["*1 skipped*"])

    def test_direct_addressing_selects(self, testdir):
        p = testdir.makepyfile("""
            def pytest_generate_tests(metafunc):
                metafunc.addcall({'i': 1}, id="1")
                metafunc.addcall({'i': 2}, id="2")
            def test_func(i):
                pass
        """)
        res = testdir.runpytest(p.basename + "::" + "test_func[1]")
        assert res.ret == 0
        res.stdout.fnmatch_lines(["*1 passed*"])

    def test_direct_addressing_notfound(self, testdir):
        p = testdir.makepyfile("""
            def test_func():
                pass
        """)
        res = testdir.runpytest(p.basename + "::" + "test_notfound")
        assert res.ret
        res.stderr.fnmatch_lines(["*ERROR*not found*"])

    def test_docstring_on_hookspec(self):
        from _pytest import hookspec
        for name, value in vars(hookspec).items():
            if name.startswith("pytest_"):
                assert value.__doc__, "no docstring for %s" % name

    def test_initialization_error_issue49(self, testdir):
        testdir.makeconftest("""
            def pytest_configure():
                x
        """)
        result = testdir.runpytest()
        assert result.ret == 3  # internal error
        result.stderr.fnmatch_lines([
            "INTERNAL*pytest_configure*",
            "INTERNAL*x*",
        ])
        assert 'sessionstarttime' not in result.stderr.str()

    @pytest.mark.parametrize('lookfor', ['test_fun.py::test_a'])
    def test_issue134_report_error_when_collecting_member(self, testdir, lookfor):
        testdir.makepyfile(test_fun="""
            def test_a():
                pass
            def""")
        result = testdir.runpytest(lookfor)
        result.stdout.fnmatch_lines(['*SyntaxError*'])
        if '::' in lookfor:
            result.stderr.fnmatch_lines([
                '*ERROR*',
            ])
            assert result.ret == 4  # usage error only if item not found

    def test_report_all_failed_collections_initargs(self, testdir):
        testdir.makepyfile(test_a="def", test_b="def")
        result = testdir.runpytest("test_a.py::a", "test_b.py::b")
        result.stderr.fnmatch_lines([
            "*ERROR*test_a.py::a*",
            "*ERROR*test_b.py::b*",
        ])

    @pytest.mark.usefixtures('recwarn')
    def test_namespace_import_doesnt_confuse_import_hook(self, testdir):
        """
        Ref #383. Python 3.3's namespace package messed with our import hooks
        Importing a module that didn't exist, even if the ImportError was
        gracefully handled, would make our test crash.

        Use recwarn here to silence this warning in Python 2.7:
            ImportWarning: Not importing directory '...\not_a_package': missing __init__.py
        """
        testdir.mkdir('not_a_package')
        p = testdir.makepyfile("""
            try:
                from not_a_package import doesnt_exist
            except ImportError:
                # We handle the import error gracefully here
                pass

            def test_whatever():
                pass
        """)
        res = testdir.runpytest(p.basename)
        assert res.ret == 0

    def test_unknown_option(self, testdir):
        result = testdir.runpytest("--qwlkej")
        result.stderr.fnmatch_lines("""
            *unrecognized*
        """)

    def test_getsourcelines_error_issue553(self, testdir, monkeypatch):
        monkeypatch.setattr("inspect.getsourcelines", None)
        p = testdir.makepyfile("""
            def raise_error(obj):
                raise IOError('source code not available')

            import inspect
            inspect.getsourcelines = raise_error

            def test_foo(invalid_fixture):
                pass
        """)
        res = testdir.runpytest(p)
        res.stdout.fnmatch_lines([
            "*source code not available*",
            "E*fixture 'invalid_fixture' not found",
        ])

    def test_plugins_given_as_strings(self, tmpdir, monkeypatch):
        """test that str values passed to main() as `plugins` arg
        are interpreted as module names to be imported and registered.
        #855.
        """
        with pytest.raises(ImportError) as excinfo:
            pytest.main([str(tmpdir)], plugins=['invalid.module'])
        assert 'invalid' in str(excinfo.value)

        p = tmpdir.join('test_test_plugins_given_as_strings.py')
        p.write('def test_foo(): pass')
        mod = py.std.types.ModuleType("myplugin")
        monkeypatch.setitem(sys.modules, 'myplugin', mod)
        assert pytest.main(args=[str(tmpdir)], plugins=['myplugin']) == 0

    def test_parametrized_with_bytes_regex(self, testdir):
        p = testdir.makepyfile("""
            import re
            import pytest
            @pytest.mark.parametrize('r', [re.compile(b'foo')])
            def test_stuff(r):
                pass
        """
                               )
        res = testdir.runpytest(p)
        res.stdout.fnmatch_lines([
            '*1 passed*'
        ])

    def test_parametrized_with_null_bytes(self, testdir):
        """Test parametrization with values that contain null bytes and unicode characters (#2644, #2957)"""
        p = testdir.makepyfile(u"""
            # encoding: UTF-8
            import pytest

            @pytest.mark.parametrize("data", [b"\\x00", "\\x00", u'ação'])
            def test_foo(data):
                assert data
        """)
        res = testdir.runpytest(p)
        res.assert_outcomes(passed=3)


class TestInvocationVariants(object):
    def test_earlyinit(self, testdir):
        p = testdir.makepyfile("""
            import pytest
            assert hasattr(pytest, 'mark')
        """)
        result = testdir.runpython(p)
        assert result.ret == 0

    @pytest.mark.xfail("sys.platform.startswith('java')")
    def test_pydoc(self, testdir):
        for name in ('py.test', 'pytest'):
            result = testdir.runpython_c("import %s;help(%s)" % (name, name))
            assert result.ret == 0
            s = result.stdout.str()
            assert 'MarkGenerator' in s

    def test_import_star_py_dot_test(self, testdir):
        p = testdir.makepyfile("""
            from py.test import *
            #collect
            #cmdline
            #Item
            # assert collect.Item is Item
            # assert collect.Collector is Collector
            main
            skip
            xfail
        """)
        result = testdir.runpython(p)
        assert result.ret == 0

    def test_import_star_pytest(self, testdir):
        p = testdir.makepyfile("""
            from pytest import *
            #Item
            #File
            main
            skip
            xfail
        """)
        result = testdir.runpython(p)
        assert result.ret == 0

    def test_double_pytestcmdline(self, testdir):
        p = testdir.makepyfile(run="""
            import pytest
            pytest.main()
            pytest.main()
        """)
        testdir.makepyfile("""
            def test_hello():
                pass
        """)
        result = testdir.runpython(p)
        result.stdout.fnmatch_lines([
            "*1 passed*",
            "*1 passed*",
        ])

    def test_python_minus_m_invocation_ok(self, testdir):
        p1 = testdir.makepyfile("def test_hello(): pass")
        res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1))
        assert res.ret == 0

    def test_python_minus_m_invocation_fail(self, testdir):
        p1 = testdir.makepyfile("def test_fail(): 0/0")
        res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1))
        assert res.ret == 1

    def test_python_pytest_package(self, testdir):
        p1 = testdir.makepyfile("def test_pass(): pass")
        res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1))
        assert res.ret == 0
        res.stdout.fnmatch_lines(["*1 passed*"])

    def test_equivalence_pytest_pytest(self):
        assert pytest.main == py.test.cmdline.main

    def test_invoke_with_string(self, capsys):
        retcode = pytest.main("-h")
        assert not retcode
        out, err = capsys.readouterr()
        assert "--help" in out
        pytest.raises(ValueError, lambda: pytest.main(0))

    def test_invoke_with_path(self, tmpdir, capsys):
        retcode = pytest.main(tmpdir)
        assert retcode == EXIT_NOTESTSCOLLECTED
        out, err = capsys.readouterr()

    def test_invoke_plugin_api(self, testdir, capsys):
        class MyPlugin(object):
            def pytest_addoption(self, parser):
                parser.addoption("--myopt")

        pytest.main(["-h"], plugins=[MyPlugin()])
        out, err = capsys.readouterr()
        assert "--myopt" in out

    def test_pyargs_importerror(self, testdir, monkeypatch):
        monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', False)
        path = testdir.mkpydir("tpkg")
        path.join("test_hello.py").write('raise ImportError')

        result = testdir.runpytest_subprocess("--pyargs", "tpkg.test_hello")
        assert result.ret != 0

        result.stdout.fnmatch_lines([
            "collected*0*items*/*1*errors"
        ])

    def test_cmdline_python_package(self, testdir, monkeypatch):
        import warnings
        monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', False)
        path = testdir.mkpydir("tpkg")
        path.join("test_hello.py").write("def test_hello(): pass")
        path.join("test_world.py").write("def test_world(): pass")
        result = testdir.runpytest("--pyargs", "tpkg")
        assert result.ret == 0
        result.stdout.fnmatch_lines([
            "*2 passed*"
        ])
        result = testdir.runpytest("--pyargs", "tpkg.test_hello")
        assert result.ret == 0
        result.stdout.fnmatch_lines([
            "*1 passed*"
        ])

        def join_pythonpath(what):
            cur = py.std.os.environ.get('PYTHONPATH')
            if cur:
                return str(what) + os.pathsep + cur
            return what
        empty_package = testdir.mkpydir("empty_package")
        monkeypatch.setenv('PYTHONPATH', join_pythonpath(empty_package))
        # the path which is not a package raises a warning on pypy;
        # no idea why only pypy and not normal python warn about it here
        with warnings.catch_warnings():
            warnings.simplefilter('ignore', ImportWarning)
            result = testdir.runpytest("--pyargs", ".")
        assert result.ret == 0
        result.stdout.fnmatch_lines([
            "*2 passed*"
        ])

        monkeypatch.setenv('PYTHONPATH', join_pythonpath(testdir))
        result = testdir.runpytest("--pyargs", "tpkg.test_missing")
        assert result.ret != 0
        result.stderr.fnmatch_lines([
            "*not*found*test_missing*",
        ])

    def test_cmdline_python_namespace_package(self, testdir, monkeypatch):
        """
        test --pyargs option with namespace packages (#1567)
        """
        monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', raising=False)

        search_path = []
        for dirname in "hello", "world":
            d = testdir.mkdir(dirname)
            search_path.append(d)
            ns = d.mkdir("ns_pkg")
            ns.join("__init__.py").write(
                "__import__('pkg_resources').declare_namespace(__name__)")
            lib = ns.mkdir(dirname)
            lib.ensure("__init__.py")
            lib.join("test_{0}.py".format(dirname)). \
                write("def test_{0}(): pass\n"
                      "def test_other():pass".format(dirname))

        # The structure of the test directory is now:
        # .
        # ├── hello
        # │   └── ns_pkg
        # │       ├── __init__.py
        # │       └── hello
        # │           ├── __init__.py
        # │           └── test_hello.py
        # └── world
        #     └── ns_pkg
        #         ├── __init__.py
        #         └── world
        #             ├── __init__.py
        #             └── test_world.py

        def join_pythonpath(*dirs):
            cur = py.std.os.environ.get('PYTHONPATH')
            if cur:
                dirs += (cur,)
            return os.pathsep.join(str(p) for p in dirs)
        monkeypatch.setenv('PYTHONPATH', join_pythonpath(*search_path))
        for p in search_path:
            monkeypatch.syspath_prepend(p)

        # mixed module and filenames:
        os.chdir('world')
        result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "ns_pkg/world")
        assert result.ret == 0
        result.stdout.fnmatch_lines([
            "*test_hello.py::test_hello*PASSED*",
            "*test_hello.py::test_other*PASSED*",
            "*test_world.py::test_world*PASSED*",
            "*test_world.py::test_other*PASSED*",
            "*4 passed*"
        ])

        # specify tests within a module
        testdir.chdir()
        result = testdir.runpytest("--pyargs", "-v", "ns_pkg.world.test_world::test_other")
        assert result.ret == 0
        result.stdout.fnmatch_lines([
            "*test_world.py::test_other*PASSED*",
            "*1 passed*"
        ])

    @pytest.mark.skipif(not hasattr(os, "symlink"), reason="requires symlinks")
    def test_cmdline_python_package_symlink(self, testdir, monkeypatch):
        """
        test --pyargs option with packages with path containing symlink can
        have conftest.py in their package (#2985)
        """
        # dummy check that we can actually create symlinks: on Windows `os.symlink` is available,
        # but normal users require special admin privileges to create symlinks.
        if sys.platform == 'win32':
            try:
                os.symlink(str(testdir.tmpdir.ensure('tmpfile')), str(testdir.tmpdir.join('tmpfile2')))
            except OSError as e:
                pytest.skip(six.text_type(e.args[0]))
        monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', raising=False)

        search_path = ["lib", os.path.join("local", "lib")]

        dirname = "lib"
        d = testdir.mkdir(dirname)
        foo = d.mkdir("foo")
        foo.ensure("__init__.py")
        lib = foo.mkdir('bar')
        lib.ensure("__init__.py")
        lib.join("test_bar.py"). \
            write("def test_bar(): pass\n"
                  "def test_other(a_fixture):pass")
        lib.join("conftest.py"). \
            write("import pytest\n"
                  "@pytest.fixture\n"
                  "def a_fixture():pass")

        d_local = testdir.mkdir("local")
        symlink_location = os.path.join(str(d_local), "lib")
        if six.PY2:
            os.symlink(str(d), symlink_location)
        else:
            os.symlink(str(d), symlink_location, target_is_directory=True)

        # The structure of the test directory is now:
        # .
        # ├── local
        # │   └── lib -> ../lib
        # └── lib
        #     └── foo
        #         ├── __init__.py
        #         └── bar
        #             ├── __init__.py
        #             ├── conftest.py
        #             └── test_bar.py

        def join_pythonpath(*dirs):
            cur = os.getenv('PYTHONPATH')
            if cur:
                dirs += (cur,)
            return os.pathsep.join(str(p) for p in dirs)

        monkeypatch.setenv('PYTHONPATH', join_pythonpath(*search_path))
        for p in search_path:
            monkeypatch.syspath_prepend(p)

        # module picked up in symlink-ed directory:
        result = testdir.runpytest("--pyargs", "-v", "foo.bar")
        testdir.chdir()
        assert result.ret == 0
        result.stdout.fnmatch_lines([
            "*lib/foo/bar/test_bar.py::test_bar*PASSED*",
            "*lib/foo/bar/test_bar.py::test_other*PASSED*",
            "*2 passed*"
        ])

    def test_cmdline_python_package_not_exists(self, testdir):
        result = testdir.runpytest("--pyargs", "tpkgwhatv")
        assert result.ret
        result.stderr.fnmatch_lines([
            "ERROR*file*or*package*not*found*",
        ])

    @pytest.mark.xfail(reason="decide: feature or bug")
    def test_noclass_discovery_if_not_testcase(self, testdir):
        testpath = testdir.makepyfile("""
            import unittest
            class TestHello(object):
                def test_hello(self):
                    assert self.attr

            class RealTest(unittest.TestCase, TestHello):
                attr = 42
        """)
        reprec = testdir.inline_run(testpath)
        reprec.assertoutcome(passed=1)

    def test_doctest_id(self, testdir):
        testdir.makefile('.txt', """
            >>> x=3
            >>> x
            4
        """)
        result = testdir.runpytest("-rf")
        lines = result.stdout.str().splitlines()
        for line in lines:
            if line.startswith("FAIL "):
                testid = line[5:].strip()
                break
        result = testdir.runpytest(testid, '-rf')
        result.stdout.fnmatch_lines([
            line,
            "*1 failed*",
        ])

    def test_core_backward_compatibility(self):
        """Test backward compatibility for get_plugin_manager function. See #787."""
        import _pytest.config
        assert type(_pytest.config.get_plugin_manager()) is _pytest.config.PytestPluginManager

    def test_has_plugin(self, request):
        """Test hasplugin function of the plugin manager (#932)."""
        assert request.config.pluginmanager.hasplugin('python')


class TestDurations(object):
    source = """
        import time
        frag = 0.002
        def test_something():
            pass
        def test_2():
            time.sleep(frag*5)
        def test_1():
            time.sleep(frag)
        def test_3():
            time.sleep(frag*10)
    """

    def test_calls(self, testdir):
        testdir.makepyfile(self.source)
        result = testdir.runpytest("--durations=10")
        assert result.ret == 0
        result.stdout.fnmatch_lines_random([
            "*durations*",
            "*call*test_3*",
            "*call*test_2*",
            "*call*test_1*",
        ])

    def test_calls_show_2(self, testdir):
        testdir.makepyfile(self.source)
        result = testdir.runpytest("--durations=2")
        assert result.ret == 0
        lines = result.stdout.get_lines_after("*slowest*durations*")
        assert "4 passed" in lines[2]

    def test_calls_showall(self, testdir):
        testdir.makepyfile(self.source)
        result = testdir.runpytest("--durations=0")
        assert result.ret == 0
        for x in "123":
            for y in 'call', :  # 'setup', 'call', 'teardown':
                for line in result.stdout.lines:
                    if ("test_%s" % x) in line and y in line:
                        break
                else:
                    raise AssertionError("not found %s %s" % (x, y))

    def test_with_deselected(self, testdir):
        testdir.makepyfile(self.source)
        result = testdir.runpytest("--durations=2", "-k test_1")
        assert result.ret == 0
        result.stdout.fnmatch_lines([
            "*durations*",
            "*call*test_1*",
        ])

    def test_with_failing_collection(self, testdir):
        testdir.makepyfile(self.source)
        testdir.makepyfile(test_collecterror="""xyz""")
        result = testdir.runpytest("--durations=2", "-k test_1")
        assert result.ret == 2
        result.stdout.fnmatch_lines([
            "*Interrupted: 1 errors during collection*",
        ])
        # Collection errors abort test execution, therefore no duration is
        # output
        assert "duration" not in result.stdout.str()

    def test_with_not(self, testdir):
        testdir.makepyfile(self.source)
        result = testdir.runpytest("-k not 1")
        assert result.ret == 0


class TestDurationWithFixture(object):
    source = """
        import time
        frag = 0.001
        def setup_function(func):
            time.sleep(frag * 3)
        def test_1():
            time.sleep(frag*2)
        def test_2():
            time.sleep(frag)
    """

    def test_setup_function(self, testdir):
        testdir.makepyfile(self.source)
        result = testdir.runpytest("--durations=10")
        assert result.ret == 0

        result.stdout.fnmatch_lines_random("""
            *durations*
            * setup *test_1*
            * call *test_1*
        """)


def test_zipimport_hook(testdir, tmpdir):
    """Test package loader is being used correctly (see #1837)."""
    zipapp = pytest.importorskip('zipapp')
    testdir.tmpdir.join('app').ensure(dir=1)
    testdir.makepyfile(**{
        'app/foo.py': """
            import pytest
            def main():
                pytest.main(['--pyarg', 'foo'])
        """,
    })
    target = tmpdir.join('foo.zip')
    zipapp.create_archive(str(testdir.tmpdir.join('app')), str(target), main='foo:main')
    result = testdir.runpython(target)
    assert result.ret == 0
    result.stderr.fnmatch_lines(['*not found*foo*'])
    assert 'INTERNALERROR>' not in result.stdout.str()


def test_import_plugin_unicode_name(testdir):
    testdir.makepyfile(
        myplugin='',
    )
    testdir.makepyfile("""
        def test(): pass
    """)
    testdir.makeconftest("""
        pytest_plugins = [u'myplugin']
    """)
    r = testdir.runpytest()
    assert r.ret == 0


def test_deferred_hook_checking(testdir):
    """
    Check hooks as late as possible (#1821).
    """
    testdir.syspathinsert()
    testdir.makepyfile(**{
        'plugin.py': """
        class Hooks:
            def pytest_my_hook(self, config):
                pass

        def pytest_configure(config):
            config.pluginmanager.add_hookspecs(Hooks)
        """,
        'conftest.py': """
            pytest_plugins = ['plugin']
            def pytest_my_hook(config):
                return 40
        """,
        'test_foo.py': """
            def test(request):
                assert request.config.hook.pytest_my_hook(config=request.config) == [40]
        """
    })
    result = testdir.runpytest()
    result.stdout.fnmatch_lines(['* 1 passed *'])


def test_fixture_values_leak(testdir):
    """Ensure that fixture objects are properly destroyed by the garbage collector at the end of their expected
    life-times (#2981).
    """
    testdir.makepyfile("""
        import attr
        import gc
        import pytest
        import weakref

        @attr.s
        class SomeObj(object):
            name = attr.ib()

        fix_of_test1_ref = None
        session_ref = None

        @pytest.fixture(scope='session')
        def session_fix():
            global session_ref
            obj = SomeObj(name='session-fixture')
            session_ref = weakref.ref(obj)
            return obj

        @pytest.fixture
        def fix(session_fix):
            global fix_of_test1_ref
            obj = SomeObj(name='local-fixture')
            fix_of_test1_ref = weakref.ref(obj)
            return obj

        def test1(fix):
            assert fix_of_test1_ref() is fix

        def test2():
            gc.collect()
            # fixture "fix" created during test1 must have been destroyed by now
            assert fix_of_test1_ref() is None
    """)
    result = testdir.runpytest()
    result.stdout.fnmatch_lines(['* 2 passed *'])
