예제 #1
0
def test_merge_insert_cells_around_conflicting_cell():
    # Modifying an original cell and inserting a new cell on both sides
    source = [
        "def foo(x, y):\n",
        "    z = x * y\n",
        "    return z\n",
    ]
    local = [["new local cell\n"], source + ["local\n"]]
    base = [source]
    remote = [source + ["remote\n"], ["new remote cell\n"]]
    # Use mergetool strategy:
    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "mergetool"
    if 0:
        # This is how it would look if neither source or cell inserts resulted
        # in conflicts:
        expected_partial = [
            local[0], source + ["local\n", "remote\n"], remote[1]
        ]
        expected_conflicts = []
    elif 1:
        # This is how it would look if source inserts but not cell inserts
        # resulted in conflicts:
        expected_partial = [local[0], source, remote[1]]
        expected_conflicts = [{
            "common_path": ("cells", 0, "source"),
            "local_diff": [op_addrange(len(source), ["local\n"])],
            "remote_diff": [op_addrange(len(source), ["remote\n"])]
        }]
    else:
        # In current behaviour:
        # - base cell 0 is aligned correctly (this is the notebook diff heuristics)
        # - conflicting edits of base cell 0 detected
        # - local insert before cell 0 is treated as part of conflict on base cell 0
        # - remote insert after cell 0 is not treated as part of conflict
        # FIXME: This may not be the exact behaviour we want:
        # - For source, we might want both inserts to be part of the conflict.
        #   (if so, fix in generic merge and chunk collection)
        # - For cells, we might want both inserts to be ok, they are separate new cells after all. (use autoresolve for this?)
        # - Figure out the best behaviour and make it happen!
        expected_partial = [source, remote[1]]
        expected_conflicts = [{
            "common_path": ("cells", ),
            "local_diff": [
                op_addrange(
                    0, [nbformat.v4.new_code_cell(source=["new local cell"])]),
                op_patch(0, [
                    op_patch("source",
                             [op_addrange(len("".join(source)), "local\n")])
                ]),
            ],
            "remote_diff": [
                op_patch(0, [
                    op_patch('source',
                             [op_addrange(len("".join(source)), "remote\n")])
                ])
            ]
        }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts,
                   merge_args)
예제 #2
0
def test_deep_merge_lists_delete_no_conflict():
    # local removes an entry
    b = [[1, 3, 5], [2, 4, 6]]
    for i in range(len(b)):
        for j in range(len(b[i])):
            l = copy.deepcopy(b)
            r = copy.deepcopy(b)
            l[i].pop(j)
            m, lc, rc = merge(b, l, r)
            assert m == l
            assert lc == []
            assert rc == []

    # remote removes an entry
    b = [[1, 3, 5], [2, 4, 6]]
    for i in range(len(b)):
        for j in range(len(b[i])):
            l = copy.deepcopy(b)
            r = copy.deepcopy(b)
            r[i].pop(j)
            m, lc, rc = merge(b, l, r)
            assert m == r
            assert lc == []
            assert rc == []

    # both remove the same entry and one each
    b = [[1, 3, 5], [2, 4, 6]]
    l = [[1, 5], [2, 4]]  # deletes 3 and 6
    r = [[1, 5], [4, 6]]  # deletes 3 and 2
    m, lc, rc = merge(b, l, r)
    #assert m == [[1, 5], [2, 4], [1, 5], [4, 6]]  # This was expected behaviour before: clear b, add l, add r
    #assert m == [[1, 5], [4]]  # 2,3,6 should be gone. TODO: This is the naively ideal thought-reading behaviour. Possible?
    assert m == b  # conflicts lead to original kept in m
    assert lc == [op_addrange(0, l), op_removerange(0, 2)]
    assert rc == [op_addrange(0, r), op_removerange(0, 2)]
def test_merge_simple_cell_source_conflicting_insert():
    # Cell inserts at same location but no other modifications:
    # should this be accepted?
    local = [["base"], ["local"]]
    base = [["base"]]
    remote = [["base"], ["remote"]]
    if 1:  # Treat as conflict
        expected_partial = [["base"]]
        expected_conflicts = [{
            "common_path": ("cells",),
            "local_diff": [op_addrange(
                1, [nbformat.v4.new_code_cell(local[1][0])]),
            ],
            "remote_diff": [op_addrange(
                1, [nbformat.v4.new_code_cell(remote[1][0])]),
            ]
        }]
    else:  # Treat as non-conflict (insert both)
        expected_partial = [["base"], ["local"], ["remote"]]
        expected_conflicts = []

    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "mergetool"

    _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
예제 #4
0
파일: test_patch.py 프로젝트: minrk/nbdime
def test_patch_list():
    # Test +, single item insertion
    assert patch([], [op_add(0, 3)]) == [3]
    assert patch([], [op_add(0, 3), op_add(0, 4)]) == [3, 4]
    assert patch([], [op_add(0, 3), op_add(0, 4), op_add(0, 5)]) == [3, 4, 5]

    # Test -, single item deletion
    assert patch([3], [op_remove(0)]) == []
    assert patch([5, 6, 7], [op_remove(0)]) == [6, 7]
    assert patch([5, 6, 7], [op_remove(1)]) == [5, 7]
    assert patch([5, 6, 7], [op_remove(2)]) == [5, 6]
    assert patch([5, 6, 7], [op_remove(0), op_remove(2)]) == [6]

    # Test :, single item replace
    pass

    # Test !, item patch
    assert patch(["hello", "world"], [op_patch(0, [op_patch(0, [op_replace(0, "H")])]),
                                      op_patch(1, [op_patch(0, [op_remove(0), op_add(0, "W")])])]) == ["Hello", "World"]

    # Test ++, sequence insertion
    assert patch([], [op_addrange(0, [3, 4]), op_add(0, 5), op_addrange(0, [6, 7])]) == [3, 4, 5, 6, 7]

    # Test --, sequence deletion
    assert patch([5, 6, 7, 8], [op_removerange(0, 2)]) == [7, 8]
    assert patch([5, 6, 7, 8], [op_removerange(1, 2)]) == [5, 8]
    assert patch([5, 6, 7, 8], [op_removerange(2, 2)]) == [5, 6]
예제 #5
0
def test_patch_list():
    # Test +, single item insertion
    assert patch([], [op_add(0, 3)]) == [3]
    assert patch([], [op_add(0, 3), op_add(0, 4)]) == [3, 4]
    assert patch([], [op_add(0, 3), op_add(0, 4), op_add(0, 5)]) == [3, 4, 5]

    # Test -, single item deletion
    assert patch([3], [op_remove(0)]) == []
    assert patch([5, 6, 7], [op_remove(0)]) == [6, 7]
    assert patch([5, 6, 7], [op_remove(1)]) == [5, 7]
    assert patch([5, 6, 7], [op_remove(2)]) == [5, 6]
    assert patch([5, 6, 7], [op_remove(0), op_remove(2)]) == [6]

    # Test :, single item replace
    pass

    # Test !, item patch
    assert patch(["hello", "world"], [op_patch(0, [op_patch(0, [op_replace(0, "H")])]),
                                      op_patch(1, [op_patch(0, [op_remove(0), op_add(0, "W")])])]) == ["Hello", "World"]

    # Test ++, sequence insertion
    assert patch([], [op_addrange( 0, [3, 4]), op_add(0, 5), op_addrange( 0, [6, 7])]) == [3, 4, 5, 6, 7]

    # Test --, sequence deletion
    assert patch([5, 6, 7, 8], [op_removerange(0, 2)]) == [7, 8]
    assert patch([5, 6, 7, 8], [op_removerange(1, 2)]) == [5, 8]
    assert patch([5, 6, 7, 8], [op_removerange(2, 2)]) == [5, 6]
