mirror of
https://github.com/odoo/runbot.git
synced 2025-03-15 15:35:46 +07:00

- Apparently if a user is on windows the ACE editor can swap out their line end from unix to windows. The patch parsers were predicated upon all patches being in unix mode (because git, and diff). Fixup both parsers to convert windows-style line end to unix before trying to parse the patch data. Also add a few fallbacks to limit the odds of an unhelpful `StopIteration` (though that might hide errors more than reveal them...) - Make sure we support `format-patch --no-signature`, just requires using the correct partition direction: I assume I used `rpartition` as a form of micro-optimisation *but* - If the separator is not found the "patch body" ends up in the third parameter rather than the first, which makes the fallback difficult. - There doesn't seem to be anything preventing *multiple* signature separators in a message, and logically the first one should hold and the rest is all part of the signature. As a result, for both reasons we need to look *forwards* for the signature separator, not backwards. Hence `str.partition`. Fixes #992
271 lines
6.8 KiB
Python
271 lines
6.8 KiB
Python
import xmlrpc.client
|
|
|
|
import pytest
|
|
|
|
from utils import Commit, read_tracking_value, matches
|
|
|
|
# basic udiff / show style patch, updates `b` from `1` to `2`
|
|
BASIC_UDIFF = """\
|
|
commit 0000000000000000000000000000000000000000
|
|
Author: 3 Discos Down <bar@example.org>
|
|
Date: 2021-04-24T17:09:14Z
|
|
|
|
whop
|
|
|
|
whop whop
|
|
|
|
diff --git a/b b/b
|
|
index 000000000000..000000000000 100644
|
|
--- a/b
|
|
+++ b/b
|
|
@@ -1,1 +1,1 @@
|
|
-1
|
|
+2
|
|
"""
|
|
|
|
FORMAT_PATCH_XMO = """\
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: 3 Discos Down <bar@example.org>
|
|
Date: Sat, 24 Apr 2021 17:09:14 +0000
|
|
Subject: [PATCH] [I18N] whop
|
|
|
|
whop whop
|
|
---
|
|
b | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/b b/b
|
|
index 000000000000..000000000000 100644
|
|
--- a/b
|
|
+++ b/b
|
|
@@ -1,1 +1,1 @@
|
|
-1
|
|
+2
|
|
--
|
|
2.46.2
|
|
"""
|
|
|
|
# slightly different format than the one I got, possibly because older?
|
|
FORMAT_PATCH_MAT = """\
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: 3 Discos Down <bar@example.org>
|
|
Date: Sat, 24 Apr 2021 17:09:14 +0000
|
|
Subject: [PATCH 1/1] [I18N] whop
|
|
|
|
whop whop
|
|
---
|
|
b | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git b b
|
|
index 000000000000..000000000000 100644
|
|
--- b
|
|
+++ b
|
|
@@ -1,1 +1,1 @@
|
|
-1
|
|
+2
|
|
--
|
|
2.34.1
|
|
"""
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _setup(repo):
|
|
with repo:
|
|
[c, _] = repo.make_commits(
|
|
None,
|
|
Commit("a", tree={"a": "1", "b": "1\n"}),
|
|
Commit("b", tree={"a": "2"}),
|
|
ref="heads/master",
|
|
)
|
|
repo.make_ref("heads/x", c)
|
|
|
|
@pytest.mark.parametrize("group,access", [
|
|
('base.group_portal', False),
|
|
('base.group_user', False),
|
|
('runbot_merge.group_patcher', True),
|
|
('runbot_merge.group_admin', False),
|
|
('base.group_system', True),
|
|
])
|
|
def test_patch_acl(env, project, group, access):
|
|
g = env.ref(group)
|
|
assert g._name == 'res.groups'
|
|
env['res.users'].create({
|
|
'name': 'xxx',
|
|
'login': 'xxx',
|
|
'password': 'xxx',
|
|
'groups_id': [(6, 0, [g.id])],
|
|
})
|
|
env2 = env.with_user('xxx', 'xxx')
|
|
def create():
|
|
return env2['runbot_merge.patch'].create({
|
|
'target': project.branch_ids.id,
|
|
'repository': project.repo_ids.id,
|
|
'patch': BASIC_UDIFF,
|
|
})
|
|
if access:
|
|
create()
|
|
else:
|
|
pytest.raises(xmlrpc.client.Fault, create)\
|
|
.match("You are not allowed to create")
|
|
|
|
def test_apply_commit(env, project, repo, users):
|
|
with repo:
|
|
[c] = repo.make_commits("x", Commit("c", tree={"b": "2"}, author={
|
|
'name': "Henry Hoover",
|
|
"email": "dustsuckinghose@example.org",
|
|
}), ref="heads/abranch")
|
|
repo.delete_ref('heads/abranch')
|
|
|
|
p = env['runbot_merge.patch'].create({
|
|
'target': project.branch_ids.id,
|
|
'repository': project.repo_ids.id,
|
|
'commit': c,
|
|
})
|
|
|
|
env.run_crons()
|
|
|
|
HEAD = repo.commit('master')
|
|
assert repo.read_tree(HEAD) == {
|
|
'a': '2',
|
|
'b': '2',
|
|
}
|
|
assert HEAD.message == "c"
|
|
assert HEAD.author['name'] == "Henry Hoover"
|
|
assert HEAD.author['email'] == "dustsuckinghose@example.org"
|
|
assert not p.active
|
|
|
|
def test_commit_conflict(env, project, repo, users):
|
|
with repo:
|
|
[c] = repo.make_commits("x", Commit("x", tree={"b": "3"}))
|
|
repo.make_commits("master", Commit("c", tree={"b": "2"}), ref="heads/master", make=False)
|
|
|
|
p = env['runbot_merge.patch'].create({
|
|
'target': project.branch_ids.id,
|
|
'repository': project.repo_ids.id,
|
|
'commit': c,
|
|
})
|
|
|
|
env.run_crons()
|
|
|
|
HEAD = repo.commit('master')
|
|
assert repo.read_tree(HEAD) == {
|
|
'a': '2',
|
|
'b': '2',
|
|
}
|
|
assert not p.active
|
|
assert [(
|
|
m.subject,
|
|
m.body,
|
|
list(map(read_tracking_value, m.tracking_value_ids)),
|
|
)
|
|
for m in reversed(p.message_ids)
|
|
] == [
|
|
(False, '<p>Unstaged direct-application patch created</p>', []),
|
|
(
|
|
"Unable to apply patch",
|
|
"""\
|
|
<p>Auto-merging b<br>\
|
|
CONFLICT (content): Merge conflict in b<br></p>\
|
|
""",
|
|
[],
|
|
),
|
|
(False, '', [('active', 1, 0)]),
|
|
]
|
|
|
|
def test_apply_udiff(env, project, repo, users):
|
|
p = env['runbot_merge.patch'].create({
|
|
'target': project.branch_ids.id,
|
|
'repository': project.repo_ids.id,
|
|
'patch': BASIC_UDIFF,
|
|
})
|
|
|
|
env.run_crons()
|
|
|
|
HEAD = repo.commit('master')
|
|
assert repo.read_tree(HEAD) == {
|
|
'a': '2',
|
|
'b': '2\n',
|
|
}
|
|
assert HEAD.message == "whop\n\nwhop whop"
|
|
assert HEAD.author['name'] == "3 Discos Down"
|
|
assert HEAD.author['email'] == "bar@example.org"
|
|
assert not p.active
|
|
|
|
|
|
@pytest.mark.parametrize('patch', [
|
|
pytest.param(FORMAT_PATCH_XMO, id='xmo'),
|
|
pytest.param(FORMAT_PATCH_MAT, id='mat'),
|
|
pytest.param(
|
|
FORMAT_PATCH_XMO.replace('\n', '\r\n'),
|
|
id='windows',
|
|
),
|
|
pytest.param(
|
|
FORMAT_PATCH_XMO.rsplit('-- \n')[0],
|
|
id='no-signature',
|
|
)
|
|
])
|
|
def test_apply_format_patch(env, project, repo, users, patch):
|
|
p = env['runbot_merge.patch'].create({
|
|
'target': project.branch_ids.id,
|
|
'repository': project.repo_ids.id,
|
|
'patch': patch,
|
|
})
|
|
|
|
env.run_crons()
|
|
|
|
bot = env['res.users'].browse((1,))
|
|
assert p.message_ids[::-1].mapped(lambda m: (
|
|
m.author_id.display_name,
|
|
m.body,
|
|
list(map(read_tracking_value, m.tracking_value_ids)),
|
|
)) == [
|
|
(p.create_uid.partner_id.display_name, '<p>Unstaged direct-application patch created</p>', []),
|
|
(bot.partner_id.display_name, "", [('active', 1, 0)]),
|
|
]
|
|
HEAD = repo.commit('master')
|
|
assert repo.read_tree(HEAD) == {
|
|
'a': '2',
|
|
'b': '2\n',
|
|
}
|
|
assert HEAD.message == "[I18N] whop\n\nwhop whop"
|
|
assert HEAD.author['name'] == "3 Discos Down"
|
|
assert HEAD.author['email'] == "bar@example.org"
|
|
assert not p.active
|
|
|
|
def test_patch_conflict(env, project, repo, users):
|
|
p = env['runbot_merge.patch'].create({
|
|
'target': project.branch_ids.id,
|
|
'repository': project.repo_ids.id,
|
|
'patch': BASIC_UDIFF,
|
|
})
|
|
with repo:
|
|
repo.make_commits('master', Commit('cccombo breaker', tree={'b': '3'}), ref='heads/master', make=False)
|
|
|
|
env.run_crons()
|
|
|
|
HEAD = repo.commit('master')
|
|
assert HEAD.message == 'cccombo breaker'
|
|
assert repo.read_tree(HEAD) == {
|
|
'a': '2',
|
|
'b': '3',
|
|
}
|
|
assert not p.active
|
|
assert [(
|
|
m.subject,
|
|
m.body,
|
|
list(map(read_tracking_value, m.tracking_value_ids)),
|
|
)
|
|
for m in reversed(p.message_ids)
|
|
] == [(
|
|
False,
|
|
'<p>Unstaged direct-application patch created</p>',
|
|
[],
|
|
), (
|
|
"Unable to apply patch",
|
|
matches("$$"), # feedback from patch can vary
|
|
[],
|
|
), (
|
|
False, '', [('active', 1, 0)]
|
|
)]
|