Skip to content

Commit b8bf713

Browse files
committed
Bug 1554447 - ERR-CONDUIT-CORE: Error while reading "ids" when pushing a stack with duplicate revisions; r=zalun
* Rewrite get_revisions to avoid duplicated code and checks. * Don't query Phabricator with an empty list. * Update tests to pass in real data to get_revisions and clear revision mapping cache between tests. Differential Revision: https://phabricator.services.mozilla.com/D32663
1 parent 15d7192 commit b8bf713

2 files changed

Lines changed: 83 additions & 66 deletions

File tree

moz-phab

Lines changed: 45 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2878,88 +2878,72 @@ def self_update(args):
28782878
logger.info("%s updated" % SELF_FILE)
28792879

28802880

2881-
def order_revisions_by_id(phids_by_id, revs, ids):
2882-
return [revs[phids_by_id[str(id)]] for id in ids if phids_by_id[str(id)]]
2883-
2884-
2885-
def order_revisions_by_phid(revs, phids):
2886-
return [revs[phid] for phid in phids]
2887-
2888-
28892881
def get_revisions(cwd, ids=None, phids=None):
28902882
"""Get revisions info from Phabricator.
28912883
28922884
Args:
28932885
cwd - current working directory
2894-
ids - list of ids
2886+
ids - list of revision ids
28952887
phids - list of revision phids
28962888
28972889
Returns a list of revisions ordered by ids or phids
2898-
2899-
Raises Exception if both ids and phids are provided
29002890
"""
2901-
if ids and phids:
2902-
# Ordering would be too complicated
2903-
raise ValueError("ids and phids are mutually exclusive")
2891+
2892+
if (ids and phids) or (ids is None and phids is None):
2893+
raise ValueError("Internal Error: Invalid args to get_revisions")
29042894

29052895
if not ids and not phids:
29062896
return []
29072897

