def test_decide_merge_list_conflicting_insertions_in_chunks():
    # Next, test when insertions DO chunk together:
    b = [1, 9]
    l = [1, 2, 7, 9]
    r = [1, 3, 7, 9]

    # Check strategyless resolution
    strategies = Strategies({})
    resolved = decide_merge(b, l, r, strategies)
    expected_partial = [1, 7, 9]
    assert apply_decisions(b, resolved) == expected_partial

    strategies = Strategies({"/*": "use-local"})
    resolved = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, resolved) == l
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-remote"})
    resolved = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, resolved) == r
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-base"})
    resolved = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, resolved) == expected_partial
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/": "clear-all"})
    resolved = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, resolved) == []
    assert not any(d.conflict for d in resolved)
Exemple #2
0
def test_autoresolve_list_conflicting_insertions_simple():
    # local and remote adds an entry each
    b = [1]
    l = [1, 2]
    r = [1, 3]
    decisions = decide_merge(b, l, r)

    strategies = Strategies({"/*": "use-local"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == l
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-remote"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == r
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-base"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == b
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "union"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == [1, 2, 3]
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "clear-parent"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == []
    assert not any(d.conflict for d in resolved)
def test_decide_merge_simple_list_insert_conflict_resolution():
    # local and remote adds an entry each
    b = [1]
    l = [1, 2]
    r = [1, 3]

    strategies = Strategies({"/*": "use-local"})
    decisions = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, decisions) == l
    assert not any(d.conflict for d in decisions)

    strategies = Strategies({"/*": "use-remote"})
    decisions = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, decisions) == r
    assert not any(d.conflict for d in decisions)

    strategies = Strategies({"/*": "use-base"})
    decisions = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, decisions) == b
    assert not any(d.conflict for d in decisions)

    strategies = Strategies({"/": "clear-all"})
    decisions = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, decisions) == []
    assert not any(d.conflict for d in decisions)
def test_autoresolve_dict_transients():
    # For this test, we need to use a custom predicate to ensure alignment

    common = {'id': 'This ensures alignment'}
    # Setup transient difference in base and local, deletion in remote
    b = {'a': {'transient': 22}}
    l = {'a': {'transient': 242}}
    r = {}

    # Make decisions based on diffs with predicates
    decisions = decide_merge(b, l, r)

    # Assert that generic merge gives conflict
    assert apply_decisions(b, decisions) == b
    assert len(decisions) == 1
    assert decisions[0].conflict

    # Without strategy, no progress is made:
    resolved = autoresolve(b, decisions, Strategies())
    assert resolved == decisions

    # Supply transient list to autoresolve, and check that transient is ignored
    strategies = Strategies(transients=['/a/transient'])
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == r
    assert not any(d.conflict for d in resolved)
def test_autoresolve_mixed_nested_transients():
    # For this test, we need to use a custom predicate to ensure alignment
    common = {'id': 'This ensures alignment'}
    predicates = defaultdict(lambda: [operator.__eq__], {
        '/': [lambda a, b: a['id'] == b['id']],
    })
    # Setup transient difference in base and local, deletion in remote
    b = [{'a': {'transient': 22}}]
    l = [{'a': {'transient': 242}}]
    b[0].update(common)
    l[0].update(common)
    r = []

    # Make decisions based on diffs with predicates
    ld = diff(b, l, path="", predicates=predicates)
    rd = diff(b, r, path="", predicates=predicates)
    decisions = decide_merge_with_diff(b, l, r, ld, rd)

    # Assert that generic merge gives conflict
    assert apply_decisions(b, decisions) == b
    assert len(decisions) == 1
    assert decisions[0].conflict

    # Without strategy, no progress is made:
    resolved = autoresolve(b, decisions, Strategies())
    assert resolved == decisions

    # Supply transient list to autoresolve, and check that transient is ignored
    strategies = Strategies(transients=['/*/a/transient'])
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == r
    assert not any(d.conflict for d in resolved)
