from __future__ import absolute_import, division, print_function
import os

import _pytest._code
import py
import pytest
from _pytest.nodes import Node, Item, FSCollector
from _pytest.resultlog import generic_path, ResultLog, \
    pytest_configure, pytest_unconfigure


def test_generic_path(testdir):
    from _pytest.main import Session
    config = testdir.parseconfig()
    session = Session(config)
    p1 = Node('a', config=config, session=session)
    # assert p1.fspath is None
    p2 = Node('B', parent=p1)
    p3 = Node('()', parent=p2)
    item = Item('c', parent=p3)

    res = generic_path(item)
    assert res == 'a.B().c'

    p0 = FSCollector('proj/test', config=config, session=session)
    p1 = FSCollector('proj/test/a', parent=p0)
    p2 = Node('B', parent=p1)
    p3 = Node('()', parent=p2)
    p4 = Node('c', parent=p3)
    item = Item('[1]', parent=p4)

    res = generic_path(item)
    assert res == 'test/a:B().c[1]'


def test_write_log_entry():
    reslog = ResultLog(None, None)
    reslog.logfile = py.io.TextIO()
    reslog.write_log_entry('name', '.', '')
    entry = reslog.logfile.getvalue()
    assert entry[-1] == '\n'
    entry_lines = entry.splitlines()
    assert len(entry_lines) == 1
    assert entry_lines[0] == '. name'

    reslog.logfile = py.io.TextIO()
    reslog.write_log_entry('name', 's', 'Skipped')
    entry = reslog.logfile.getvalue()
    assert entry[-1] == '\n'
    entry_lines = entry.splitlines()
    assert len(entry_lines) == 2
    assert entry_lines[0] == 's name'
    assert entry_lines[1] == ' Skipped'

    reslog.logfile = py.io.TextIO()
    reslog.write_log_entry('name', 's', 'Skipped\n')
    entry = reslog.logfile.getvalue()
    assert entry[-1] == '\n'
    entry_lines = entry.splitlines()
    assert len(entry_lines) == 2
    assert entry_lines[0] == 's name'
    assert entry_lines[1] == ' Skipped'

    reslog.logfile = py.io.TextIO()
    longrepr = ' tb1\n tb 2\nE tb3\nSome Error'
    reslog.write_log_entry('name', 'F', longrepr)
    entry = reslog.logfile.getvalue()
    assert entry[-1] == '\n'
    entry_lines = entry.splitlines()
    assert len(entry_lines) == 5
    assert entry_lines[0] == 'F name'
    assert entry_lines[1:] == [' ' + line for line in longrepr.splitlines()]