예제 #6
0
파일: test_patch.py 프로젝트: minrk/nbdime
def test_patch_str():
    # Test +, single item insertion
    assert patch("42", [op_patch(0, [op_add(0, "3"), op_remove(1)])]) == "34"

    # Test -, single item deletion
    assert patch("3", [op_patch(0, [op_remove(0)])]) == ""
    assert patch("42", [op_patch(0, [op_remove(0)])]) == "2"
    assert patch("425", [op_patch(0, [op_remove(0)])]) == "25"
    assert patch("425", [op_patch(0, [op_remove(1)])]) == "45"
    assert patch("425", [op_patch(0, [op_remove(2)])]) == "42"

    # Test :, single item replace
    assert patch("52", [op_patch(0, [op_replace(0, "4")])]) == "42"
    assert patch("41", [op_patch(0, [op_replace(1, "2")])]) == "42"
    assert patch("42", [op_patch(0, [op_replace(0, "3"), op_replace(1, "5")])]) == "35"
    assert patch("hello", [op_patch(0, [op_replace(0, "H")])]) == "Hello"
    # Replace by delete-then-insert
    assert patch("world", [op_patch(0, [op_remove(0), op_add(0, "W")])]) == "World"

    # Test !, item patch (doesn't make sense for str)
    pass

    # Test ++, sequence insertion
    assert patch("", [op_patch(0, [op_addrange(0, "34"), op_add(0, "5"), op_addrange(0, "67")])]) == "34567"

    # Test --, sequence deletion
    assert patch("abcd", [op_patch(0, [op_removerange(0, 2)])]) == "cd"
    assert patch("abcd", [op_patch(0, [op_removerange(1, 2)])]) == "ad"
    assert patch("abcd", [op_patch(0, [op_removerange(2, 2)])]) == "ab"
예제 #7
0
def test_deep_merge_lists_delete_no_conflict():
    # local removes an entry
    b = [[1, 3, 5], [2, 4, 6]]
    for i in range(len(b)):
        for j in range(len(b[i])):
            l = copy.deepcopy(b)
            r = copy.deepcopy(b)
            l[i].pop(j)
            decisions = decide_merge(b, l, r)
            assert apply_decisions(b, decisions) == l
            assert not any([d.conflict for d in decisions])

    # remote removes an entry
    b = [[1, 3, 5], [2, 4, 6]]
    for i in range(len(b)):
        for j in range(len(b[i])):
            l = copy.deepcopy(b)
            r = copy.deepcopy(b)
            r[i].pop(j)
            decisions = decide_merge(b, l, r)
            assert apply_decisions(b, decisions) == r
            assert not any([d.conflict for d in decisions])

    # both remove the same entry and one each
    b = [[1, 3, 5], [2, 4, 6]]
    l = [[1, 5], [2, 4]]  # deletes 3 and 6
    r = [[1, 5], [4, 6]]  # deletes 3 and 2
    decisions = decide_merge(b, l, r)
    m = apply_decisions(b, decisions)
    #assert m == [[1, 5], [2, 4], [1, 5], [4, 6]]  # This was expected behaviour before: clear b, add l, add r
    #assert m == [[1, 5], [4]]  # 2,3,6 should be gone. TODO: This is the naively ideal thought-reading behaviour. Possible?
    assert m == b  # conflicts lead to original kept in m
    assert decisions[0].conflict
    assert decisions[0].local_diff == [op_addrange(0, l), op_removerange(0, 2)]
    assert decisions[0].remote_diff == [op_addrange(0, r), op_removerange(0, 2)]
예제 #8
0
def test_deep_merge_lists_delete_no_conflict():
    # local removes an entry
    b = [[1, 3, 5], [2, 4, 6]]
    for i in range(len(b)):
        for j in range(len(b[i])):
            l = copy.deepcopy(b)
            r = copy.deepcopy(b)
            l[i].pop(j)
            decisions = decide_merge(b, l, r)
            assert apply_decisions(b, decisions) == l
            assert not any([d.conflict for d in decisions])

    # remote removes an entry
    b = [[1, 3, 5], [2, 4, 6]]
    for i in range(len(b)):
        for j in range(len(b[i])):
            l = copy.deepcopy(b)
            r = copy.deepcopy(b)
            r[i].pop(j)
            decisions = decide_merge(b, l, r)
            assert apply_decisions(b, decisions) == r
            assert not any([d.conflict for d in decisions])

    # both remove the same entry and one each
    b = [[1, 3, 5], [2, 4, 6]]
    l = [[1, 5], [2, 4]]  # deletes 3 and 6
    r = [[1, 5], [4, 6]]  # deletes 3 and 2
    decisions = decide_merge(b, l, r)
    m = apply_decisions(b, decisions)
    #assert m == [[1, 5], [2, 4], [1, 5], [4, 6]]  # This was expected behaviour before: clear b, add l, add r
    #assert m == [[1, 5], [4]]  # 2,3,6 should be gone. TODO: This is the naively ideal thought-reading behaviour. Possible?
    assert m == b  # conflicts lead to original kept in m
    assert decisions[0].conflict
    assert decisions[0].local_diff == [op_addrange(0, l), op_removerange(0, 2)]
    assert decisions[0].remote_diff == [op_addrange(0, r), op_removerange(0, 2)]
예제 #9
0
def test_merge_simple_cell_source_conflicting_insert():
    # Cell inserts at same location but no other modifications:
    # should this be accepted?
    local = [["base"], ["local"]]
    base = [["base"]]
    remote = [["base"], ["remote"]]
    if 1:  # Treat as conflict
        expected_partial = [["base"]]
        expected_conflicts = [{
            "common_path": ("cells",),
            "local_diff": [op_addrange(
                1, [nbformat.v4.new_code_cell(local[1][0])]),
            ],
            "remote_diff": [op_addrange(
                1, [nbformat.v4.new_code_cell(remote[1][0])]),
            ]
        }]
    else:  # Treat as non-conflict (insert both)
        expected_partial = [["base"], ["local"], ["remote"]]
        expected_conflicts = []

    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "mergetool"

    _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
