from __future__ import absolute_import, division, print_function

import glob
import os
import py_compile
import stat
import sys
import zipfile
import py
import pytest

import _pytest._code
from _pytest.assertion import util
from _pytest.assertion.rewrite import rewrite_asserts, PYTEST_TAG, AssertionRewritingHook
from _pytest.main import EXIT_NOTESTSCOLLECTED

ast = pytest.importorskip("ast")
if sys.platform.startswith("java"):
    # XXX should be xfail
    pytest.skip("assert rewrite does currently not work on jython")


def setup_module(mod):
    mod._old_reprcompare = util._reprcompare
    _pytest._code._reprcompare = None


def teardown_module(mod):
    util._reprcompare = mod._old_reprcompare
    del mod._old_reprcompare


def rewrite(src):
    tree = ast.parse(src)
    rewrite_asserts(tree)
    return tree


def getmsg(f, extra_ns=None, must_pass=False):
    """Rewrite the assertions in f, run it, and get the failure message."""
    src = '\n'.join(_pytest._code.Code(f).source().lines)
    mod = rewrite(src)
    code = compile(mod, "<test>", "exec")
    ns = {}
    if extra_ns is not None:
        ns.update(extra_ns)
    py.builtin.exec_(code, ns)
    func = ns[f.__name__]
    try:
        func()
    except AssertionError:
        if must_pass:
            pytest.fail("shouldn't have raised")
        s = str(sys.exc_info()[1])
        if not s.startswith("assert"):
            return "AssertionError: " + s
        return s
    else:
        if not must_pass:
            pytest.fail("function didn't raise at all")


