Пример #1
0
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)
Пример #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-all"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == []
    assert not any(d.conflict for d in resolved)
Пример #3
0
def test_autoresolve_dict_transients():
    # 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)
Пример #4
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-all"})
    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])
Пример #5
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}}
Пример #6
0
def test_autoresolve_dict_transients():
    # 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)
Пример #7
0
def test_autoresolve_empty_strategies():
    """Check that a autoresolve works with empty strategies"""
    expected_partial_source = [["base\n", "some other\n", "lines\n", "to align\n"]]
    expected_partial_metadata = [{'myval': 'base'}]
    expected_partial_outputs = [["base\nsome other\nlines\nto align\n", "output2", "output3"]]
    base, local, remote, expected_partial = _make_notebook_with_multi_conflicts(
        expected_partial_source, expected_partial_metadata, expected_partial_outputs
    )

    expected_conflicts = [
        {
            'action': 'base',
            'common_path': ('cells', 0, 'source'),
            'conflict': True,
            'local_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['local\n']},
                           {'key': 0, 'length': 1, 'op': 'removerange'}],
            'remote_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['remote\n']},
                            {'key': 0, 'length': 1, 'op': 'removerange'}]
        },
        {
            'action': 'base',
            'common_path': ('cells', 0, 'outputs', 0, 'data', 'text/plain'),
            'conflict': True,
            'local_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['local\n']},
                           {'key': 0, 'length': 1, 'op': 'removerange'}],
            'remote_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['remote\n']},
                            {'key': 0, 'length': 1, 'op': 'removerange'}]
        },
        {
            'action': 'base',
            'common_path': ('cells', 0, 'metadata', 'myval'),
            'conflict': True,
            'local_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['local']},
                           {'key': 0, 'length': 1, 'op': 'removerange'}],
            'remote_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['remote']},
                            {'key': 0, 'length': 1, 'op': 'removerange'}]
        }
    ]

    # Since we cannot pass directly a strategies object, include copy of relevant code:
    local_diffs = diff_notebooks(base, local)
    remote_diffs = diff_notebooks(base, remote)
    decisions = decide_merge_with_diff(
        base, local, remote, local_diffs, remote_diffs)
    strategies = Strategies()
    decisions = autoresolve(base, decisions, strategies)
    partial = apply_decisions(base, decisions)

    _check(partial, expected_partial, decisions, expected_conflicts)
Пример #8
0
def test_autoresolve_empty_strategies():
    """Check that a autoresolve works with empty strategies"""
    expected_partial_source = [["base\n", "some other\n", "lines\n", "to align\n"]]
    expected_partial_metadata = [{'myval': 'base'}]
    expected_partial_outputs = [["base\nsome other\nlines\nto align\n", "output2", "output3"]]
    base, local, remote, expected_partial = _make_notebook_with_multi_conflicts(
        expected_partial_source, expected_partial_metadata, expected_partial_outputs
    )

    expected_conflicts = [
        {
            'action': 'base',
            'common_path': ('cells', 0, 'source'),
            'conflict': True,
            'local_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['local\n']},
                           {'key': 0, 'length': 1, 'op': 'removerange'}],
            'remote_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['remote\n']},
                            {'key': 0, 'length': 1, 'op': 'removerange'}]
        },
        {
            'action': 'base',
            'common_path': ('cells', 0, 'outputs', 0, 'data', 'text/plain'),
            'conflict': True,
            'local_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['local\n']},
                           {'key': 0, 'length': 1, 'op': 'removerange'}],
            'remote_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['remote\n']},
                            {'key': 0, 'length': 1, 'op': 'removerange'}]
        },
        {
            'action': 'base',
            'common_path': ('cells', 0, 'metadata', 'myval'),
            'conflict': True,
            'local_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['local']},
                           {'key': 0, 'length': 1, 'op': 'removerange'}],
            'remote_diff': [{'key': 0, 'op': 'addrange', 'valuelist': ['remote']},
                            {'key': 0, 'length': 1, 'op': 'removerange'}]
        }
    ]

    # Since we cannot pass directly a strategies object, include copy of relevant code:
    local_diffs = diff_notebooks(base, local)
    remote_diffs = diff_notebooks(base, remote)
    decisions = decide_merge_with_diff(
        base, local, remote, local_diffs, remote_diffs)
    strategies = Strategies()
    decisions = autoresolve(base, decisions, strategies)
    partial = apply_decisions(base, decisions)

    _check(partial, expected_partial, decisions, expected_conflicts)
Пример #9
0
def xtest_autoresolve_use_one():
    # These are reused in all tests below
    args = None
    base = { "foo": 1 }
    local = { "foo": 2 }
    remote = { "foo": 3 }

    strategies = { "/foo": "use-base" }
    merged, local_conflicts, remote_conflicts = autoresolve(base, diff(base, local), diff(base, remote), args, strategies, "")
    assert local_conflicts == []
    assert remote_conflicts == []
    assert merged == { "foo": 1 }

    strategies = { "/foo": "use-local" }
    merged, local_conflicts, remote_conflicts = autoresolve(base, diff(base, local), diff(base, remote), args, strategies, "")
    assert merged == { "foo": 2 }
    assert local_conflicts == []
    assert remote_conflicts == []

    strategies = { "/foo": "use-remote" }
    merged, local_conflicts, remote_conflicts = autoresolve(base, diff(base, local), diff(base, remote), args, strategies, "")
    assert merged == { "foo": 3 }
    assert local_conflicts == []
    assert remote_conflicts == []
Пример #10
0
def test_autoresolve_dict_fail():
    """Check that "fail" strategy results in proper exception raised."""
    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 = {"/foo/bar": "fail"}
    decisions = decide_merge(base2, local2, remote2)
    with pytest.raises(RuntimeError):
        autoresolve(base2, decisions, strategies)
    strategies = {"/foo": "fail"}
    with pytest.raises(RuntimeError):
        autoresolve(base2, decisions, strategies)
Пример #11
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)
Пример #12
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 = {}
    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 = {"/*": "use-local"}
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == l
    assert not any(d.conflict for d in resolved)

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

    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 = {"/*": "join"}
    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 = {"/*": "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 = {}
    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 = {"/*": "use-local"}
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == l
    assert not any(d.conflict for d in resolved)

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

    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 = {"/*": "join"}
    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 = {"/*": "clear-parent"}
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == []
    assert not any(d.conflict for d in resolved)
Пример #13
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-all"})
    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-all"})
    resolved = autoresolve(b, decisions, strategies)
    assert apply_decisions(b, resolved) == []
    assert not any(d.conflict for d in resolved)