예제 #10
0
def test_patch_str():
    # Test +, single item insertion
    assert patch("42", [op_patch(0, [op_add(0, "3"), op_remove(1)])]) == "34"

    # Test -, single item deletion
    assert patch("3", [op_patch(0, [op_remove(0)])]) == ""
    assert patch("42", [op_patch(0, [op_remove(0)])]) == "2"
    assert patch("425", [op_patch(0, [op_remove(0)])]) == "25"
    assert patch("425", [op_patch(0, [op_remove(1)])]) == "45"
    assert patch("425", [op_patch(0, [op_remove(2)])]) == "42"

    # Test :, single item replace
    assert patch("52", [op_patch(0, [op_replace(0, "4")])]) == "42"
    assert patch("41", [op_patch(0, [op_replace(1, "2")])]) == "42"
    assert patch("42", [op_patch(0, [op_replace(0, "3"), op_replace(1, "5")])]) == "35"
    assert patch("hello", [op_patch(0, [op_replace(0, "H")])]) == "Hello"
    # Replace by delete-then-insert
    assert patch("world", [op_patch(0, [op_remove(0), op_add(0, "W")])]) == "World"

    # Test !, item patch (doesn't make sense for str)
    pass

    # Test ++, sequence insertion
    assert patch("", [op_patch(0, [op_addrange( 0, "34"), op_add(0, "5"), op_addrange( 0, "67")])]) == "34567"

    # Test --, sequence deletion
    assert patch("abcd", [op_patch(0, [op_removerange(0, 2)])]) == "cd"
    assert patch("abcd", [op_patch(0, [op_removerange(1, 2)])]) == "ad"
    assert patch("abcd", [op_patch(0, [op_removerange(2, 2)])]) == "ab"
예제 #11
0
def test_merge_inserts_within_deleted_range():
    # Multiple inserts within a deleted range
    base = """
def f(x):
    return x**2

def g(x):
    return x + 2
"""

    # Insert foo and bar
    local = """
def foo(y):
    return y / 3

def f(x):
    return x**2

def bar(y):
    return y - 3

def g(x):
    return x + 2
"""

    remote = ""  # Delete all

    if 0:
        # This is quite optimistic and would require employing aggressive
        # attempts at automatic resolution beyond what git and meld do:
        expected_partial = """
def foo(y):
    return y / 3

def bar(y):
    return y - 3
"""
    else:
        expected_partial = base
        expected_conflicts = [{
            "common_path": ("cells", ),
            "local_diff": [
                op_patch("cells", [
                    op_addrange(0, [nbformat.v4.new_code_cell(local)]),
                    op_removerange(0, 1)
                ])
            ],
            "remote_diff": [
                op_patch("cells", [
                    op_addrange(0, [nbformat.v4.new_code_cell(remote)]),
                    op_removerange(0, 1)
                ])
            ]
        }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts)

    # Keep it failing
    assert False
def test_merge_cell_sources_neighbouring_inserts():
    base = sources_to_notebook([[
        "def f(x):",
        "    return x**2",
        ], [
        "def g(y):",
        "    return y + 2",
        ],
        ])
    local = sources_to_notebook([[
        "def f(x):",
        "    return x**2",
        ], [
        "print(f(3))",
        ], [
        "def g(y):",
        "    return y + 2",
        ],
        ])
    remote = sources_to_notebook([[
        "def f(x):",
        "    return x**2",
        ], [
        "print(f(7))",
        ], [
        "def g(y):",
        "    return y + 2",
        ],
        ])
    if 1:
        expected_partial = base
        expected_conflicts = [{
            "common_path": ("cells",),
            "local_diff": [op_addrange(1, [local.cells[1]])],
            "remote_diff": [op_addrange(1, [remote.cells[1]])]
        }]
    else:
        # Strategy local_then_remote:
        expected_partial = sources_to_notebook([[
            "def f(x):",
            "    return x**2",
            ], [
            "print(f(3))",
            ], [
            "print(f(7))",
            ], [
            "def g(y):",
            "    return y + 2",
            ],
            ])
        expected_conflicts = []

    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "mergetool"

    _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
예제 #13
0
def test_merge_input_strategy_inline_source_conflict():
    # Conflicting cell inserts at same location as removing old cell
    local = [["local\n", "some other\n", "lines\n", "to align\n"]]
    base = [["base\n", "some other\n", "lines\n", "to align\n"]]
    remote = [["remote\n", "some other\n", "lines\n", "to align\n"]]
    # Ideal case:
    if have_git:
        expected_partial = [[
            "<<<<<<< local\n",
            "local\n",
            "=======\n",
            "remote\n",
            ">>>>>>> remote\n",
            "some other\n",
            "lines\n",
            "to align\n",
            ]]
    else:
        # Fallback is not very smart yet:
        expected_partial = [[
            "<<<<<<< local\n",
            "local\n",
            "some other\n",
            "lines\n",
            "to align\n",
            '||||||| base\n',
            'base\n',
            'some other\n',
            'lines\n',
            'to align\n',
            "=======\n",
            "remote\n",
            "some other\n",
            "lines\n",
            "to align\n",
            ">>>>>>> remote",
            ]]
    expected_conflicts = [{
        "common_path": ("cells", 0, "source"),
        "local_diff": [
            op_addrange(0, local[0][0:1]),
            op_removerange(0, 1)
            ],
        "remote_diff": [
            op_addrange(0, remote[0][0:1]),
            op_removerange(0, 1)
            ],
        "custom_diff": [
            op_addrange(0, expected_partial[0]),
            op_removerange(0, len(base[0]))
            ],
        }]
    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "use-base"
    merge_args.input_strategy = "inline"
    _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
예제 #14
0
def test_merge_conflicts_get_diff_indices_shifted():
    # Trying to induce conflicts with shifting of diff indices
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
        ]
    local = [["same"], source+["local"], ["different"]]
    base = [["same"], source+["base"], ["same"]]
    remote = [["different"], source+["remote"], ["same"]]
    expected_partial = [["different"],
                        source + ["local", "remote"],
                        ["different"]]
    expected_conflicts = [{
        "common_path": (),
        "local_diff": [
            op_removerange(1, 1),
            op_addrange(1, ["left"]),
        ],
        "remote_diff": [
            op_removerange(1, 1),
            op_addrange(1, ["right"]),
        ]
    }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts)

    # Trying to induce conflicts with shifting of diff indices
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
        ]
    local = [["same"],
             source + ["long line with minor change L"],
             ["different"]]
    base = [["same"],
            source + ["long line with minor change"],
            ["same"]]
    remote = [["different"],
              source + ["long line with minor change R"],
              ["same"]]
    expected_partial = [["different"],
                        source + ["long line with minor change"],
                        ["different"]]
    expected_conflicts = [{
        "common_path": (),
        "local_diff": [
            op_removerange(1, 1),
            op_addrange(1, ["left"]),
        ],
        "remote_diff": [
            op_removerange(1, 1),
            op_addrange(1, ["right"]),
        ]
    }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts)