class TestAssertionRewrite(object):

    def test_place_initial_imports(self):
        s = """'Doc string'\nother = stuff"""
        m = rewrite(s)
        # Module docstrings in 3.7 are part of Module node, it's not in the body
        # so we remove it so the following body items have the same indexes on
        # all Python versions
        if sys.version_info < (3, 7):
            assert isinstance(m.body[0], ast.Expr)
            assert isinstance(m.body[0].value, ast.Str)
            del m.body[0]
        for imp in m.body[0:2]:
            assert isinstance(imp, ast.Import)
            assert imp.lineno == 2
            assert imp.col_offset == 0
        assert isinstance(m.body[2], ast.Assign)
        s = """from __future__ import with_statement\nother_stuff"""
        m = rewrite(s)
        assert isinstance(m.body[0], ast.ImportFrom)
        for imp in m.body[1:3]:
            assert isinstance(imp, ast.Import)
            assert imp.lineno == 2
            assert imp.col_offset == 0
        assert isinstance(m.body[3], ast.Expr)
        s = """'doc string'\nfrom __future__ import with_statement"""
        m = rewrite(s)
        if sys.version_info < (3, 7):
            assert isinstance(m.body[0], ast.Expr)
            assert isinstance(m.body[0].value, ast.Str)
            del m.body[0]
        assert isinstance(m.body[0], ast.ImportFrom)
        for imp in m.body[1:3]:
            assert isinstance(imp, ast.Import)
            assert imp.lineno == 2
            assert imp.col_offset == 0
        s = """'doc string'\nfrom __future__ import with_statement\nother"""
        m = rewrite(s)
        if sys.version_info < (3, 7):
            assert isinstance(m.body[0], ast.Expr)
            assert isinstance(m.body[0].value, ast.Str)
            del m.body[0]
        assert isinstance(m.body[0], ast.ImportFrom)
        for imp in m.body[1:3]:
            assert isinstance(imp, ast.Import)
            assert imp.lineno == 3
            assert imp.col_offset == 0
        assert isinstance(m.body[3], ast.Expr)
        s = """from . import relative\nother_stuff"""
        m = rewrite(s)
        for imp in m.body[0:2]:
            assert isinstance(imp, ast.Import)
            assert imp.lineno == 1
            assert imp.col_offset == 0
        assert isinstance(m.body[3], ast.Expr)

    def test_dont_rewrite(self):
        s = """'PYTEST_DONT_REWRITE'\nassert 14"""
        m = rewrite(s)
        if sys.version_info < (3, 7):
            assert len(m.body) == 2
            assert isinstance(m.body[0], ast.Expr)
            assert isinstance(m.body[0].value, ast.Str)
            del m.body[0]
        else:
            assert len(m.body) == 1
        assert m.body[0].msg is None

    def test_dont_rewrite_plugin(self, testdir):
        contents = {
            "conftest.py": "pytest_plugins = 'plugin'; import plugin",
            "plugin.py": "'PYTEST_DONT_REWRITE'",
            "test_foo.py": "def test_foo(): pass",
        }
        testdir.makepyfile(**contents)
        result = testdir.runpytest_subprocess()
        assert "warnings" not in "".join(result.outlines)

    def test_name(self):
        def f():
            assert False
        assert getmsg(f) == "assert False"

        def f():
            f = False
            assert f

        assert getmsg(f) == "assert False"

        def f():
            assert a_global  # noqa

        assert getmsg(f, {"a_global": False}) == "assert False"

        def f():
            assert sys == 42

        assert getmsg(f, {"sys": sys}) == "assert sys == 42"

        def f():
            assert cls == 42  # noqa

        class X(object):
            pass

        assert getmsg(f, {"cls": X}) == "assert cls == 42"

    def test_assert_already_has_message(self):
        def f():
            assert False, "something bad!"
        assert getmsg(f) == "AssertionError: something bad!\nassert False"

    def test_assertion_message(self, testdir):
        testdir.makepyfile("""
            def test_foo():
                assert 1 == 2, "The failure message"
        """)
        result = testdir.runpytest()
        assert result.ret == 1
        result.stdout.fnmatch_lines([
            "*AssertionError*The failure message*",
            "*assert 1 == 2*",
        ])

    def test_assertion_message_multiline(self, testdir):
        testdir.makepyfile("""
            def test_foo():
                assert 1 == 2, "A multiline\\nfailure message"
        """)
        result = testdir.runpytest()
        assert result.ret == 1
        result.stdout.fnmatch_lines([
            "*AssertionError*A multiline*",
            "*failure message*",
            "*assert 1 == 2*",
        ])

    def test_assertion_message_tuple(self, testdir):
        testdir.makepyfile("""
            def test_foo():
                assert 1 == 2, (1, 2)
        """)
        result = testdir.runpytest()
        assert result.ret == 1
        result.stdout.fnmatch_lines([
            "*AssertionError*%s*" % repr((1, 2)),
            "*assert 1 == 2*",
        ])

    def test_assertion_message_expr(self, testdir):
        testdir.makepyfile("""
            def test_foo():
                assert 1 == 2, 1 + 2
        """)
        result = testdir.runpytest()
        assert result.ret == 1
        result.stdout.fnmatch_lines([
            "*AssertionError*3*",
            "*assert 1 == 2*",
        ])

    def test_assertion_message_escape(self, testdir):
        testdir.makepyfile("""
            def test_foo():
                assert 1 == 2, 'To be escaped: %'
        """)
        result = testdir.runpytest()
        assert result.ret == 1
        result.stdout.fnmatch_lines([
            "*AssertionError: To be escaped: %",
            "*assert 1 == 2",
        ])

    def test_boolop(self):
        def f():
            f = g = False
            assert f and g

        assert getmsg(f) == "assert (False)"

        def f():
            f = True
            g = False
            assert f and g

        assert getmsg(f) == "assert (True and False)"

        def f():
            f = False
            g = True
            assert f and g

        assert getmsg(f) == "assert (False)"

        def f():
            f = g = False
            assert f or g

        assert getmsg(f) == "assert (False or False)"

        def f():
            f = g = False
            assert not f and not g

        getmsg(f, must_pass=True)

        def x():
            return False

        def f():
            assert x() and x()

        assert getmsg(f, {"x": x}) == """assert (False)
 +  where False = x()"""

        def f():
            assert False or x()

        assert getmsg(f, {"x": x}) == """assert (False or False)
 +  where False = x()"""

        def f():
            assert 1 in {} and 2 in {}

        assert getmsg(f) == "assert (1 in {})"

        def f():
            x = 1
            y = 2
            assert x in {1: None} and y in {}

        assert getmsg(f) == "assert (1 in {1: None} and 2 in {})"

        def f():
            f = True
            g = False
            assert f or g

        getmsg(f, must_pass=True)

        def f():
            f = g = h = lambda: True
            assert f() and g() and h()

        getmsg(f, must_pass=True)

    def test_short_circuit_evaluation(self):
        def f():
            assert True or explode  # noqa

        getmsg(f, must_pass=True)

        def f():
            x = 1
            assert x == 1 or x == 2

        getmsg(f, must_pass=True)

    def test_unary_op(self):
        def f():
            x = True
            assert not x

        assert getmsg(f) == "assert not True"

        def f():
            x = 0
            assert ~x + 1

        assert getmsg(f) == "assert (~0 + 1)"

        def f():
            x = 3
            assert -x + x

        assert getmsg(f) == "assert (-3 + 3)"

        def f():
            x = 0
            assert +x + x

        assert getmsg(f) == "assert (+0 + 0)"

    def test_binary_op(self):
        def f():
            x = 1
            y = -1
            assert x + y

        assert getmsg(f) == "assert (1 + -1)"

        def f():
            assert not 5 % 4
        assert getmsg(f) == "assert not (5 % 4)"

    def test_boolop_percent(self):
        def f():
            assert 3 % 2 and False

        assert getmsg(f) == "assert ((3 % 2) and False)"

        def f():
            assert False or 4 % 2
        assert getmsg(f) == "assert (False or (4 % 2))"

    @pytest.mark.skipif("sys.version_info < (3,5)")
    def test_at_operator_issue1290(self, testdir):
        testdir.makepyfile("""
            class Matrix(object):
                def __init__(self, num):
                    self.num = num
                def __matmul__(self, other):
                    return self.num * other.num

            def test_multmat_operator():
                assert Matrix(2) @ Matrix(3) == 6""")
        testdir.runpytest().assert_outcomes(passed=1)

    def test_call(self):
        def g(a=42, *args, **kwargs):
            return False

        ns = {"g": g}

        def f():
            assert g()

        assert getmsg(f, ns) == """assert False
 +  where False = g()"""

        def f():
            assert g(1)

        assert getmsg(f, ns) == """assert False
 +  where False = g(1)"""

        def f():
            assert g(1, 2)

        assert getmsg(f, ns) == """assert False
 +  where False = g(1, 2)"""

        def f():
            assert g(1, g=42)

        assert getmsg(f, ns) == """assert False
 +  where False = g(1, g=42)"""

        def f():
            assert g(1, 3, g=23)

        assert getmsg(f, ns) == """assert False
 +  where False = g(1, 3, g=23)"""

        def f():
            seq = [1, 2, 3]
            assert g(*seq)

        assert getmsg(f, ns) == """assert False
 +  where False = g(*[1, 2, 3])"""

        def f():
            x = "a"
            assert g(**{x: 2})

        assert getmsg(f, ns) == """assert False
 +  where False = g(**{'a': 2})"""

    def test_attribute(self):
        class X(object):
            g = 3

        ns = {"x": X}

        def f():
            assert not x.g  # noqa

        assert getmsg(f, ns) == """assert not 3
 +  where 3 = x.g"""

        def f():
            x.a = False  # noqa
            assert x.a   # noqa

        assert getmsg(f, ns) == """assert False
 +  where False = x.a"""

    def test_comparisons(self):

        def f():
            a, b = range(2)
            assert b < a

        assert getmsg(f) == """assert 1 < 0"""

        def f():
            a, b, c = range(3)
            assert a > b > c

        assert getmsg(f) == """assert 0 > 1"""

        def f():
            a, b, c = range(3)
            assert a < b > c

        assert getmsg(f) == """assert 1 > 2"""

        def f():
            a, b, c = range(3)
            assert a < b <= c

        getmsg(f, must_pass=True)

        def f():
            a, b, c = range(3)
            assert a < b
            assert b < c

        getmsg(f, must_pass=True)

    def test_len(self):

        def f():
            values = list(range(10))
            assert len(values) == 11

        assert getmsg(f).startswith("""assert 10 == 11
 +  where 10 = len([""")

    def test_custom_reprcompare(self, monkeypatch):
        def my_reprcompare(op, left, right):
            return "42"

        monkeypatch.setattr(util, "_reprcompare", my_reprcompare)

        def f():
            assert 42 < 3

        assert getmsg(f) == "assert 42"

        def my_reprcompare(op, left, right):
            return "%s %s %s" % (left, op, right)

        monkeypatch.setattr(util, "_reprcompare", my_reprcompare)

        def f():
            assert 1 < 3 < 5 <= 4 < 7

        assert getmsg(f) == "assert 5 <= 4"

    def test_assert_raising_nonzero_in_comparison(self):
        def f():
            class A(object):

                def __nonzero__(self):
                    raise ValueError(42)

                def __lt__(self, other):
                    return A()

                def __repr__(self):
                    return "<MY42 object>"

            def myany(x):
                return False

            assert myany(A() < 0)

        assert "<MY42 object> < 0" in getmsg(f)

    def test_formatchar(self):
        def f():
            assert "%test" == "test"

        assert getmsg(f).startswith("assert '%test' == 'test'")

    def test_custom_repr(self):
        def f():
            class Foo(object):
                a = 1

                def __repr__(self):
                    return "\n{ \n~ \n}"

            f = Foo()
            assert 0 == f.a

        assert r"where 1 = \n{ \n~ \n}.a" in util._format_lines([getmsg(f)])[0]