def test_decide_merge_strategy_clear_all():
    base = {"foo": [1, 2]}
    local = {"foo": [1, 4, 2]}
    remote = {"foo": [1, 3, 2]}

    strategies = Strategies({"/foo": "clear-all"})
    decisions = decide_merge(base, local, remote, strategies)
    assert apply_decisions(base, decisions) == {"foo": []}

    base = {"foo": [1, 2]}
    local = {"foo": [1, 4, 2]}
    remote = {"foo": [1, 2, 3]}

    strategies = Strategies({"/foo": "clear-all"})
    decisions = decide_merge(base, local, remote, strategies)
    assert apply_decisions(base, decisions) == {"foo": [1, 4, 2, 3]}
def test_decide_merge_strategy_remove():
    base = {"foo": [1, 2]}
    local = {"foo": [1, 4, 2]}
    remote = {"foo": [1, 3, 2]}

    strategies = Strategies({"/foo": "remove"})
    decisions = decide_merge(base, local, remote, strategies)
    assert apply_decisions(base, decisions) == {"foo": [1, 2]}
    assert decisions[0].local_diff != []
    assert decisions[0].remote_diff != []

    strategies = Strategies({})
    decisions = decide_merge(base, local, remote, strategies)
    assert apply_decisions(base, decisions) == {"foo": [1, 2]}
    assert decisions[0].local_diff != []
    assert decisions[0].remote_diff != []
Exemple #8
0
def test_autoresolve_dict_fail():
    """Check that "fail" strategy results in proper exception raised."""
    strategies = Strategies({"/foo": "fail"})
    with pytest.raises(RuntimeError):
        autoresolve(base, conflicted_decisions, strategies)

    base2 = {"foo": {"bar": 1}}
    local2 = {"foo": {"bar": 2}}
    remote2 = {"foo": {"bar": 3}}
    strategies = Strategies({"/foo/bar": "fail"})
    decisions = decide_merge(base2, local2, remote2)
    with pytest.raises(RuntimeError):
        autoresolve(base2, decisions, strategies)
    strategies = Strategies({"/foo": "fail"})
    with pytest.raises(RuntimeError):
        autoresolve(base2, decisions, strategies)
def test_decide_merge_strategy_fail(reset_log):
    """Check that "fail" strategy results in proper exception raised."""
    # One level dict
    base = {"foo": 1}
    local = {"foo": 2}
    remote = {"foo": 3}
    strategies = Strategies({"/foo": "fail"})
    with pytest.raises(RuntimeError):
        conflicted_decisions = decide_merge(base, local, remote, strategies)

    # Nested dicts
    base = {"foo": {"bar": 1}}
    local = {"foo": {"bar": 2}}
    remote = {"foo": {"bar": 3}}
    strategies = Strategies({"/foo/bar": "fail"})
    with pytest.raises(RuntimeError):
        decisions = decide_merge(base, local, remote, strategies)
def test_decide_merge_dict_transients():
    # Setup transient difference in base and local, deletion in remote
    b = {'a': {'transient': 22}}
    l = {'a': {'transient': 242}}
    r = {}

    # Assert that generic merge gives conflict
    strategies = Strategies()
    decisions = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, decisions) == b
    assert len(decisions) == 1
    assert decisions[0].conflict

    # Supply transient list to autoresolve, and check that transient is ignored
    strategies = Strategies(transients=['/a/transient'])
    decisions = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, decisions) == r
    assert not any(d.conflict for d in decisions)
def test_decide_merge_strategy_clear2():
    base = {"foo": "1"}
    local = {"foo": "2"}
    remote = {"foo": "3"}
    strategies = Strategies({"/foo": "clear"})
    decisions = decide_merge(base, local, remote, strategies)
    #assert decisions == []
    assert apply_decisions(base, decisions) == {"foo": ""}
    assert not any([d.conflict for d in decisions])
def test_decide_merge_simple_list_insert_conflict_resolution__union():
    # local and remote adds an entry each
    b = [1]
    l = [1, 2]
    r = [1, 3]

    strategies = Strategies({"/": "union"})
    decisions = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, decisions) == [1, 2, 3]
    assert not any(d.conflict for d in decisions)
