from mercurial.node import nullrev as X
from mercurial import (
    hg,
    pycompat,
)

from tortoisehg.util import hglib

import helpers

def setup_module():
    global _tmpdir
    _tmpdir = helpers.mktmpdir(__name__)


def createrepo(textgraph):
    path = pycompat.mkdtemp(dir=_tmpdir)
    helpers.buildgraph(path, textgraph)
    return hg.repository(hglib.loadui(), path)

def test_edge_connection_1():
    repo = createrepo(r"""
      o
     /
    o
    """)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_edge_connection_2():
    repo = createrepo(r"""
    o
     \
      o
    """)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_edge_connection_3():
    repo = createrepo(r"""
    o
     \
     /
    o
    """)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_edge_connection_4():
    repo = createrepo(r"""
    o
     \
      |
      o
    """)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_edge_connection_5():
    repo = createrepo(r"""
    o
     \
      \
       o
    """)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_edge_connection_6():
    repo = createrepo(r"""
      o
      |
     /
    o
    """)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_edge_connection_7():
    repo = createrepo(r"""
       o
      /
     /
    o
    """)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_edge_connection_8():
    repo = createrepo(r"""
      o
     /
    |
    o
    """)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_edge_connection_9():
    repo = createrepo(r"""
      o
     /
     \
      o
    """)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_straight():
    repo = createrepo(r"""
    o
    |
    o
    o # lines which have only "|" can be omitted
    o
    """)
    assert repo.changelog.parentrevs(3) == (2, X)
    assert repo.changelog.parentrevs(2) == (1, X)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_branched():
    repo = createrepo(r"""
    o
    |  o
    o /
    |/
    o
    """)
    assert repo.changelog.parentrevs(3) == (1, X)
    assert repo.changelog.parentrevs(2) == (0, X)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_merged():
    repo = createrepo(r"""
    4
    |\
    | 3
    |/|
    | 2 [branch=foo]
    1 |
    |/
    0
    """)
    assert repo.changelog.parentrevs(4) == (1, 3)
    assert repo.changelog.parentrevs(3) == (2, 1)
    assert repo.changelog.parentrevs(2) == (0, X)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_merged_2():
    repo = createrepo(r"""
    3
    2\
    | 1
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (2, 1)

def test_horizontaledge_1():
    repo = createrepo(r"""
    6
    | 5
    +---4
    3 | |
    |\|/
    | 2 [branch=foo]
    1 |
    |/
    0
    """)
    assert repo.changelog.parentrevs(6) == (3, X)
    assert repo.changelog.parentrevs(5) == (2, X)
    assert repo.changelog.parentrevs(4) == (2, 3)
    assert repo.changelog.parentrevs(3) == (1, 2)
    assert repo.changelog.parentrevs(2) == (0, X)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_horizontaledge_2():
    repo = createrepo(r"""
    7
    |   6
    | 5---+     # right to left
    | | | |
    | |/  4
    | 3  /
    +---2       # left to right
    |/
    1
    0
    """)
    assert repo.changelog.parentrevs(7) == (1, X)
    assert repo.changelog.parentrevs(6) == (3, X)
    assert repo.changelog.parentrevs(5) == (3, 4)
    assert repo.changelog.parentrevs(4) == (2, X)
    assert repo.changelog.parentrevs(3) == (1, X)
    assert repo.changelog.parentrevs(2) == (1, X)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_horizontaledge_double():
    repo = createrepo(r"""
    4
    | +-3-+ [branch=foo]
    | |   2 [branch=foo]
    | |  /
    | | /
    |/ /
    1 /
    |/
    0
    """)
    assert repo.changelog.parentrevs(4) == (1, X)
    assert repo.changelog.parentrevs(3) == (2, 1)
    assert repo.changelog.parentrevs(2) == (0, X)
    assert repo.changelog.parentrevs(1) == (0, X)

def test_p1_selection_bybranch_1():
    """treat parent which has same branch as p1"""
    repo = createrepo(r"""
    3 [branch=foo]
    |\
    2 |
    | 1 [branch=foo]
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (1, 2)

def test_p1_selection_bybranch_2():
    """treat parent which has same branch as p1"""
    repo = createrepo(r"""
      3 [branch=default]
     /|
    2 |
    | 1 [branch=foo]
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (2, 1)

def test_p1_selection_bybranch_3():
    """treat parent which has same branch as p1"""
    repo = createrepo(r"""
    +-3--+ [branch=default]
    |   /
    2  /
    | 1 [branch=foo]
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (2, 1)

def test_p1_selection_bybranch_4():
    """treat parent which has same branch as p1"""
    repo = createrepo(r"""
    +-3--+ [branch=foo]
    |   /
    2  /
    | 1 [branch=foo]
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (1, 2)

def test_p1_selection_bygraph_1():
    """treat parent under '|' as p1 when can't determine by branch"""
    repo = createrepo(r"""
    3
    |\
    2 |
    | 1
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (2, 1)

def test_p1_selection_bygraph_2():
    """treat parent under '|' as p1 when can't determine by branch"""
    repo = createrepo(r"""
      3
     /|
    2 |
    | 1
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (1, 2)

def test_p1_selection_bygraph_3():
    """treat parent under '|' as p1 when can't determine by branch"""
    repo = createrepo(r"""
    3 [branch=default]
    |\
    2 |
    | 1
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (2, 1)

def test_p1_selection_bygraph_4():
    """treat parent under '|' as p1 when can't determine by branch"""
    repo = createrepo(r"""
      3 [branch=default]
     /|
    2 |
    | 1
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (1, 2)

def test_p1_selection_bygraph_5():
    """treat parent under '|' as p1 when can't determine by branch"""
    repo = createrepo(r"""
    3
    |\
    2 |
    | 1 [branch=foo]
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (2, 1)
    assert repo[3].branch() == b'default'

def test_p1_selection_bygraph_6():
    """treat parent under '|' as p1 when can't determine by branch"""
    repo = createrepo(r"""
      3
     /|
    2 |
    | 1 [branch=foo]
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (1, 2)
    assert repo[3].branch() == b'foo'

def test_cross_edge():
    repo = createrepo(r"""
    9
    |           8
    |     7     |
    |   6 |     |
    | 5 |  \   /
    +-------------4
    |/   \  | |
    +-----3 | |
    +-------2 |
    +---------1
    0
    """)
    assert repo.changelog.parentrevs(8) == (1, X)
    assert repo.changelog.parentrevs(7) == (2, X)
    assert repo.changelog.parentrevs(6) == (3, X)
    assert repo.changelog.parentrevs(5) == (0, X)

def test_comment():
    repo = createrepo(r"""
    o
    |#o
    |
    o
    """)
    assert len(repo) == 2

def test_branch():
    repo = createrepo(r"""
      4
     /|
    3 |
    |\|
    | 2 [branch=foo]
    1 |
    |/
    0
    """)
    assert repo[0].branch() == b"default"
    assert repo[1].branch() == b"default"
    assert repo[2].branch() == b"foo"
    assert repo[3].branch() == b"default"
    assert repo[4].branch() == b"foo"

def test_user():
    repo = createrepo(r"""
    1 [user=bob]
    |
    0
    """)
    assert repo[0].user() == b"alice"
    assert repo[1].user() == b"bob"

def test_files():
    repo = createrepo(r"""
    1 [files="foo,bar/baz"]
    |
    0
    """)

    assert set(repo[1].files()) == {helpers.GraphBuilder.DUMMYFILE,
                                    b"foo", b"bar/baz"}

def test_file_move():
    repo = createrepo(r"""
    2 [files="foo=>baz/foo2"]
    1 [files="foo,bar"]
    0
    """)
    assert set(repo[2].files()) == {helpers.GraphBuilder.DUMMYFILE,
                                    b"foo", b"baz/foo2"}
    assert set(repo[2].manifest()) == {helpers.GraphBuilder.DUMMYFILE,
                                       b".hgignore", b"bar", b"baz/foo2"}

def test_file_remove():
    repo = createrepo(r"""
    2 [files="foo=>"]
    1 [files="foo,bar"]
    0
    """)
    assert set(repo[2].files()) == {helpers.GraphBuilder.DUMMYFILE, b"foo"}
    assert set(repo[2].manifest()) == {helpers.GraphBuilder.DUMMYFILE,
                                       b".hgignore", b"bar"}

def _contentgetter(repo, path):
    return lambda rev: repo[rev].filectx(path).data()

def test_merge():
    repo = createrepo(r"""
    3
    |\
    | 2 [branch=x files=foo]
    1 | [files=foo]
    |/
    0
    """)
    foo = _contentgetter(repo, b"foo")
    assert foo(1) != foo(2)
    assert foo(1) != foo(3)
    assert foo(2) != foo(3)
    assert foo(3).find(b"<<<") < 0

def test_merge_local():
    repo = createrepo(r"""
    3 [merge=local]
    |\
    | 2 [files='foo, bar']
    1 | [files=foo]
    |/
    0
    """)
    foo = _contentgetter(repo, b"foo")
    bar = _contentgetter(repo, b"bar")
    assert foo(1) != foo(2)
    assert foo(1) == foo(3)
    assert bar(2) == bar(3)

def test_merge_other():
    repo = createrepo(r"""
    3 [merge=other]
    |\
    | 2 [files=foo]
    1 | [files='foo, bar']
    |/
    0
    """)
    foo = _contentgetter(repo, b"foo")
    bar = _contentgetter(repo, b"bar")
    assert foo(1) != foo(2)
    assert foo(2) == foo(3)
    assert bar(1) == bar(3)


def _sourcerev(repo, rev):
    source = repo[rev].extra()[b'source']
    return repo[source].rev()

def test_graft():
    repo = createrepo(r"""

    3   [source=2]
    | 2
    1 |
    |/
    0
    """)
    assert repo.changelog.parentrevs(3) == (1, X)
    assert repo.changelog.parentrevs(2) == (0, X)
    assert repo.changelog.parentrevs(1) == (0, X)
    assert _sourcerev(repo, 3) == 2

def test_graft_branch():
    repo = createrepo(r"""
      4 [branch=default]
     /|
    3 | [source=2]
    | 2 [branch=foo]
    1 |
    |/
    0
    """)
    assert repo.changelog.parentrevs(4) == (3, 2)
    assert repo.changelog.parentrevs(3) == (1, X)
    assert repo.changelog.parentrevs(2) == (0, X)
    assert repo.changelog.parentrevs(1) == (0, X)
    assert _sourcerev(repo, 3) == 2

def test_graft_local():
    repo = createrepo(r"""
    3   [source=2 merge=local]
    | 2 [files=foo,bar]
    1 | [files=foo]
    |/
    0 [files=foo]
    """)
    foo = _contentgetter(repo, b"foo")
    assert foo(1) == foo(3)
    assert foo(2) != foo(3)

def test_graft_other():
    repo = createrepo(r"""
    3   [source=2 merge=other]
    | 2 [files=foo]
    1 | [files=foo]
    |/
    0 [files=foo]
    """)
    foo = _contentgetter(repo, b"foo")
    assert foo(1) != foo(3)
    assert foo(2) == foo(3)

def test_graft_user():
    repo = createrepo(r"""
    3   [source=2 user=bob]
    | 2
    1 |
    |/
    0
    """)
    assert repo[3].user() == b"bob"

def shouldraiseerror(graph, expected):
    # we can't use `with assert_raises()` because `with` is not supported
    # by Python 2.4
    try:
        createrepo(graph)
        assert False, "InvalidGraph should be raised"
    except helpers.InvalidGraph as ex:
        assert ex.innermessage == expected

def test_error_multirev():
    shouldraiseerror(r"""
    o o
    |/
    o
    """, "2 or more rev in same line")

def test_error_isolatededge():
    shouldraiseerror(r"""
    o
    |\
    |
    o
    """, "isolated edge")

def test_error_isolatededge_2():
    shouldraiseerror(r"""
    o
    |
    |/
    o
    """, "isolated edge")

def test_error_toomanyparents():
    shouldraiseerror(r"""
      o
     /|
    o |\
    |/  |
    +---o
    o
    """, "too many parents")

def test_error_toomanyparents_2():
    shouldraiseerror(r"""
      o-+
     /| |
    o | |
    |/  |
    +---o
    o
    """, "too many parents")

def test_error_invalidhorizontaledge():
    shouldraiseerror(r"""
    o
    +-+o+
    |/ /
    | /
    |/
    o
    """, "invalid horizontal edge")

def test_error_invalidhorizontaledge_2():
    shouldraiseerror(r"""
    o
    + o
    |/
    o
    """, "invalid horizontal edge")

def test_error_invalidsource_1():
    shouldraiseerror(r"""
    1 [source=1a]
    |
    0
    """, "`source` must be integer")

def test_error_invalidsource_2():
    shouldraiseerror(r"""
    1 [source=2]
    |
    0
    """, "`source` must point past revision")

def test_error_graftwith2parents():
    shouldraiseerror(r"""
    2 [source=1]
    |\
    | 1
    |/
    0
    """, "grafted revision must have only one parent")