class TestRewriteOnImport(object):

    def test_pycache_is_a_file(self, testdir):
        testdir.tmpdir.join("__pycache__").write("Hello")
        testdir.makepyfile("""
            def test_rewritten():
                assert "@py_builtins" in globals()""")
        assert testdir.runpytest().ret == 0

    def test_pycache_is_readonly(self, testdir):
        cache = testdir.tmpdir.mkdir("__pycache__")
        old_mode = cache.stat().mode
        cache.chmod(old_mode ^ stat.S_IWRITE)
        testdir.makepyfile("""
            def test_rewritten():
                assert "@py_builtins" in globals()""")
        try:
            assert testdir.runpytest().ret == 0
        finally:
            cache.chmod(old_mode)

    def test_zipfile(self, testdir):
        z = testdir.tmpdir.join("myzip.zip")
        z_fn = str(z)
        f = zipfile.ZipFile(z_fn, "w")
        try:
            f.writestr("test_gum/__init__.py", "")
            f.writestr("test_gum/test_lizard.py", "")
        finally:
            f.close()
        z.chmod(256)
        testdir.makepyfile("""
            import sys
            sys.path.append(%r)
            import test_gum.test_lizard""" % (z_fn,))
        assert testdir.runpytest().ret == EXIT_NOTESTSCOLLECTED

    def test_readonly(self, testdir):
        sub = testdir.mkdir("testing")
        sub.join("test_readonly.py").write(
            py.builtin._totext("""
def test_rewritten():
    assert "@py_builtins" in globals()
            """).encode("utf-8"), "wb")
        old_mode = sub.stat().mode
        sub.chmod(320)
        try:
            assert testdir.runpytest().ret == 0
        finally:
            sub.chmod(old_mode)

    def test_dont_write_bytecode(self, testdir, monkeypatch):
        testdir.makepyfile("""
            import os
            def test_no_bytecode():
                assert "__pycache__" in __cached__
                assert not os.path.exists(__cached__)
                assert not os.path.exists(os.path.dirname(__cached__))""")
        monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
        assert testdir.runpytest_subprocess().ret == 0

    def test_orphaned_pyc_file(self, testdir):
        if sys.version_info < (3, 0) and hasattr(sys, 'pypy_version_info'):
            pytest.skip("pypy2 doesn't run orphaned pyc files")

        testdir.makepyfile("""
            import orphan
            def test_it():
                assert orphan.value == 17
            """)
        testdir.makepyfile(orphan="""
            value = 17
            """)
        py_compile.compile("orphan.py")
        os.remove("orphan.py")

        # Python 3 puts the .pyc files in a __pycache__ directory, and will
        # not import from there without source.  It will import a .pyc from
        # the source location though.
        if not os.path.exists("orphan.pyc"):
            pycs = glob.glob("__pycache__/orphan.*.pyc")
            assert len(pycs) == 1
            os.rename(pycs[0], "orphan.pyc")

        assert testdir.runpytest().ret == 0

    @pytest.mark.skipif('"__pypy__" in sys.modules')
    def test_pyc_vs_pyo(self, testdir, monkeypatch):
        testdir.makepyfile("""
            import pytest
            def test_optimized():
                "hello"
                assert test_optimized.__doc__ is None"""
                           )
        p = py.path.local.make_numbered_dir(prefix="runpytest-", keep=None,
                                            rootdir=testdir.tmpdir)
        tmp = "--basetemp=%s" % p
        monkeypatch.setenv("PYTHONOPTIMIZE", "2")
        monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
        assert testdir.runpytest_subprocess(tmp).ret == 0
        tagged = "test_pyc_vs_pyo." + PYTEST_TAG
        assert tagged + ".pyo" in os.listdir("__pycache__")
        monkeypatch.undo()
        monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
        assert testdir.runpytest_subprocess(tmp).ret == 1
        assert tagged + ".pyc" in os.listdir("__pycache__")

    def test_package(self, testdir):
        pkg = testdir.tmpdir.join("pkg")
        pkg.mkdir()
        pkg.join("__init__.py").ensure()
        pkg.join("test_blah.py").write("""
def test_rewritten():
    assert "@py_builtins" in globals()""")
        assert testdir.runpytest().ret == 0

    def test_translate_newlines(self, testdir):
        content = "def test_rewritten():\r\n assert '@py_builtins' in globals()"
        b = content.encode("utf-8")
        testdir.tmpdir.join("test_newlines.py").write(b, "wb")
        assert testdir.runpytest().ret == 0

    @pytest.mark.skipif(sys.version_info < (3, 4),
                        reason='packages without __init__.py not supported on python 2')
    def test_package_without__init__py(self, testdir):
        pkg = testdir.mkdir('a_package_without_init_py')
        pkg.join('module.py').ensure()
        testdir.makepyfile("import a_package_without_init_py.module")
        assert testdir.runpytest().ret == EXIT_NOTESTSCOLLECTED

    def test_rewrite_warning(self, pytestconfig, monkeypatch):
        hook = AssertionRewritingHook(pytestconfig)
        warnings = []

        def mywarn(code, msg):
            warnings.append((code, msg))

        monkeypatch.setattr(hook.config, 'warn', mywarn)
        hook.mark_rewrite('_pytest')
        assert '_pytest' in warnings[0][1]

    def test_rewrite_module_imported_from_conftest(self, testdir):
        testdir.makeconftest('''
            import test_rewrite_module_imported
        ''')
        testdir.makepyfile(test_rewrite_module_imported='''
            def test_rewritten():
                assert "@py_builtins" in globals()
        ''')
        assert testdir.runpytest_subprocess().ret == 0

    def test_remember_rewritten_modules(self, pytestconfig, testdir, monkeypatch):
        """
        AssertionRewriteHook should remember rewritten modules so it
        doesn't give false positives (#2005).
        """
        monkeypatch.syspath_prepend(testdir.tmpdir)
        testdir.makepyfile(test_remember_rewritten_modules='')
        warnings = []
        hook = AssertionRewritingHook(pytestconfig)
        monkeypatch.setattr(hook.config, 'warn', lambda code, msg: warnings.append(msg))
        hook.find_module('test_remember_rewritten_modules')
        hook.load_module('test_remember_rewritten_modules')
        hook.mark_rewrite('test_remember_rewritten_modules')
        hook.mark_rewrite('test_remember_rewritten_modules')
        assert warnings == []

    def test_rewrite_warning_using_pytest_plugins(self, testdir):
        testdir.makepyfile(**{
            'conftest.py': "pytest_plugins = ['core', 'gui', 'sci']",
            'core.py': "",
            'gui.py': "pytest_plugins = ['core', 'sci']",
            'sci.py': "pytest_plugins = ['core']",
            'test_rewrite_warning_pytest_plugins.py': "def test(): pass",
        })
        testdir.chdir()
        result = testdir.runpytest_subprocess()
        result.stdout.fnmatch_lines(['*= 1 passed in *=*'])
        assert 'pytest-warning summary' not in result.stdout.str()

    def test_rewrite_warning_using_pytest_plugins_env_var(self, testdir, monkeypatch):
        monkeypatch.setenv('PYTEST_PLUGINS', 'plugin')
        testdir.makepyfile(**{
            'plugin.py': "",
            'test_rewrite_warning_using_pytest_plugins_env_var.py': """
                import plugin
                pytest_plugins = ['plugin']
                def test():
                    pass
            """,
        })
        testdir.chdir()
        result = testdir.runpytest_subprocess()
        result.stdout.fnmatch_lines(['*= 1 passed in *=*'])
        assert 'pytest-warning summary' not in result.stdout.str()

    @pytest.mark.skipif(sys.version_info[0] > 2, reason='python 2 only')
    def test_rewrite_future_imports(self, testdir):
        """Test that rewritten modules don't inherit the __future__ flags
        from the assertrewrite module.

        assertion.rewrite imports __future__.division (and others), so
        ensure rewritten modules don't inherit those flags.

        The test below will fail if __future__.division is enabled
        """
        testdir.makepyfile('''
            def test():
                x = 1 / 2
                assert type(x) is int
        ''')
        result = testdir.runpytest()
        assert result.ret == 0