Exemple #13
0
def test_autoresolve_dict_clear():
    """Check strategy "clear" in various cases."""

    base2 = {"foo": [1, 2]}
    local2 = {"foo": [1, 4, 2]}
    remote2 = {"foo": [1, 3, 2]}
    decisions = decide_merge(base2, local2, remote2)
    assert apply_decisions(base2, decisions) == {"foo": [1, 2]}
    assert decisions[0].local_diff != []
    assert decisions[0].remote_diff != []
    strategies = Strategies({"/foo": "clear-parent"})
    resolved = autoresolve(base2, decisions, strategies)
    assert apply_decisions(base2, resolved) == {"foo": []}
    assert not any([d.conflict for d in resolved])

    strategies = Strategies({"/foo": "clear"})
    resolved = autoresolve(base2, decisions, strategies)
    assert apply_decisions(base2, resolved) == {"foo": [1, None]}
    assert not any([d.conflict for d in resolved])
def test_decide_merge_list_conflicting_insertions_in_chunks__union():
    # Next, test when insertions DO chunk together:
    b = [1, 9]
    l = [1, 2, 7, 9]
    r = [1, 3, 7, 9]

    strategies = Strategies({"/": "union"})
    resolved = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, resolved) == [1, 2, 3, 7, 9]
    assert not any(d.conflict for d in resolved)
Exemple #15
0
def merge_strategies(args):
    strategies = Strategies({
        # Pick highest minor format:
        "/version": "take-max"
    })

    ignore_transients = args.ignore_transients if args else True
    if ignore_transients:
        strategies.transients = []
        strategies.update({})

    # Get args, default to inline for cli tool, intended to produce
    # an editable notebook that can be manually edited
    merge_strategy = args.merge_strategy if args else None

    # Set root strategy
    strategies["/"] = merge_strategy

    return strategies
def test_decide_merge_strategy_clear1():
    """Check strategy "clear" in various cases."""
    # One level dict, clearing item value (think foo==execution_count)
    base = {"foo": 1}
    local = {"foo": 2}
    remote = {"foo": 3}
    strategies = Strategies({"/foo": "clear"})
    decisions = decide_merge(base, local, remote, strategies)
    assert apply_decisions(base, decisions) == {"foo": None}
    assert not any([d.conflict for d in decisions])
def test_decide_merge_list_conflicting_insertions_separate_chunks__union():
    # local and remote adds an equal entry plus a different entry each
    # First, test when insertions DO NOT chunk together:
    b = [1, 9]
    l = [1, 2, 9, 11]
    r = [1, 3, 9, 11]

    strategies = Strategies({"/": "union"})
    resolved = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, resolved) == [1, 2, 3, 9, 11]
    assert not any(d.conflict for d in resolved)
Exemple #18
0
def test_autoresolve_dict_use_one_side():
    strategies = Strategies({"/foo": "use-base"})
    decisions = autoresolve(base, conflicted_decisions, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base, decisions) == {"foo": 1}

    strategies = Strategies({"/foo": "use-local"})
    decisions = autoresolve(base, conflicted_decisions, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base, decisions) == {"foo": 2}

    strategies = Strategies({"/foo": "use-remote"})
    decisions = autoresolve(base, conflicted_decisions, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base, decisions) == {"foo": 3}

    base2 = {"foo": {"bar": 1}}
    local2 = {"foo": {"bar": 2}}
    remote2 = {"foo": {"bar": 3}}
    conflicted_decisions2 = decide_merge(base2, local2, remote2)

    strategies = Strategies({"/foo/bar": "use-base"})
    decisions = autoresolve(base2, conflicted_decisions2, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base2, decisions) == {"foo": {"bar": 1}}

    strategies = Strategies({"/foo/bar": "use-local"})
    decisions = autoresolve(base2, conflicted_decisions2, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base2, decisions) == {"foo": {"bar": 2}}

    strategies = Strategies({"/foo/bar": "use-remote"})
    decisions = autoresolve(base2, conflicted_decisions2, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base2, decisions) == {"foo": {"bar": 3}}
def test_decide_merge_list_conflicting_insertions_separate_chunks_v2():
    # local and remote adds an equal entry plus a different entry each
    # First, test when insertions DO NOT chunk together:
    b = [1, 9]
    l = [1, 2, 9, 11]
    r = [1, 3, 9, 11]

    # Check strategyless resolution
    strategies = Strategies({})
    resolved = decide_merge(b, l, r, strategies)
    expected_partial = [1, 9, 11]
    assert apply_decisions(b, resolved) == expected_partial
    assert len(resolved) == 2
    assert resolved[0].conflict
    assert not resolved[1].conflict