2908-
collected_ids = (
2909-
dict(
2898+
# Initialise depending on if we're passed revision IDs or PHIDs.
2899+
if ids:
2900+
ids = [str(rev_id) for rev_id in ids]
2901+
phids_by_id = dict(
29102902
[
2911-
(str(id), cache.get("rev-id-%s" % id))
2912-
for id in ids
2913-
if "rev-id-%s" % id in cache
2903+
(rev_id, cache.get("rev-id-%s" % rev_id))
2904+
for rev_id in ids
2905+
if "rev-id-%s" % rev_id in cache
29142906
]
29152907
)
2916-
if ids
2917-
else {}
2918-
)
2908+
found_phids = phids_by_id.values()
2909+
query_field = "ids"
2910+
query_values = [int(rev_id) for rev_id in set(ids) - set(phids_by_id.keys())]
29192911

2920-
found_phids = collected_ids.values() if ids else phids[:]
2921-
2922-
collected_revs = (
2923-
dict(
2924-
[
2925-
(phid, cache.get("rev-%s" % phid))
2926-
for phid in found_phids
2927-
if "rev-%s" % phid in cache
2928-
]
2929-
)
2930-
if found_phids
2931-
else {}
2912+
else:
2913+
phids_by_id = {}
2914+
found_phids = phids[:]
2915+
query_field = "phids"
2916+
query_values = list(set(phids) - set(phids_by_id.values()))
2917+
2918+
# Revisions metadata keyed by PHID.
2919+
revisions = dict(
2920+
[
2921+
(phid, cache.get("rev-%s" % phid))
2922+
for phid in found_phids
2923+
if "rev-%s" % phid in cache
2924+
]
29322925
)
29332926

2934-
if ids and len(ids) == len(collected_ids):
2935-
return order_revisions_by_id(collected_ids, collected_revs, ids)
2936-
2937-
if phids and len(phids) == len(collected_revs):
2938-
return order_revisions_by_phid(collected_revs, phids)
2939-
2940-
api_call_args = {"constraints": {}, "attachments": {"reviewers": True}}
2927+
# Query Phabricator if we don't have cached values for revisions.
2928+
if query_values:
2929+
api_call_args = {
2930+
"constraints": {query_field: query_values},
2931+
"attachments": {"reviewers": True},
2932+
}
2933+
response = arc_call_conduit("differential.revision.search", api_call_args, cwd)
2934+
rev_list = response.get("data")
2935+
2936+
for r in rev_list:
2937+
phids_by_id[str(r["id"])] = r["phid"]
2938+
revisions[r["phid"]] = r
2939+
cache.set("rev-id-%s" % r["id"], r["phid"])
2940+
cache.set("rev-%s" % r["phid"], r)
2941+
2942+
# Return revisions in the same order requested.
29412943
if ids:
2942-
to_collect = list(set(ids) - set([int(key) for key in collected_ids.keys()]))
2943-
api_call_args["constraints"]["ids"] = to_collect
2944+
return [revisions[phids_by_id[rev_id]] for rev_id in ids if phids_by_id[rev_id]]
29442945
else:
2945-
to_collect = list(set(phids) - set(collected_ids.values()))
2946-
api_call_args["constraints"]["phids"] = to_collect
2947-
2948-
response = arc_call_conduit("differential.revision.search", api_call_args, cwd)
2949-
rev_list = response.get("data")
2950-
if not rev_list:
2951-
return
2952-
2953-
for r in rev_list:
2954-
collected_ids[str(r["id"])] = r["phid"]
2955-
collected_revs[r["phid"]] = r
2956-
cache.set("rev-id-%s" % r["id"], r["phid"])
2957-
cache.set("rev-%s" % r["phid"], r)
2958-
2959-
if ids:
2960-
return order_revisions_by_id(collected_ids, collected_revs, ids)
2961-
2962-
return order_revisions_by_phid(collected_revs, phids)
2946+
return [revisions[phid] for phid in phids]
29632947

29642948

29652949
def get_diffs(cwd, phids):

tests/test_patch.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,58 @@ def test_prepare_body():
4242
@mock.patch("mozphab.arc_call_conduit")
4343
def test_get_revisions(m_arc):
4444
get_revs = mozphab.get_revisions
45+
46+
# sanity checks
47+
with pytest.raises(ValueError):
48+
get_revs("x", ids=[1], phids=["PHID-1"])
49+
with pytest.raises(ValueError):
50+
get_revs("x", ids=None)
4551
with pytest.raises(ValueError):
46-
get_revs("x", ids=[1], phids=[1])
52+
get_revs("x", phids=None)
4753

48-
m_arc.return_value = {}
49-
assert get_revs("x", ids=[1]) is None
54+
m_arc.return_value = {"data": [dict(id=1, phid="PHID-1")]}
55+
56+
# differential.revision.search by revision-id
57+
assert len(get_revs("x", ids=[1])) == 1
5058
m_arc.assert_called_with(
5159
"differential.revision.search",
5260
dict(constraints=dict(ids=[1]), attachments=dict(reviewers=True)),
5361
"x",
5462
)
63+
64+
# differential.revision.search by phid
65+
m_arc.reset_mock()
66+
mozphab.cache.reset()
67+
assert len(get_revs("x", phids=["PHID-1"])) == 1
68+
m_arc.assert_called_with(
69+
"differential.revision.search",
70+
dict(constraints=dict(phids=["PHID-1"]), attachments=dict(reviewers=True)),
71+
"x",
72+
)
73+
74+
# differential.revision.search by revision-id with duplicates
75+
m_arc.reset_mock()
76+
mozphab.cache.reset()
77+
assert len(get_revs("x", ids=[1, 1])) == 2
78+
m_arc.assert_called_with(
79+
"differential.revision.search",
80+
dict(constraints=dict(ids=[1]), attachments=dict(reviewers=True)),
81+
"x",
82+
)
83+
84+
# differential.revision.search by phid with duplicates
5585
m_arc.reset_mock()
56-
assert get_revs("x", phids=[1]) is None
86+
mozphab.cache.reset()
87+
assert len(get_revs("x", phids=["PHID-1", "PHID-1"])) == 2
5788
m_arc.assert_called_with(
5889
"differential.revision.search",
59-
dict(constraints=dict(phids=[1]), attachments=dict(reviewers=True)),
90+
dict(constraints=dict(phids=["PHID-1"]), attachments=dict(reviewers=True)),
6091
"x",
6192
)
6293

94+
# ordering of results must match input
6395
m_arc.reset_mock()
96+
mozphab.cache.reset()
6497
m_arc.return_value = {
6598
"data": [
6699
dict(id=1, phid="PHID-1"),

0 commit comments

Comments
 (0)