class TestAssertionRewriteHookDetails(object):
    def test_loader_is_package_false_for_module(self, testdir):
        testdir.makepyfile(test_fun="""
            def test_loader():
                assert not __loader__.is_package(__name__)
            """)
        result = testdir.runpytest()
        result.stdout.fnmatch_lines([
            "* 1 passed*",
        ])

    def test_loader_is_package_true_for_package(self, testdir):
        testdir.makepyfile(test_fun="""
            def test_loader():
                assert not __loader__.is_package(__name__)

            def test_fun():
                assert __loader__.is_package('fun')

            def test_missing():
                assert not __loader__.is_package('pytest_not_there')
            """)
        testdir.mkpydir('fun')
        result = testdir.runpytest()
        result.stdout.fnmatch_lines([
            '* 3 passed*',
        ])

    @pytest.mark.skipif("sys.version_info[0] >= 3")
    @pytest.mark.xfail("hasattr(sys, 'pypy_translation_info')")
    def test_assume_ascii(self, testdir):
        content = "u'\xe2\x99\xa5\x01\xfe'"
        testdir.tmpdir.join("test_encoding.py").write(content, "wb")
        res = testdir.runpytest()
        assert res.ret != 0
        assert "SyntaxError: Non-ASCII character" in res.stdout.str()

    @pytest.mark.skipif("sys.version_info[0] >= 3")
    def test_detect_coding_cookie(self, testdir):
        testdir.makepyfile(test_cookie="""
            # -*- coding: utf-8 -*-
            u"St\xc3\xa4d"
            def test_rewritten():
                assert "@py_builtins" in globals()""")
        assert testdir.runpytest().ret == 0

    @pytest.mark.skipif("sys.version_info[0] >= 3")
    def test_detect_coding_cookie_second_line(self, testdir):
        testdir.makepyfile(test_cookie="""
            # -*- coding: utf-8 -*-
            u"St\xc3\xa4d"
            def test_rewritten():
                assert "@py_builtins" in globals()""")
        assert testdir.runpytest().ret == 0

    @pytest.mark.skipif("sys.version_info[0] >= 3")
    def test_detect_coding_cookie_crlf(self, testdir):
        testdir.makepyfile(test_cookie="""
            # -*- coding: utf-8 -*-
            u"St\xc3\xa4d"
            def test_rewritten():
                assert "@py_builtins" in globals()""")
        assert testdir.runpytest().ret == 0

    def test_sys_meta_path_munged(self, testdir):
        testdir.makepyfile("""
            def test_meta_path():
                import sys; sys.meta_path = []""")
        assert testdir.runpytest().ret == 0

    def test_write_pyc(self, testdir, tmpdir, monkeypatch):
        from _pytest.assertion.rewrite import _write_pyc
        from _pytest.assertion import AssertionState
        try:
            import __builtin__ as b
        except ImportError:
            import builtins as b
        config = testdir.parseconfig([])
        state = AssertionState(config, "rewrite")
        source_path = tmpdir.ensure("source.py")
        pycpath = tmpdir.join("pyc").strpath
        assert _write_pyc(state, [1], source_path.stat(), pycpath)

        def open(*args):
            e = IOError()
            e.errno = 10
            raise e

        monkeypatch.setattr(b, "open", open)
        assert not _write_pyc(state, [1], source_path.stat(), pycpath)

    def test_resources_provider_for_loader(self, testdir):
        """
        Attempts to load resources from a package should succeed normally,
        even when the AssertionRewriteHook is used to load the modules.

        See #366 for details.
        """
        pytest.importorskip("pkg_resources")

        testdir.mkpydir('testpkg')
        contents = {
            'testpkg/test_pkg': """
                import pkg_resources

                import pytest
                from _pytest.assertion.rewrite import AssertionRewritingHook

                def test_load_resource():
                    assert isinstance(__loader__, AssertionRewritingHook)
                    res = pkg_resources.resource_string(__name__, 'resource.txt')
                    res = res.decode('ascii')
                    assert res == 'Load me please.'
                """,
        }
        testdir.makepyfile(**contents)
        testdir.maketxtfile(**{'testpkg/resource': "Load me please."})

        result = testdir.runpytest_subprocess()
        result.assert_outcomes(passed=1)

    def test_read_pyc(self, tmpdir):
        """
        Ensure that the `_read_pyc` can properly deal with corrupted pyc files.
        In those circumstances it should just give up instead of generating
        an exception that is propagated to the caller.
        """
        import py_compile
        from _pytest.assertion.rewrite import _read_pyc

        source = tmpdir.join('source.py')
        pyc = source + 'c'

        source.write('def test(): pass')
        py_compile.compile(str(source), str(pyc))

        contents = pyc.read(mode='rb')
        strip_bytes = 20  # header is around 8 bytes, strip a little more
        assert len(contents) > strip_bytes
        pyc.write(contents[:strip_bytes], mode='wb')

        assert _read_pyc(source, str(pyc)) is None  # no error

    def test_reload_is_same(self, testdir):
        # A file that will be picked up during collecting.
        testdir.tmpdir.join("file.py").ensure()
        testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent("""
            [pytest]
            python_files = *.py
        """))

        testdir.makepyfile(test_fun="""
            import sys
            try:
                from imp import reload
            except ImportError:
                pass

            def test_loader():
                import file
                assert sys.modules["file"] is reload(file)
            """)
        result = testdir.runpytest('-s')
        result.stdout.fnmatch_lines([
            "* 1 passed*",
        ])

    def test_get_data_support(self, testdir):
        """Implement optional PEP302 api (#808).
        """
        path = testdir.mkpydir("foo")
        path.join("test_foo.py").write(_pytest._code.Source("""
            class Test(object):
                def test_foo(self):
                    import pkgutil
                    data = pkgutil.get_data('foo.test_foo', 'data.txt')
                    assert data == b'Hey'
        """))
        path.join('data.txt').write('Hey')
        result = testdir.runpytest()
        result.stdout.fnmatch_lines('*1 passed*')


