1
# Copyright 2010 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Tests for automatic landing thing."""
10
from launchpadlib.uris import (
11
DEV_SERVICE_ROOT, EDGE_SERVICE_ROOT, LPNET_SERVICE_ROOT,
14
from bzrlib.plugins.tarmac_land.tarmac_land import (
15
get_bugs_clause, get_reviewer_clause,
16
get_reviewer_handle, get_testfix_clause, get_qa_clause,
17
MissingReviewError, MissingBugsError, MissingBugsIncrementalError,
20
from fakemethod import FakeMethod
24
"""Fake launchpadlib Bug object.
26
Only used for the purposes of testing.
29
def __init__(self, id):
34
"""Fake launchpadlib Person object.
36
Only used for the purposes of testing.
39
def __init__(self, name, irc_handles):
41
self.irc_nicknames = list(irc_handles)
47
Only used for the purposes of testing.
50
def __init__(self, nickname, network):
51
self.nickname = nickname
52
self.network = network
55
class FakeLPMergeProposal:
56
"""Fake launchpadlib MergeProposal object.
58
Only used for the purposes of testing.
61
def __init__(self, root=None):
63
self.commit_message = None
64
self.queue_status = "Needs review"
66
def setStatus(self, status):
67
self.queue_status = status
73
class TestBugsClaused(unittest.TestCase):
74
"""Tests for `get_bugs_clause`."""
76
def test_no_bugs(self):
77
# If there are no bugs, then there is no bugs clause.
78
bugs_clause = get_bugs_clause([])
79
self.assertEqual('', bugs_clause)
81
def test_one_bug(self):
82
# If there's a bug, then the bugs clause is [bug=$ID].
84
bugs_clause = get_bugs_clause([bug])
85
self.assertEqual('[bug=45]', bugs_clause)
87
def test_two_bugs(self):
88
# If there are two bugs, then the bugs clause is [bug=$ID,$ID].
91
bugs_clause = get_bugs_clause([bug1, bug2])
92
self.assertEqual('[bug=20,45]', bugs_clause)
95
class TestSetStatusApproved(unittest.TestCase):
98
self.mp = MergeProposal(FakeLPMergeProposal())
99
self.mp._mp.setStatus(status="Work in progress")
101
def test_set_status_approved(self):
102
self.mp.set_status_approved()
103
self.assertEqual(self.mp._mp.queue_status, "Approved")
106
class TestGetTestfixClause(unittest.TestCase):
107
"""Tests for `get_testfix_clause`"""
109
def test_no_testfix(self):
111
self.assertEqual('', get_testfix_clause(testfix))
113
def test_is_testfix(self):
115
self.assertEqual('[testfix]', get_testfix_clause(testfix))
118
class TestGetQaClause(unittest.TestCase):
119
"""Tests for `get_qa_clause`"""
121
def test_no_bugs_no_option_given(self):
125
self.assertRaises(MissingBugsError, get_qa_clause, bugs, no_qa,
128
def test_bugs_noqa_option_given(self):
132
self.assertEqual('[no-qa]',
133
get_qa_clause([bug1], no_qa, incr))
135
def test_no_bugs_noqa_option_given(self):
139
self.assertEqual('[no-qa]',
140
get_qa_clause(bugs, no_qa, incr))
142
def test_bugs_no_option_given(self):
147
get_qa_clause([bug1], no_qa, incr))
149
def test_bugs_incr_option_given(self):
153
self.assertEqual('[incr]',
154
get_qa_clause([bug1], no_qa, incr))
156
def test_no_bugs_incr_option_given(self):
160
self.assertRaises(MissingBugsIncrementalError,
161
get_qa_clause, bugs, no_qa, incr)
163
def test_bugs_incr_and_noqa_option_given(self):
167
self.assertEqual('[no-qa][incr]',
168
get_qa_clause([bug1], no_qa, incr))
170
def test_rollback_given(self):
172
self.assertEqual('[rollback=123]',
173
get_qa_clause(bugs, rollback=123))
175
def test_rollback_and_noqa_and_incr_given(self):
179
self.assertEqual('[rollback=123]',
180
get_qa_clause(bugs, rollback=123))
183
class TestGetReviewerHandle(unittest.TestCase):
184
"""Tests for `get_reviewer_handle`."""
186
def makePerson(self, name, irc_handles):
187
return FakePerson(name, irc_handles)
189
def test_no_irc_nicknames(self):
190
# If the person has no IRC nicknames, their reviewer handle is their
191
# Launchpad user name.
192
person = self.makePerson(name='foo', irc_handles=[])
193
self.assertEqual('foo', get_reviewer_handle(person))
195
def test_freenode_irc_nick_preferred(self):
196
# If the person has a Freenode IRC nickname, then that is preferred as
198
person = self.makePerson(
199
name='foo', irc_handles=[FakeIRC('bar', 'irc.freenode.net')])
200
self.assertEqual('bar', get_reviewer_handle(person))
202
def test_non_freenode_nicks_ignored(self):
203
# If the person has IRC nicks that aren't freenode, we ignore them.
204
person = self.makePerson(
205
name='foo', irc_handles=[FakeIRC('bar', 'irc.efnet.net')])
206
self.assertEqual('foo', get_reviewer_handle(person))
209
class TestGetCommitMessage(unittest.TestCase):
212
self.mp = MergeProposal(FakeLPMergeProposal())
213
self.fake_bug = FakeBug(20)
214
self.fake_person = self.makePerson('foo')
216
def makePerson(self, name):
217
return FakePerson(name, [])
219
def test_commit_with_bugs(self):
224
self.mp.get_bugs = FakeMethod([self.fake_bug])
225
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
227
self.assertEqual("[r=foo][ui=none][bug=20] Foobaring the sbrubble.",
228
self.mp.build_commit_message("Foobaring the sbrubble.",
229
testfix, no_qa, incr))
231
def test_commit_no_bugs_no_noqa(self):
236
self.mp.get_bugs = FakeMethod([])
237
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
239
self.assertRaises(MissingBugsError, self.mp.build_commit_message,
240
testfix, no_qa, incr)
242
def test_commit_no_bugs_with_noqa(self):
247
self.mp.get_bugs = FakeMethod([])
248
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
250
self.assertEqual("[r=foo][ui=none][no-qa] Foobaring the sbrubble.",
251
self.mp.build_commit_message("Foobaring the sbrubble.",
252
testfix, no_qa, incr))
254
def test_commit_bugs_with_noqa(self):
259
self.mp.get_bugs = FakeMethod([self.fake_bug])
260
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
263
"[r=foo][ui=none][bug=20][no-qa] Foobaring the sbrubble.",
264
self.mp.build_commit_message("Foobaring the sbrubble.",
265
testfix, no_qa, incr))
267
def test_commit_bugs_with_incr(self):
272
self.mp.get_bugs = FakeMethod([self.fake_bug])
273
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
276
"[r=foo][ui=none][bug=20][incr] Foobaring the sbrubble.",
277
self.mp.build_commit_message("Foobaring the sbrubble.",
278
testfix, no_qa, incr))
280
def test_commit_no_bugs_with_incr(self):
285
self.mp.get_bugs = FakeMethod([self.fake_bug])
286
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
289
"[r=foo][ui=none][bug=20][incr] Foobaring the sbrubble.",
290
self.mp.build_commit_message("Foobaring the sbrubble.",
291
testfix, no_qa, incr))
293
def test_commit_with_noqa_and_incr(self):
298
self.mp.get_bugs = FakeMethod([self.fake_bug])
299
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
302
"[r=foo][ui=none][bug=20][no-qa][incr] Foobaring the sbrubble.",
303
self.mp.build_commit_message("Foobaring the sbrubble.",
304
testfix, no_qa, incr))
306
def test_commit_with_rollback(self):
307
self.mp.get_bugs = FakeMethod([self.fake_bug])
308
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
311
"[r=foo][ui=none][bug=20][rollback=123] Foobaring the sbrubble.",
312
self.mp.build_commit_message("Foobaring the sbrubble.",
315
def test_takes_into_account_existing_tags_on_commit_text(self):
316
self.mp.get_bugs = FakeMethod([self.fake_bug])
317
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
320
"[r=foo][ui=none][bug=20][rollback=123] Foobaring the sbrubble.",
321
self.mp.build_commit_message(
322
"[r=foo][ui=none][bug=20][rollback=123] Foobaring the sbrubble.",
326
class TestSetCommitMessage(unittest.TestCase):
329
self.mp = MergeProposal(FakeLPMergeProposal())
331
def test_set_commit_message(self):
332
commit_message = "Foobaring the sbrubble."
333
self.mp.set_commit_message(commit_message)
334
self.assertEqual(self.mp._mp.commit_message, commit_message)
337
class TestGetReviewerClause(unittest.TestCase):
338
"""Tests for `get_reviewer_clause`."""
340
def makePerson(self, name):
341
return FakePerson(name, [])
343
def get_reviewer_clause(self, reviewers):
344
return get_reviewer_clause(reviewers)
346
def test_one_reviewer_no_type(self):
347
# It's very common for a merge proposal to be reviewed by one person
348
# with no specified type of review. It such cases the review clause is
349
# '[r=<person>][ui=none]'.
350
clause = self.get_reviewer_clause({None: [self.makePerson('foo')]})
351
self.assertEqual('[r=foo][ui=none]', clause)
353
def test_two_reviewers_no_type(self):
354
# Branches can have more than one reviewer.
355
clause = self.get_reviewer_clause(
356
{None: [self.makePerson('foo'), self.makePerson('bar')]})
357
self.assertEqual('[r=bar,foo][ui=none]', clause)
359
def test_mentat_reviewers(self):
360
# A mentat review sometimes is marked like 'ui*'. Due to the
361
# unordered nature of dictionaries, the reviewers are sorted before
362
# being put into the clause for predictability.
363
clause = self.get_reviewer_clause(
364
{None: [self.makePerson('foo')],
365
'code*': [self.makePerson('newguy')],
366
'ui': [self.makePerson('beuno')],
367
'ui*': [self.makePerson('bac')]})
368
self.assertEqual('[r=foo,newguy][ui=bac,beuno]', clause)
370
def test_code_reviewer_counts(self):
371
# Some people explicitly specify the 'code' type when they do code
372
# reviews, these are treated in the same way as reviewers without any
374
clause = self.get_reviewer_clause({'code': [self.makePerson('foo')]})
375
self.assertEqual('[r=foo][ui=none]', clause)
377
def test_release_critical(self):
378
# Reviews that are marked as release-critical are included in a
380
clause = self.get_reviewer_clause(
381
{'code': [self.makePerson('foo')],
382
'release-critical': [self.makePerson('bar')]})
383
self.assertEqual('[release-critical=bar][r=foo][ui=none]', clause)
385
def test_db_reviewer_counts(self):
386
# There's no special way of annotating database reviews in Launchpad
387
# commit messages, so they are included with the code reviews.
388
clause = self.get_reviewer_clause({'db': [self.makePerson('foo')]})
389
self.assertEqual('[r=foo][ui=none]', clause)
391
def test_ui_reviewers(self):
392
# If someone has done a UI review, then that appears in the clause
393
# separately from the code reviews.
394
clause = self.get_reviewer_clause(
395
{'code': [self.makePerson('foo')],
396
'ui': [self.makePerson('bar')],
398
self.assertEqual('[r=foo][ui=bar]', clause)
400
def test_no_reviewers(self):
401
# If the merge proposal hasn't been approved by anyone, we cannot
402
# generate a valid clause.
403
self.assertRaises(MissingReviewError, self.get_reviewer_clause, {})