예제 #15
0
def test_merge_cell_sources_neighbouring_inserts():
    base = sources_to_notebook([[
        "def f(x):",
        "    return x**2",
        ], [
        "def g(y):",
        "    return y + 2",
        ],
        ])
    local = sources_to_notebook([[
        "def f(x):",
        "    return x**2",
        ], [
        "print(f(3))",
        ], [
        "def g(y):",
        "    return y + 2",
        ],
        ])
    remote = sources_to_notebook([[
        "def f(x):",
        "    return x**2",
        ], [
        "print(f(7))",
        ], [
        "def g(y):",
        "    return y + 2",
        ],
        ])
    if 1:
        expected_partial = base
        expected_conflicts = [{
            "common_path": ("cells",),
            "local_diff": [op_addrange(1, [local.cells[1]])],
            "remote_diff": [op_addrange(1, [remote.cells[1]])]
        }]
    else:
        # Strategy local_then_remote:
        expected_partial = sources_to_notebook([[
            "def f(x):",
            "    return x**2",
            ], [
            "print(f(3))",
            ], [
            "print(f(7))",
            ], [
            "def g(y):",
            "    return y + 2",
            ],
            ])
        expected_conflicts = []

    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "mergetool"

    _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
예제 #16
0
def test_merge_conflicts_get_diff_indices_shifted():
    # Trying to induce conflicts with shifting of diff indices
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
        ]
    local = [["same"], source+["local"], ["different"]]
    base = [["same"], source+["base"], ["same"]]
    remote = [["different"], source+["remote"], ["same"]]
    expected_partial = [["different"],
                        source + ["local", "remote"],
                        ["different"]]
    expected_conflicts = [{
        "common_path": (),
        "local_diff": [
            op_removerange(1, 1),
            op_addrange(1, ["left"]),
        ],
        "remote_diff": [
            op_removerange(1, 1),
            op_addrange(1, ["right"]),
        ]
    }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts)

    # Trying to induce conflicts with shifting of diff indices
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
        ]
    local = [["same"],
             source + ["long line with minor change L"],
             ["different"]]
    base = [["same"],
            source + ["long line with minor change"],
            ["same"]]
    remote = [["different"],
              source + ["long line with minor change R"],
              ["same"]]
    expected_partial = [["different"],
                        source + ["long line with minor change"],
                        ["different"]]
    expected_conflicts = [{
        "common_path": (),
        "local_diff": [
            op_removerange(1, 1),
            op_addrange(1, ["left"]),
        ],
        "remote_diff": [
            op_removerange(1, 1),
            op_addrange(1, ["right"]),
        ]
    }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts)
예제 #17
0
def test_merge_insert_cells_around_conflicting_cell():
    # Modifying an original cell and inserting a new cell on both sides
    source = [
        "def foo(x, y):\n",
        "    z = x * y\n",
        "    return z\n",
        ]
    local = [["new local cell\n"],
             source + ["local\n"]]
    base = [source]
    remote = [source + ["remote\n"],
              ["new remote cell\n"]]
    # Use mergetool strategy:
    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "mergetool"
    if 0:
        # This is how it would look if neither source or cell inserts resulted
        # in conflicts:
        expected_partial = [local[0],
                            source + ["local\n", "remote\n"],
                            remote[1]]
        expected_conflicts = []
    elif 1:
        # This is how it would look if source inserts but not cell inserts
        # resulted in conflicts:
        expected_partial = [local[0], source, remote[1]]
        expected_conflicts = [{
            "common_path": ("cells", 0, "source"),
            "local_diff": [op_addrange(len(source), ["local\n"])],
            "remote_diff": [op_addrange(len(source), ["remote\n"])]
        }]
    else:
        # In current behaviour:
        # - base cell 0 is aligned correctly (this is the notebook diff heuristics)
        # - conflicting edits of base cell 0 detected
        # - local insert before cell 0 is treated as part of conflict on base cell 0
        # - remote insert after cell 0 is not treated as part of conflict
        # FIXME: This may not be the exact behaviour we want:
        # - For source, we might want both inserts to be part of the conflict.
        #   (if so, fix in generic merge and chunk collection)
        # - For cells, we might want both inserts to be ok, they are separate new cells after all. (use autoresolve for this?)
        # - Figure out the best behaviour and make it happen!
        expected_partial = [source, remote[1]]
        expected_conflicts = [{
            "common_path": ("cells",),
            "local_diff": [
                op_addrange(0, [nbformat.v4.new_code_cell(
                    source=["new local cell"])]),
                op_patch(0, [op_patch("source", [
                    op_addrange(len("".join(source)), "local\n")])]),
            ],
            "remote_diff": [op_patch(0, [op_patch('source', [
                op_addrange(len("".join(source)), "remote\n")])])]
        }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
예제 #18
0
파일: test_merge.py 프로젝트: eotp/nbdime
def test_deep_merge_twosided_inserts_no_conflict():
    # local and remote adds an entry each in a new sublist
    b = [[1]]
    l = [[1], [2], [3]]
    r = [[1], [2], [4]]
    assert diff(b, l) == [op_addrange(1, [[2], [3]])]
    assert diff(b, r) == [op_addrange(1, [[2], [4]])]
    m, lc, rc = merge(b, l, r)
    # No identification of equal inserted list [2] expected from current algorithm
    assert m == [[1], [2], [3], [2], [4]]
    assert lc == []
    assert rc == []
예제 #19
0
def test_deep_merge_twosided_inserts_no_conflict():
    # local and remote adds an entry each in a new sublist
    b = [[1]]
    l = [[1], [2], [3]]
    r = [[1], [2], [4]]
    assert diff(b, l) == [op_addrange(1, [[2], [3]])]
    assert diff(b, r) == [op_addrange(1, [[2], [4]])]
    m, lc, rc = merge(b, l, r)
    # No identification of equal inserted list [2] expected from current algorithm
    assert m == [[1], [2], [3], [2], [4]]
    assert lc == []
    assert rc == []
예제 #20
0
def test_merge_inserts_within_deleted_range():
    # Multiple inserts within a deleted range
    base = """
def f(x):
    return x**2

def g(x):
    return x + 2
"""

    # Insert foo and bar
    local = """
def foo(y):
    return y / 3

def f(x):
    return x**2

def bar(y):
    return y - 3

def g(x):
    return x + 2
"""

    remote = ""  # Delete all

    if 0:
        # This is quite optimistic and would require employing aggressive
        # attempts at automatic resolution beyond what git and meld do:
        expected_partial = """
def foo(y):
    return y / 3

def bar(y):
    return y - 3
"""
    else:
        expected_partial = base
        expected_conflicts = [{
            "common_path": ("cells",),
            "local_diff": [op_patch("cells", [op_addrange(0, [
                nbformat.v4.new_code_cell(local)]), op_removerange(0, 1)])],
            "remote_diff": [op_patch("cells", [op_addrange(0, [
                nbformat.v4.new_code_cell(remote)]), op_removerange(0, 1)])]
        }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts)

    # Keep it failing
    assert False
예제 #21
0
def test_merge_conflicts_get_diff_indices_shifted():
    # Trying to induce conflicts with shifting of diff indices
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
    ]
    local = [["same"], source + ["local"], ["different"]]
    base = [["same"], source + ["base"], ["same"]]
    remote = [["different"], source + ["remote"], ["same"]]
    expected_partial = [["different"], source + ["local", "remote"],
                        ["different"]]
    expected_lco = [
        op_removerange(1, 1),
        op_addrange(1, ["left"]),
    ]
    expected_rco = [
        op_removerange(1, 1),
        op_addrange(1, ["right"]),
    ]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # Trying to induce conflicts with shifting of diff indices
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
    ]
    local = [["same"], source + ["long line with minor change L"],
             ["different"]]
    base = [["same"], source + ["long line with minor change"], ["same"]]
    remote = [["different"], source + ["long line with minor change R"],
              ["same"]]
    expected_partial = [["different"],
                        source + ["long line with minor change"],
                        ["different"]]
    expected_lco = [
        op_removerange(1, 1),
        op_addrange(1, ["left"]),  # todo
    ]
    expected_rco = [
        op_removerange(1, 1),
        op_addrange(1, ["right"]),  # todo
    ]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)