class TestWithFunctionIntegration(object):
    # XXX (hpk) i think that the resultlog plugin should
    # provide a Parser object so that one can remain
    # ignorant regarding formatting details.
    def getresultlog(self, testdir, arg):
        resultlog = testdir.tmpdir.join("resultlog")
        testdir.plugins.append("resultlog")
        args = ["--resultlog=%s" % resultlog] + [arg]
        testdir.runpytest(*args)
        return [x for x in resultlog.readlines(cr=0) if x]

    def test_collection_report(self, testdir):
        ok = testdir.makepyfile(test_collection_ok="")
        fail = testdir.makepyfile(test_collection_fail="XXX")
        lines = self.getresultlog(testdir, ok)
        assert not lines

        lines = self.getresultlog(testdir, fail)
        assert lines
        assert lines[0].startswith("F ")
        assert lines[0].endswith("test_collection_fail.py"), lines[0]
        for x in lines[1:]:
            assert x.startswith(" ")
        assert "XXX" in "".join(lines[1:])

    def test_log_test_outcomes(self, testdir):
        mod = testdir.makepyfile(test_mod="""
            import pytest
            def test_pass(): pass
            def test_skip(): pytest.skip("hello")
            def test_fail(): raise ValueError("FAIL")

            @pytest.mark.xfail
            def test_xfail(): raise ValueError("XFAIL")
            @pytest.mark.xfail
            def test_xpass(): pass

        """)
        lines = self.getresultlog(testdir, mod)
        assert len(lines) >= 3
        assert lines[0].startswith(". ")
        assert lines[0].endswith("test_pass")
        assert lines[1].startswith("s "), lines[1]
        assert lines[1].endswith("test_skip")
        assert lines[2].find("hello") != -1

        assert lines[3].startswith("F ")
        assert lines[3].endswith("test_fail")
        tb = "".join(lines[4:8])
        assert tb.find('raise ValueError("FAIL")') != -1

        assert lines[8].startswith('x ')
        tb = "".join(lines[8:14])
        assert tb.find('raise ValueError("XFAIL")') != -1

        assert lines[14].startswith('X ')
        assert len(lines) == 15

    @pytest.mark.parametrize("style", ("native", "long", "short"))
    def test_internal_exception(self, style):
        # they are produced for example by a teardown failing
        # at the end of the run or a failing hook invocation
        try:
            raise ValueError
        except ValueError:
            excinfo = _pytest._code.ExceptionInfo()
        reslog = ResultLog(None, py.io.TextIO())
        reslog.pytest_internalerror(excinfo.getrepr(style=style))
        entry = reslog.logfile.getvalue()
        entry_lines = entry.splitlines()

        assert entry_lines[0].startswith('! ')
        if style != "native":
            assert os.path.basename(__file__)[:-9] in entry_lines[0]  # .pyc/class
        assert entry_lines[-1][0] == ' '
        assert 'ValueError' in entry


def test_generic(testdir, LineMatcher):
    testdir.plugins.append("resultlog")
    testdir.makepyfile("""
        import pytest
        def test_pass():
            pass
        def test_fail():
            assert 0
        def test_skip():
            pytest.skip("")
        @pytest.mark.xfail
        def test_xfail():
            assert 0
        @pytest.mark.xfail(run=False)
        def test_xfail_norun():
            assert 0
    """)
    testdir.runpytest("--resultlog=result.log")
    lines = testdir.tmpdir.join("result.log").readlines(cr=0)
    LineMatcher(lines).fnmatch_lines([
        ". *:test_pass",
        "F *:test_fail",
        "s *:test_skip",
        "x *:test_xfail",
        "x *:test_xfail_norun",
    ])


def test_makedir_for_resultlog(testdir, LineMatcher):
    """--resultlog should automatically create directories for the log file"""
    testdir.plugins.append("resultlog")
    testdir.makepyfile("""
        import pytest
        def test_pass():
            pass
    """)
    testdir.runpytest("--resultlog=path/to/result.log")
    lines = testdir.tmpdir.join("path/to/result.log").readlines(cr=0)
    LineMatcher(lines).fnmatch_lines([
        ". *:test_pass",
    ])


def test_no_resultlog_on_slaves(testdir):
    config = testdir.parseconfig("-p", "resultlog", "--resultlog=resultlog")

    assert not hasattr(config, '_resultlog')
    pytest_configure(config)
    assert hasattr(config, '_resultlog')
    pytest_unconfigure(config)
    assert not hasattr(config, '_resultlog')

    config.slaveinput = {}
    pytest_configure(config)
    assert not hasattr(config, '_resultlog')
    pytest_unconfigure(config)
    assert not hasattr(config, '_resultlog')


def test_failure_issue380(testdir):
    testdir.makeconftest("""
        import pytest
        class MyCollector(pytest.File):
            def collect(self):
                raise ValueError()
            def repr_failure(self, excinfo):
                return "somestring"
        def pytest_collect_file(path, parent):
            return MyCollector(parent=parent, fspath=path)
    """)
    testdir.makepyfile("""
        def test_func():
            pass
    """)
    result = testdir.runpytest("--resultlog=log")
    assert result.ret == 2