92
149
Only used for the purposes of testing.
95
def __init__(self, root=None):
152
def __init__(self, root=None, queue_status='Approved', votes=None,
155
root = FakeLaunchpad()
97
157
self.source_branch = FakeBranch('lp_source')
98
158
self.target_branch = FakeBranch('lp_target')
99
159
self.commit_message = 'Message1'
100
self.votes = [FakeVote()]
160
if votes is not None:
163
self.votes = [FakeVote()]
164
self.queue_status = queue_status
165
self.reviewer = reviewer
171
class TestGetEmail(TestCase):
173
def test_get_email(self):
174
self.assertEqual('jrandom@example.org', get_email(FakePerson()))
176
def test_get_email_none(self):
177
self.assertIs(None, get_email(FakePerson(email=None)))
180
class TestPQMRegexAcceptance(unittest.TestCase):
181
"""Tests if the generated commit message is accepted by PQM regexes."""
184
# PQM regexes; might need update once in a while
185
self.devel_open_re = ("(?is)^\s*(:?\[testfix\])?\[(?:"
186
"release-critical=[^\]]+|rs?=[^\]]+)\]")
187
self.dbdevel_normal_re = ("(?is)^\s*(:?\[testfix\])?\[(?:"
188
"release-critical|rs?=[^\]]+)\]")
190
self.mp = MergeProposal(FakeLPMergeProposal())
191
self.fake_bug = FakeBug(20)
192
self.fake_person = FakePerson('foo', [])
193
self.mp.get_bugs = FakeMethod([self.fake_bug])
194
self.mp.get_reviews = FakeMethod({None : [self.fake_person]})
196
def assertRegexpMatches(self, text, expected_regexp, msg=None):
197
"""Fail the test unless the text matches the regular expression.
199
Method default in Python 2.7. Can be removed as soon as LP goes 2.7.
201
if isinstance(expected_regexp, basestring):
202
expected_regexp = re.compile(expected_regexp)
203
if not expected_regexp.search(text):
204
msg = msg or "Regexp didn't match"
205
msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern,
207
raise self.failureException(msg)
209
def _test_commit_message_match(self, incr, no_qa, testfix):
210
commit_message = self.mp.get_commit_message("Foobaring the sbrubble.",
211
testfix, no_qa, incr)
212
self.assertRegexpMatches(commit_message, self.devel_open_re)
213
self.assertRegexpMatches(commit_message, self.dbdevel_normal_re)
215
def test_testfix_match(self):
216
self._test_commit_message_match(incr=False, no_qa=False, testfix=True)
218
def test_regular_match(self):
219
self._test_commit_message_match(incr=False, no_qa=False, testfix=False)
221
def test_noqa_match(self):
222
self._test_commit_message_match(incr=False, no_qa=True, testfix=False)
224
def test_incr_match(self):
225
self._test_commit_message_match(incr=True, no_qa=False, testfix=False)
103
228
class TestBugsClaused(unittest.TestCase):
121
246
bugs_clause = get_bugs_clause([bug1, bug2])
122
247
self.assertEqual('[bug=20,45]', bugs_clause)
249
def test_fixed_bugs_are_excluded(self):
250
# If a bug is fixed then it is excluded from the bugs clause.
252
bug2 = FakeBug(45, bug_tasks=[
253
FakeBugTask('fake-project', 'Fix Released')])
254
bug3 = FakeBug(67, bug_tasks=[
255
FakeBugTask('fake-project', 'Fix Committed')])
256
bugs_clause = get_bugs_clause([bug1, bug2, bug3])
257
self.assertEqual('[bug=20]', bugs_clause)
259
def test_bugs_open_on_launchpad_are_included(self):
260
# If a bug has been fixed on one target but not in launchpad, then it
261
# is included in the bugs clause, because it's relevant to launchpad
263
bug = FakeBug(20, bug_tasks=[
264
FakeBugTask('fake-project', 'Fix Released'),
265
FakeBugTask('launchpad', 'Triaged')])
266
bugs_clause = get_bugs_clause([bug])
267
self.assertEqual('[bug=20]', bugs_clause)
269
def test_bugs_fixed_on_launchpad_but_open_in_others_are_excluded(self):
270
# If a bug has been fixed in Launchpad but not fixed on a different
271
# target, then it is excluded from the bugs clause, since we don't
273
bug = FakeBug(20, bug_tasks=[
274
FakeBugTask('fake-project', 'Triaged'),
275
FakeBugTask('launchpad', 'Fix Released')])
276
bugs_clause = get_bugs_clause([bug])
277
self.assertEqual('', bugs_clause)
125
280
class TestGetTestfixClause(unittest.TestCase):
126
281
"""Tests for `get_testfix_clause`"""
401
553
self.assertRaises(MissingReviewError, self.get_reviewer_clause, {})
556
class TestLaunchpadBranchLander(TestCase):
558
def get_lander(self, landing_targets=None):
559
branch = FakeBranch('public')
560
if landing_targets is not None:
561
branch.landing_targets = landing_targets
562
launchpad = FakeLaunchpad([branch])
563
return LaunchpadBranchLander(launchpad)
565
def test_get_merge_proposal_from_branch_no_proposals(self):
566
lander = self.get_lander()
567
branch = FakeBzrBranch()
568
e = self.assertRaises(errors.BzrCommandError,
569
lander.get_merge_proposal_from_branch, branch)
570
self.assertEqual('The public branch has no active source merge'
571
' proposals. You must have a merge proposal before'
572
' attempting to land the branch.', str(e))
574
def test_get_merge_proposal_one_proposal(self):
575
proposal = FakeLPMergeProposal()
576
lander = self.get_lander([proposal])
577
branch = FakeBzrBranch()
578
lander_proposal = lander.get_merge_proposal_from_branch(branch)
579
self.assertIs(proposal, lander_proposal._mp)
581
def test_get_merge_proposal_two_proposal(self):
582
lander = self.get_lander([FakeLPMergeProposal(),
583
FakeLPMergeProposal()])
584
branch = FakeBzrBranch()
585
e = self.assertRaises(errors.BzrCommandError,
586
lander.get_merge_proposal_from_branch, branch)
587
self.assertEqual('The public branch has multiple active source merge'
588
' proposals. You must provide the URL to the one'
589
' you wish to use.', str(e))
591
def test_get_merge_proposal_inactive(self):
592
for status in ['Rejected', 'Work in progress', 'Merged', 'Queued',
593
'Code failed to merge', 'Superseded']:
594
proposal = FakeLPMergeProposal(queue_status=status)
595
lander = self.get_lander([proposal])
596
branch = FakeBzrBranch()
597
e = self.assertRaises(errors.BzrCommandError,
598
lander.get_merge_proposal_from_branch,
600
self.assertEqual('The public branch has no active source merge'
601
' proposals. You must have a merge proposal'
602
' before attempting to land the branch.',
605
def test_get_merge_proposal_active(self):
606
branch = FakeBzrBranch()
607
for status in ['Approved', 'Needs review']:
608
proposal = FakeLPMergeProposal(queue_status=status)
609
lander = self.get_lander([proposal])
610
lander_proposal = lander.get_merge_proposal_from_branch(branch)
611
self.assertIs(proposal, lander_proposal._mp)
614
class TestMergeProposal(TestCase):
616
def test_get_reviews_none_unapproved(self):
617
"""Reviewer is not considered for un-approved propoals."""
618
reviewer = FakePerson()
619
proposal = FakeLPMergeProposal(queue_status='Needs review', votes=[],
621
mp = MergeProposal(proposal)
622
approvals = mp.get_reviews()
623
self.assertEqual({}, approvals)
625
def test_get_reviews_none_approved(self):
626
"""Approving a proposal counts as a vote if other approvals."""
627
reviewer = FakePerson()
628
proposal = FakeLPMergeProposal(queue_status='Approved', votes=[],
630
mp = MergeProposal(proposal)
631
approvals = mp.get_reviews()
632
self.assertEqual({None: [reviewer]}, approvals)
634
def test_get_reviews_one_approved(self):
635
"""As long as there is one approve vote, don't use reviewer."""
636
reviewer = FakePerson()
637
proposal = FakeLPMergeProposal(queue_status='Approved',
640
mp = MergeProposal(proposal)
641
approvals = mp.get_reviews()
642
self.assertEqual({None: [proposal.votes[0].reviewer]}, approvals)
644
def test_get_stakeholder_emails(self):
646
mp = MergeProposal(FakeLPMergeProposal(root=lp))
647
lp.me.preferred_email_address = FakeEmailAddress('lander@example.org')
648
owner = mp._mp.source_branch.owner
649
owner.preferred_email_address = FakeEmailAddress('owner@example.org')
650
expected = set(['owner@example.org', 'lander@example.org'])
651
emails = mp.get_stakeholder_emails()
652
self.assertEqual(expected, emails)
654
def test_get_stakeholder_emails_none(self):
656
mp = MergeProposal(FakeLPMergeProposal(root=lp))
657
lp.me.preferred_email_address = FakeEmailAddress('lander@example.org')
658
owner = mp._mp.source_branch.owner
659
owner.preferred_email_address = None
660
expected = set(['lander@example.org'])
661
emails = mp.get_stakeholder_emails()
662
self.assertEqual(expected, emails)
404
665
class TestSubmitter(TestCaseWithTransport):
406
667
def make_submitter(self):