예제 #22
0
def test_merge_multiline_cell_source_conflict():
    # Modifying cell on both sides interpreted as editing the original cell
    # (this is where heuristics kick in: when is a cell modified and when is it replaced?)
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
        ]
    local  = [source + ["local"]]
    base   = [source]
    remote = [source + ["remote"]]
    expected_partial = base
    expected_lco = _patch_cell_source(0, [op_addrange(len("\n".join(source)), "\nlocal")])
    expected_rco = _patch_cell_source(0, [op_addrange(len("\n".join(source)), "\nremote")])
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)
예제 #23
0
def test_merge_insert_cells_around_conflicting_cell():
    # Modifying an original cell and inserting a new cell on both sides
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
    ]
    local = [["new local cell"], source + ["local"]]
    base = [source]
    remote = [source + ["remote"], ["new remote cell"]]
    if 0:
        # This is how it would look if neither source or cell inserts resulted in conflicts:
        expected_partial = [["new local cell"], source + ["local", "remote"],
                            ["new remote cell"]]
        expected_lco = []
        expected_rco = []
    elif 0:
        # This is how it would look if source inserts but not cell inserts resulted in conflicts:
        expected_partial = [local[0], source, remote[1]]
        expected_lco = _patch_cell_source(
            1, [op_addrange(len("\n".join(source)), "\nlocal")])
        expected_rco = _patch_cell_source(
            1, [op_addrange(len("\n".join(source)), "\nremote")])
    else:
        # In current behaviour:
        # - base cell 0 is aligned correctly (this is the notebook diff heuristics)
        # - conflicting edits of base cell 0 detected
        # - local insert before cell 0 is treated as part of conflict on base cell 0
        # - remote insert after cell 0 is not treated as part of conflict
        # FIXME: This may not be the exact behaviour we want:
        # - For source, we might want both inserts to be part of the conflict.
        #   (if so, fix in generic merge and chunk collection)
        # - For cells, we might want both inserts to be ok, they are separate new cells after all. (use autoresolve for this?)
        # - Figure out the best behaviour and make it happen!
        expected_partial = [source, remote[1]]
        expected_lco = [
            op_patch("cells", [
                op_addrange(
                    0, [nbformat.v4.new_code_cell(source=["new local cell"])]),
                op_patch(0, [
                    op_patch("source",
                             [op_addrange(len("\n".join(source)), "\nlocal")])
                ]),
            ])
        ]
        expected_rco = _patch_cell_source(
            0, [op_addrange(len("\n".join(source)), "\nremote")])
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)
예제 #24
0
def test_deep_merge_twosided_inserts_conflicted2():
    # local and remote adds an entry each in a new sublist
    b = [[1]]
    l = [[1], [2], [3]]
    r = [[1], [2], [4]]
    assert diff(b, l) == [op_addrange(1, [[2], [3]])]
    assert diff(b, r) == [op_addrange(1, [[2], [4]])]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == [[1], [2]]
    assert len(decisions) == 2
    assert_either_decision(decisions[0], [op_addrange(1, [[2]])])
    d = decisions[1]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(1, l[2:])]
    assert d.remote_diff == [op_addrange(1, r[2:])]
예제 #25
0
def test_deep_merge_twosided_inserts_conflicted2():
    # local and remote adds an entry each in a new sublist
    b = [[1]]
    l = [[1], [2], [3]]
    r = [[1], [2], [4]]
    assert diff(b, l) == [op_addrange(1, [[2], [3]])]
    assert diff(b, r) == [op_addrange(1, [[2], [4]])]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == [[1], [2]]
    assert len(decisions) == 2
    assert_either_decision(decisions[0], [op_addrange(1, [[2]])])
    d = decisions[1]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(1, l[2:])]
    assert d.remote_diff == [op_addrange(1, r[2:])]
예제 #26
0
def test_merge_multiline_cell_source_conflict():
    # Modifying cell on both sides interpreted as editing the original cell
    # (this is where heuristics kick in: when is a cell modified and when is it replaced?)
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
    ]
    local = [source + ["local"]]
    base = [source]
    remote = [source + ["remote"]]
    expected_partial = base
    expected_lco = _patch_cell_source(0, [op_addrange(len(source), ["local"])])
    expected_rco = _patch_cell_source(0,
                                      [op_addrange(len(source), ["remote"])])
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)
예제 #27
0
def test_merge_input_strategy_inline_source_conflict():
    # Conflicting cell inserts at same location as removing old cell
    local = [["local\n", "some other\n", "lines\n", "to align\n"]]
    base = [["base\n", "some other\n", "lines\n", "to align\n"]]
    remote = [["remote\n", "some other\n", "lines\n", "to align\n"]]
    # Ideal case:
    expected_partial = [[
       "<<<<<<< local\n",
       "local\n",
       #"||||||| base\n",
       #"base\n",
       "=======\n",
       "remote\n",
       ">>>>>>> remote\n",
       "some other\n",
       "lines\n",
       "to align\n",
       ]]
    # Current case:
    _expected_partial = [[
        "<<<<<<< local\n",
        "local\nsome other\nlines\nto align\n",
        "||||||| base\n",
        "base\nsome other\nlines\nto align\n",
        "=======\n",
        "remote\nsome other\nlines\nto align\n",
        ">>>>>>> remote"]]
    expected_conflicts = [{
        "common_path": ("cells", 0),
        "local_diff": [
            op_patch('source', [
                op_addrange(
                    0, local[0][0:1]),
                op_removerange(0, 1)],
            )],
        "remote_diff": [
            op_patch('source', [
                op_addrange(
                    0, remote[0][0:1]),
                op_removerange(0, 1)],

            )],
        }]
    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "use-base"
    merge_args.input_strategy = "inline"
    _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
예제 #28
0
def test_merge_conflicts_get_diff_indices_shifted():
    # Trying to induce conflicts with shifting of diff indices
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
        ]
    local  = [["same"], source+["local"], ["different"]]
    base   = [["same"], source+["base"], ["same"]]
    remote = [["different"], source+["remote"], ["same"]]
    expected_partial = [["different"], source+["local", "remote"], ["different"]]
    expected_lco = [
        op_removerange(1, 1),
        op_addrange(1, ["left"]),
        ]
    expected_rco = [
        op_removerange(1, 1),
        op_addrange(1, ["right"]),
        ]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)


    # Trying to induce conflicts with shifting of diff indices
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
        ]
    local  = [["same"], source+["long line with minor change L"], ["different"]]
    base   = [["same"], source+["long line with minor change"], ["same"]]
    remote = [["different"], source+["long line with minor change R"], ["same"]]
    expected_partial = [["different"], source+["long line with minor change"], ["different"]]
    expected_lco = [
        op_removerange(1, 1),
        op_addrange(1, ["left"]), # todo
        ]
    expected_rco = [
        op_removerange(1, 1),
        op_addrange(1, ["right"]), # todo
        ]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)
