New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
unify: unified_patches order fix #71
Conversation
cc @mvesper |
if conflict: | ||
if not conflict.handled: | ||
conflict.handled = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this check since the same conflict can be checked twice:
- on its
first_patch
- on its
second_patch
Since we check below that the patch we are trying to apply is either first_patch
or second_patch
, given the conflict resolution, the second time the conflict is brought up from self._index
it would always be different than the patch we are checking against.
patches = [fp1, sp1, sp2]
conflicts = [(fp1,sp1), (fp1,sp2)]
_index = {
fp1: (fp1, sp2)
sp1: (fp1, sp1),
sp2: (fp1, sp2),
}
# Try to take first:
# fp1 will point to (fp1, sp2) and its resolution is fp1 so we append.
# sp1 will point to (fp1, sp1) but the conflict resolves to fp1.
# sp2 will point to (fp1, sp2) but the conflict resolves to fp1.
# -- We will never find fp1 in the patch list.
# -- Every conflict that resolves to fp1 will never be matched with anything else in the list.
unified = [fp1]
# Try to take second:
# fp1 will point to (fp1, sp2) but the conflict resolves to sp2, so we skip it.
# sp1 will point to (fp1, sp1) and its resolution is sp1 so we append.
# sp2 will point to (fp1, sp2) and its resolution is sp2 so we append.
unified = [sp1, sp2]
Rough demonstration of algo:
- We always append in the correct order
- For any patch
p
in sorted patches- It never appeared before in
sorted_patches
- It will never appear afterwards in
sorted_patches
- If it points to a conflict:
- Conflicts resolves to
p
and from 1st and 2nd bullet no otherp1
exists insorted_patches
so thatp == p1
. For appending a patchpatch.take_patch()
should be equal top
. Therefore we won't find another patch that satisfies this condition so there's no need to check about handling a conflict twice. - Conflict does not resolve to
p
but top1
-> we will appendp1
when it occurs in the list (if we didn't already)
- Conflicts resolves to
- It never appeared before in
@@ -43,6 +44,41 @@ def take_first(conflict, _, __, ___): | |||
self.assertEqual(m.unified_patches, | |||
[('change', 'changeme', ('Jo', 'Joe'))]) | |||
|
|||
def test_continue_run_multiple_conflicts_per_patch(self): | |||
lca = {'foo': [{'x': [1, 2, 3]}, {'y': [2, 3, 4]}]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you try to make simpler test data and remove duplicated code from two tests?
def test_continue_run_multiple_conflicts_per_path(self):
lca = {'first': [1, 2]}
first = {'first': [1, 2, 3]}
second = {'second': 2}
expected = [
('f', {'first': [1, 2, 3], 'second': 2}),
('s', {'second': 2}),
]
for resolution, result in expected:
m = Merger(lca, first, second, {})
try:
m.run()
except UnresolvedConflictsException as e:
m.continue_run([resolution for _ in e.content])
self.assertEqual(patch(m.unified_patches, lca), result)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The simpler test case does not trigger the problem that I was fixing. We need nested objects for this test to fail before the fix, so I'll keep the large test fixtures.
Also, when regression testing the bug it made sense to have two separate tests for each case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, when regression testing the bug it made sense to have two separate tests for each case.
@mihaibivol since we are using pytest
, you can use @pytest.mark.parameterize
to keep the results of each run visible.
first = {'foo': [{'x': 1}, {'y': 2}, {'z': 4}]} | ||
second = {'bar': 'baz'} | ||
|
||
expected = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you use list of tuples then you don't need following dict access expected[resolution]
(moreover it's easier to use pytest.mark.parameterize
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tests inherits from unittest.TestCase
so that's why parametrize
does not work (SO).
I'll just change to items()
iteration and I'll leave it as is for the time being.
The follow-up solutions for this would either be:
- Require nose-parametrize for parametrizing
unitest.TestCase
test methods - Port everything to
pytest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should have say "easier to port to use pytest.mark.parameterize
.
@mvesper can you please have a look at these changes? @mihaibivol please keep the PR up-to-date with the master branch until it gets integrated. Thanks |
@jirikuncar Mihai and I discussed the issue when he found the bug. The changes and regression test seem reasonable. |
@mihaibivol @mvesper Thank you both! Last thing I will ask @mihaibivol to amend commit message by adding After you amend the commit message you can run |
I don't seem to make |
Can you be more descriptive so people can understand this change in RELEASE-NOTES?
|
* FIX Fixes order in which handled conflicts are unified so that the Merger's unified_patches can be always appplied. * Adds regression tests for the added fix. * Removes conflict.handled field. Signed-off-by: Mihai Bivol <miha.bivol@cern.ch>
New features
Improved features
Bug fixes
|
Signed-off-by: Mihai Bivol miha.bivol@cern.ch