def test_decide_merge_list_conflicting_insertions_separate_chunks_v1():
    # local and remote adds an equal entry plus a different entry each
    # First, test when insertions DO NOT chunk together:
    b = [1, 9]
    l = [1, 2, 9, 11]
    r = [1, 3, 9, 11]

    # Check strategyless resolution
    strategies = Strategies({})
    resolved = decide_merge(b, l, r, strategies)
    expected_partial = [1, 9, 11]
    assert apply_decisions(b, resolved) == expected_partial
    assert len(resolved) == 2
    assert resolved[0].conflict
    assert not resolved[1].conflict

    strategies = Strategies({"/*": "use-local"})
    resolved = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, resolved) == l
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-remote"})
    resolved = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, resolved) == r
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-base"})
    resolved = decide_merge(b, l, r, strategies)
    # Strategy is only applied to conflicted decisions:
    assert apply_decisions(b, resolved) == expected_partial
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/": "clear-all"})
    resolved = decide_merge(b, l, r, strategies)
    assert apply_decisions(b, resolved) == []
    assert not any(d.conflict for d in resolved)
def test_decide_merge_strategy_use_foo_on_dict_items():
    base = {"foo": 1}
    local = {"foo": 2}
    remote = {"foo": 3}

    strategies = Strategies({"/foo": "use-base"})
    decisions = decide_merge(base, local, remote, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base, decisions) == {"foo": 1}

    strategies = Strategies({"/foo": "use-local"})
    decisions = decide_merge(base, local, remote, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base, decisions) == {"foo": 2}

    strategies = Strategies({"/foo": "use-remote"})
    decisions = decide_merge(base, local, remote, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base, decisions) == {"foo": 3}

    base = {"foo": {"bar": 1}}
    local = {"foo": {"bar": 2}}
    remote = {"foo": {"bar": 3}}

    strategies = Strategies({"/foo/bar": "use-base"})
    decisions = decide_merge(base, local, remote, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base, decisions) == {"foo": {"bar": 1}}

    strategies = Strategies({"/foo/bar": "use-local"})
    decisions = decide_merge(base, local, remote, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base, decisions) == {"foo": {"bar": 2}}

    strategies = Strategies({"/foo/bar": "use-remote"})
    decisions = decide_merge(base, local, remote, strategies)
    assert not any([d.conflict for d in decisions])
    assert apply_decisions(base, decisions) == {"foo": {"bar": 3}}
Exemple #22
0
def test_autoresolve_list_conflicting_insertions_mixed():
    # local and remote adds an equal entry plus a different entry each
    # First, test when insertions DO NOT chunk together:
    b = [1, 9]
    l = [1, 2, 9, 11]
    r = [1, 3, 9, 11]
    decisions = decide_merge(b, l, r)

    # Check strategyless resolution
    strategies = Strategies({})
    resolved = autoresolve(b, decisions, strategies)
    expected_partial = [1, 9, 11]
    assert apply_decisions(b, resolved) == expected_partial
    assert len(resolved) == 2
    assert resolved[0].conflict
    assert not resolved[1].conflict

    strategies = Strategies({"/*": "use-local"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == l
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-remote"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == r
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-base"})
    resolved = autoresolve(b, decisions, strategies)
    # Strategy is only applied to conflicted decisions:
    assert apply_decisions(b, resolved) == expected_partial
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "union"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == [1, 2, 3, 9, 11]
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "clear-parent"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == []
    assert not any(d.conflict for d in resolved)

    # Next, test when insertions DO chunk together:
    b = [1, 9]
    l = [1, 2, 7, 9]
    r = [1, 3, 7, 9]
    decisions = decide_merge(b, l, r)

    # Check strategyless resolution
    strategies = Strategies({})
    resolved = autoresolve(b, decisions, strategies)
    expected_partial = [1, 7, 9]
    assert apply_decisions(b, resolved) == expected_partial
    assert resolved == decisions  # Not able to resolve anything

    strategies = Strategies({"/*": "use-local"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == l
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-remote"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == r
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "use-base"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == expected_partial
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "union"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == [1, 2, 3, 7, 9]
    assert not any(d.conflict for d in resolved)

    strategies = Strategies({"/*": "clear-parent"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == []
    assert not any(d.conflict for d in resolved)