예제 #29
0
def test_merge_simple_cell_source_conflicting_edit_aligned():
    # Conflicting edit in first line of single cell
    local = [["local\n", "some other\n", "lines\n", "to align\n"]]
    base = [["base\n", "some other\n", "lines\n", "to align\n"]]
    remote = [["remote\n", "some other\n", "lines\n", "to align\n"]]
    expected_partial = [["base\n", "some other\n", "lines\n", "to align\n"]]
    expected_conflicts = [{
        "common_path": ("cells", 0, "source"),
        "local_diff": [op_addrange(0, local[0][0:1]),
                       op_removerange(0, 1)],
        "remote_diff": [op_addrange(0, remote[0][0:1]),
                        op_removerange(0, 1)]
    }]
    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "mergetool"

    _check_sources(base, local, remote, expected_partial, expected_conflicts,
                   merge_args)
예제 #30
0
def test_merge_insert_cells_around_conflicting_cell():
    # Modifying an original cell and inserting a new cell on both sides
    source = [
        "def foo(x, y):",
        "    z = x * y",
        "    return z",
        ]
    local  = [["new local cell"],
              source + ["local"]]
    base   = [source]
    remote = [source + ["remote"],
              ["new remote cell"]]
    if 0:
        # This is how it would look if neither source or cell inserts resulted in conflicts:
        expected_partial = [["new local cell"],
                            source + ["local", "remote"],
                            ["new remote cell"]]
        expected_lco = []
        expected_rco = []
    elif 0:
        # This is how it would look if source inserts but not cell inserts resulted in conflicts:
        expected_partial = [local[0], source, remote[1]]
        expected_lco = _patch_cell_source(1, [op_addrange(len("\n".join(source)), "\nlocal")])
        expected_rco = _patch_cell_source(1, [op_addrange(len("\n".join(source)), "\nremote")])
    else:
        # In current behaviour:
        # - base cell 0 is aligned correctly (this is the notebook diff heuristics)
        # - conflicting edits of base cell 0 detected
        # - local insert before cell 0 is treated as part of conflict on base cell 0
        # - remote insert after cell 0 is not treated as part of conflict
        # FIXME: This may not be the exact behaviour we want:
        # - For source, we might want both inserts to be part of the conflict.
        #   (if so, fix in generic merge and chunk collection)
        # - For cells, we might want both inserts to be ok, they are separate new cells after all. (use autoresolve for this?)
        # - Figure out the best behaviour and make it happen!
        expected_partial = [source, remote[1]]
        expected_lco = [op_patch("cells", [
            op_addrange(0, [nbformat.v4.new_code_cell(source=["new local cell"])]),
            op_patch(0, [op_patch("source",
                                  [op_addrange(len("\n".join(source)), "\nlocal")]
                                  )]),
            ])]
        expected_rco = _patch_cell_source(0, [op_addrange(len("\n".join(source)), "\nremote")])
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)
예제 #31
0
def test_merge_simple_cell_source_conflicting_edit_aligned():
    # Conflicting cell inserts at same location as removing old cell
    local = [["local\n", "some other\n", "lines\n", "to align\n"]]
    base = [["base\n", "some other\n", "lines\n", "to align\n"]]
    remote = [["remote\n", "some other\n", "lines\n", "to align\n"]]
    expected_partial = [["base\n", "some other\n", "lines\n", "to align\n"]]
    expected_conflicts = [{
        "common_path": ("cells", 0, "source"),
        "local_diff": [
            op_addrange(
                0, local[0][0:1]),
            op_removerange(0, 1)],
        "remote_diff": [
            op_addrange(
                0, remote[0][0:1]),
            op_removerange(0, 1)]
        }]
    merge_args = copy.deepcopy(args)
    merge_args.merge_strategy = "mergetool"
    _check_sources(base, local, remote, expected_partial, expected_conflicts, merge_args)
예제 #32
0
def test_merge_multiline_cell_source_conflict():
    # Modifying cell on both sides interpreted as editing the original cell
    # (this is where heuristics kick in: when is a cell modified and when is
    # it replaced?)
    source = [
        "def foo(x, y):\n",
        "    z = x * y\n",
        "    return z\n",
    ]
    local = [source + ["local\n"] + [""]]
    base = [source + [""]]
    remote = [source + ["remote\n"] + [""]]

    le = op_addrange(3, "local\n")
    re = op_addrange(3, "remote\n")

    expected_partial = base
    expected_conflicts = [{
        "common_path": ("cells", "0", "source"),
        "local_diff": [le],
        "remote_diff": [re]
    }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts)
예제 #33
0
def test_merge_multiline_cell_source_conflict():
    # Modifying cell on both sides interpreted as editing the original cell
    # (this is where heuristics kick in: when is a cell modified and when is
    # it replaced?)
    source = [
        "def foo(x, y):\n",
        "    z = x * y\n",
        "    return z\n",
        ]
    local = [source + ["local\n"] + [""]]
    base = [source + [""]]
    remote = [source + ["remote\n"] + [""]]

    le = op_addrange(3, "local\n")
    re = op_addrange(3, "remote\n")

    expected_partial = base
    expected_conflicts = [{
        "common_path": ("cells", "0", "source"),
        "local_diff": [le],
        "remote_diff": [re]
    }]
    _check_sources(base, local, remote, expected_partial, expected_conflicts)