def test_issue731(testdir):
    testdir.makepyfile("""
    class LongReprWithBraces(object):
        def __repr__(self):
           return 'LongReprWithBraces({' + ('a' * 80) + '}' + ('a' * 120) + ')'

        def some_method(self):
            return False

    def test_long_repr():
        obj = LongReprWithBraces()
        assert obj.some_method()
    """)
    result = testdir.runpytest()
    assert 'unbalanced braces' not in result.stdout.str()


class TestIssue925(object):
    def test_simple_case(self, testdir):
        testdir.makepyfile("""
        def test_ternary_display():
            assert (False == False) == False
        """)
        result = testdir.runpytest()
        result.stdout.fnmatch_lines('*E*assert (False == False) == False')

    def test_long_case(self, testdir):
        testdir.makepyfile("""
        def test_ternary_display():
             assert False == (False == True) == True
        """)
        result = testdir.runpytest()
        result.stdout.fnmatch_lines('*E*assert (False == True) == True')

    def test_many_brackets(self, testdir):
        testdir.makepyfile("""
            def test_ternary_display():
                 assert True == ((False == True) == True)
            """)
        result = testdir.runpytest()
        result.stdout.fnmatch_lines('*E*assert True == ((False == True) == True)')


class TestIssue2121():
    def test_simple(self, testdir):
        testdir.tmpdir.join("tests/file.py").ensure().write("""
def test_simple_failure():
    assert 1 + 1 == 3
""")
        testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent("""
            [pytest]
            python_files = tests/**.py
        """))

        result = testdir.runpytest()
        result.stdout.fnmatch_lines('*E*assert (1 + 1) == 3')
