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_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]
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)]
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]
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"
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"
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_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)
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)
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_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)]
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)
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)]
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)
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)
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)
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)
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)
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)