예제 #34
0
def test_deep_merge_lists_insert_conflicted():

    # Some notes explaining the below expected values... while this works:
    assert diff([1], [1, 2]) == [op_addrange(1, [2])]
    # This does not happen:
    #assert diff([[1]], [[1, 2]]) == [op_patch(0, [op_addrange(1, [2])])]
    # Instead we get this:
    assert diff([[1]],
                [[1, 2]]) == [op_addrange(0, [[1, 2]]),
                              op_removerange(0, 1)]
    # To get the "patch inner list" version instead of the "remove inner list + add new inner list" version,
    # the diff algorithm would need to identify that the inner list [1] is similar to [1,2],
    # e.g. through heuristics. In the case [1] vs [1,2] the answer will probably be "not similar enough" even
    # with better heuristics than we have today, i.e. we can never be quite certain what the "right choice" is.

    # *** Because of this uncertainty, insertions at the same location are suspect and must be treated as conflicts! ***

    # local and remote adds an entry each to inner list
    # (documents failure to identify inner list patching opportunity)
    b = [[1]]
    l = [[1, 2]]
    r = [[1, 3]]
    decisions = decide_merge(b, l, r)
    #assert apply_decisions(b, decisions) == [[1, 2], [1, 3]]  # This was expected behaviour in old code, obviously not what we want
    #assert apply_decisions(b, decisions) == [[1, 2, 3]]  # This is the behaviour we want from an ideal thought-reading algorithm, unclear if possible
    #assert apply_decisions(b, decisions) == [[1]]  # This is the behaviour we get if reverts to base value
    assert len(decisions) == 1
    d = decisions[0]
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(0, [[1, 2]]), op_removerange(0, 1)]
    assert d.remote_diff == [op_addrange(0, [[1, 3]]), op_removerange(0, 1)]

    # local and remote adds the same entry plus an entry each
    b = [[1]]
    l = [[1, 2, 4]]
    r = [[1, 3, 4]]
    decisions = decide_merge(b, l, r)
    # No identification of equal inserted value 4 expected from current algorithm
    #assert apply_decisions(b, decisions) == [[1, 2, 4, 3, 4]]  # TODO: Is this the behaviour we want, merge in inner list?
    #assert apply_decisions(b, decisions) == [[1, 2, 4], [1, 3, 4]]  # This was expected behaviour in previous algorithm
    #assert lc == []
    #assert rc == []
    assert apply_decisions(b, decisions) == [[
        1
    ]]  # This is expected behaviour today, base left for conflict resolution
    assert len(decisions) == 1
    d = decisions[0]
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(0, [[1, 2, 4]]), op_removerange(0, 1)]
    assert d.remote_diff == [op_addrange(0, [[1, 3, 4]]), op_removerange(0, 1)]
예제 #35
0
def test_deep_merge_lists_insert_conflicted():

    # Some notes explaining the below expected values... while this works:
    assert diff([1], [1, 2]) == [op_addrange(1, [2])]
    # This does not happen:
    #assert diff([[1]], [[1, 2]]) == [op_patch(0, [op_addrange(1, [2])])]
    # Instead we get this:
    assert diff([[1]], [[1, 2]]) == [op_addrange(0, [[1, 2]]), op_removerange(0, 1)]
    # To get the "patch inner list" version instead of the "remove inner list + add new inner list" version,
    # the diff algorithm would need to identify that the inner list [1] is similar to [1,2],
    # e.g. through heuristics. In the case [1] vs [1,2] the answer will probably be "not similar enough" even
    # with better heuristics than we have today, i.e. we can never be quite certain what the "right choice" is.

    # *** Because of this uncertainty, insertions at the same location are suspect and must be treated as conflicts! ***


    # local and remote adds an entry each to inner list
    # (documents failure to identify inner list patching opportunity)
    b = [[1]]
    l = [[1, 2]]
    r = [[1, 3]]
    decisions = decide_merge(b, l, r)
    #assert apply_decisions(b, decisions) == [[1, 2], [1, 3]]  # This was expected behaviour in old code, obviously not what we want
    #assert apply_decisions(b, decisions) == [[1, 2, 3]]  # This is the behaviour we want from an ideal thought-reading algorithm, unclear if possible
    #assert apply_decisions(b, decisions) == [[1]]  # This is the behaviour we get if reverts to base value
    assert len(decisions) == 1
    d = decisions[0]
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(0, [[1, 2]]), op_removerange(0, 1)]
    assert d.remote_diff == [op_addrange(0, [[1, 3]]), op_removerange(0, 1)]

    # local and remote adds the same entry plus an entry each
    b = [[1]]
    l = [[1, 2, 4]]
    r = [[1, 3, 4]]
    decisions = decide_merge(b, l, r)
    # No identification of equal inserted value 4 expected from current algorithm
    #assert apply_decisions(b, decisions) == [[1, 2, 4, 3, 4]]  # TODO: Is this the behaviour we want, merge in inner list?
    #assert apply_decisions(b, decisions) == [[1, 2, 4], [1, 3, 4]]  # This was expected behaviour in previous algorithm
    #assert lc == []
    #assert rc == []
    assert apply_decisions(b, decisions) == [[1]]  # This is expected behaviour today, base left for conflict resolution
    assert len(decisions) == 1
    d = decisions[0]
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(0, [[1, 2, 4]]), op_removerange(0, 1)]
    assert d.remote_diff == [op_addrange(0, [[1, 3, 4]]), op_removerange(0, 1)]
예제 #36
0
def test_deep_merge_twosided_inserts_conflicted():
    # local and remote adds an entry each in a new sublist
    b = []
    l = [[2], [3]]
    r = [[2], [4]]
    assert diff(b, l) == [op_addrange(0, [[2], [3]])]
    assert diff(b, r) == [op_addrange(0, [[2], [4]])]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == [[2]]
    assert len(decisions) == 2
    d = decisions[0]
    assert not d.conflict
    assert d.common_path == ()
    assert d.action == 'either'
    assert d.local_diff == [op_addrange(0, [[2]])]
    assert d.remote_diff == [op_addrange(0, [[2]])]
    d = decisions[1]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(0, [[3]])]
    assert d.remote_diff == [op_addrange(0, [[4]])]
예제 #37
0
def test_deep_merge_twosided_inserts_conflicted():
    # local and remote adds an entry each in a new sublist
    b = []
    l = [[2], [3]]
    r = [[2], [4]]
    assert diff(b, l) == [op_addrange(0, [[2], [3]])]
    assert diff(b, r) == [op_addrange(0, [[2], [4]])]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == [[2]]
    assert len(decisions) == 2
    d = decisions[0]
    assert not d.conflict
    assert d.common_path == ()
    assert d.action == 'either'
    assert d.local_diff == [op_addrange(0, [[2]])]
    assert d.remote_diff == [op_addrange(0, [[2]])]
    d = decisions[1]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(0, [[3]])]
    assert d.remote_diff == [op_addrange(0, [[4]])]
예제 #38
0
def test_shallow_merge_lists_insert_conflicted():
    # local and remote adds an entry each
    b = [1]
    l = [1, 2]
    r = [1, 3]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == b
    assert len(decisions) == 1
    d = decisions[0]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(1, [2])]
    assert d.remote_diff == [op_addrange(1, [3])]

    # local and remote adds an equal entry plus a different entry each
    b = [1, 9]
    l = [1, 2, 7, 9]
    r = [1, 3, 7, 9]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == [1, 7, 9]
    assert len(decisions) == 2
    d = decisions[0]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(1, [2])]
    assert d.remote_diff == [op_addrange(1, [3])]
    d = decisions[1]
    assert d.common_path == ()
    assert_either_decision(d, [op_addrange(1, [7])])

    # local and remote adds entries to empty base
    b = []
    l = [1, 2, 4]
    r = [1, 3, 4]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == [1, 4]
    assert len(decisions) == 3
    d = decisions[0]
    assert d.common_path == ()
    assert_either_decision(d, [op_addrange(0, [1])])
    d = decisions[1]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(0, [2])]
    assert d.remote_diff == [op_addrange(0, [3])]
    d = decisions[2]
    assert d.common_path == ()
    assert_either_decision(d, [op_addrange(0, [4])])

    # local and remote adds different entries interleaved within each base entry
    b = [2, 5, 8]
    l = [0, 2, 3, 5, 6, 8, 9]
    r = [1, 2, 4, 5, 7, 8, 10]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == b
    assert len(decisions) == 4
    assert all([d.conflict for d in decisions])
    assert all([d.common_path == () for d in decisions])
    assert decisions[0].local_diff == [op_addrange(0, [0])]
    assert decisions[0].remote_diff == [op_addrange(0, [1])]
    assert decisions[1].local_diff == [op_addrange(1, [3])]
    assert decisions[1].remote_diff == [op_addrange(1, [4])]
    assert decisions[2].local_diff == [op_addrange(2, [6])]
    assert decisions[2].remote_diff == [op_addrange(2, [7])]
    assert decisions[3].local_diff == [op_addrange(3, [9])]
    assert decisions[3].remote_diff == [op_addrange(3, [10])]
예제 #39
0
def test_shallow_merge_lists_insert_conflicted():
    # local and remote adds an entry each
    b = [1]
    l = [1, 2]
    r = [1, 3]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == b
    assert len(decisions) == 1
    d = decisions[0]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(1, [2])]
    assert d.remote_diff == [op_addrange(1, [3])]

    # local and remote adds an equal entry plus a different entry each
    b = [1, 9]
    l = [1, 2, 7, 9]
    r = [1, 3, 7, 9]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == [1, 7, 9]
    assert len(decisions) == 2
    d = decisions[0]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(1, [2])]
    assert d.remote_diff == [op_addrange(1, [3])]
    d = decisions[1]
    assert d.common_path == ()
    assert_either_decision(d, [op_addrange(1, [7])])

    # local and remote adds entries to empty base
    b = []
    l = [1, 2, 4]
    r = [1, 3, 4]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == [1, 4]
    assert len(decisions) == 3
    d = decisions[0]
    assert d.common_path == ()
    assert_either_decision(d, [op_addrange(0, [1])])
    d = decisions[1]
    assert d.conflict
    assert d.common_path == ()
    assert d.local_diff == [op_addrange(0, [2])]
    assert d.remote_diff == [op_addrange(0, [3])]
    d = decisions[2]
    assert d.common_path == ()
    assert_either_decision(d, [op_addrange(0, [4])])

    # local and remote adds different entries interleaved within each base entry
    b = [2, 5, 8]
    l = [0, 2, 3, 5, 6, 8, 9]
    r = [1, 2, 4, 5, 7, 8, 10]
    decisions = decide_merge(b, l, r)
    assert apply_decisions(b, decisions) == b
    assert len(decisions) == 4
    assert all([d.conflict for d in decisions])
    assert all([d.common_path == () for d in decisions])
    assert decisions[0].local_diff == [op_addrange(0, [0])]
    assert decisions[0].remote_diff == [op_addrange(0, [1])]
    assert decisions[1].local_diff == [op_addrange(1, [3])]
    assert decisions[1].remote_diff == [op_addrange(1, [4])]
    assert decisions[2].local_diff == [op_addrange(2, [6])]
    assert decisions[2].remote_diff == [op_addrange(2, [7])]
    assert decisions[3].local_diff == [op_addrange(3, [9])]
    assert decisions[3].remote_diff == [op_addrange(3, [10])]
예제 #40
0
def test_merge_simple_cell_sources():
    # A very basic test: Just checking changes to a single cell source,

    # No change
    local  = [["same"]]
    base   = [["same"]]
    remote = [["same"]]
    expected_partial = [["same"]]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # One sided change
    local  = [["same"]]
    base   = [["same"]]
    remote = [["different"]]
    expected_partial = [["different"]]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # One sided change
    local  = [["different"]]
    base   = [["same"]]
    remote = [["same"]]
    expected_partial = [["different"]]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # Same change on both sides
    local  = [["different"]]
    base   = [["same"]]
    remote = [["different"]]
    expected_partial = [["different"]]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # Conflicting cell inserts at same location as removing old cell
    local  = [["local"]]
    base   = [["base"]]
    remote = [["remote"]]
    expected_partial = [["base"]]
    expected_lco = [op_patch("cells", [
        op_addrange(0, [nbformat.v4.new_code_cell(source) for source in local]),
        op_removerange(0, 1)])]
    expected_rco = [op_patch("cells", [
        op_addrange(0, [nbformat.v4.new_code_cell(source) for source in remote]),
        op_removerange(0, 1)])]
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # Cell inserts at same location but no other modifications: should this be accepted?
    local  = [["base"], ["local"]]
    base   = [["base"]]
    remote = [["base"], ["remote"]]
    if 0:  # Treat as conflict
        expected_partial = [["base"]]
        expected_lco = [op_patch("cells", [
            op_addrange(1, [nbformat.v4.new_code_cell(source) for source in local]),
            ])]
        expected_rco = [op_patch("cells", [
            op_addrange(1, [nbformat.v4.new_code_cell(source) for source in remote]),
            ])]
    else:  # Treat as non-conflict (insert both)
        expected_partial = [["base"], ["local"], ["remote"]]
        expected_lco = []
        expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)
예제 #41
0
def test_merge_simple_cell_sources():
    # A very basic test: Just checking changes to a single cell source,

    # No change
    local = [["same"]]
    base = [["same"]]
    remote = [["same"]]
    expected_partial = [["same"]]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # One sided change
    local = [["same"]]
    base = [["same"]]
    remote = [["different"]]
    expected_partial = [["different"]]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # One sided change
    local = [["different"]]
    base = [["same"]]
    remote = [["same"]]
    expected_partial = [["different"]]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # Same change on both sides
    local = [["different"]]
    base = [["same"]]
    remote = [["different"]]
    expected_partial = [["different"]]
    expected_lco = []
    expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # Conflicting cell inserts at same location as removing old cell
    local = [["local"]]
    base = [["base"]]
    remote = [["remote"]]
    expected_partial = [["base"]]
    expected_lco = [
        op_patch("cells", [
            op_addrange(
                0, [nbformat.v4.new_code_cell(source) for source in local]),
            op_removerange(0, 1)
        ])
    ]
    expected_rco = [
        op_patch("cells", [
            op_addrange(
                0, [nbformat.v4.new_code_cell(source) for source in remote]),
            op_removerange(0, 1)
        ])
    ]
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)

    # Cell inserts at same location but no other modifications: should this be accepted?
    local = [["base"], ["local"]]
    base = [["base"]]
    remote = [["base"], ["remote"]]
    if 0:  # Treat as conflict
        expected_partial = [["base"]]
        expected_lco = [
            op_patch("cells", [
                op_addrange(
                    1, [nbformat.v4.new_code_cell(source)
                        for source in local]),
            ])
        ]
        expected_rco = [
            op_patch("cells", [
                op_addrange(
                    1,
                    [nbformat.v4.new_code_cell(source) for source in remote]),
            ])
        ]
    else:  # Treat as non-conflict (insert both)
        expected_partial = [["base"], ["local"], ["remote"]]
        expected_lco = []
        expected_rco = []
    _check(base, local, remote, expected_partial, expected_lco, expected_rco)