Merge lp:~rockstar/launchpad/merge-queues-model into lp:launchpad/db-devel

Proposed by Paul Hummer
Status: Merged
Approved by: Aaron Bentley
Approved revision: no longer in the source branch.
Merged at revision: 9912
Proposed branch: lp:~rockstar/launchpad/merge-queues-model
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~rockstar/launchpad/merge-queues-db
Diff against target: 715 lines (+383/-131)
11 files modified
lib/lp/code/enums.py (+0/-39)
lib/lp/code/errors.py (+11/-0)
lib/lp/code/interfaces/branch.py (+30/-4)
lib/lp/code/interfaces/branchmergequeue.py (+94/-0)
lib/lp/code/model/branch.py (+24/-10)
lib/lp/code/model/branchmergequeue.py (+79/-0)
lib/lp/code/model/branchnamespace.py (+0/-1)
lib/lp/code/model/tests/test_branch.py (+40/-0)
lib/lp/code/model/tests/test_branchmergeproposal.py (+0/-77)
lib/lp/code/model/tests/test_branchmergequeue.py (+91/-0)
lib/lp/testing/factory.py (+14/-0)
To merge this branch: bzr merge lp:~rockstar/launchpad/merge-queues-model
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+38762@code.launchpad.net

Description of the change

This branch adds the model code for branch merge queues. In my next branch, I'll need to break up the interfaces, because I'm exposing this in the API, but I thought that, instead of doing that in this pipe, it'd make more sense to do it when needed, as this branch seems to be a pretty good stopping point for the work it provides.

Oh, I was found an enum that should have been removed in the previous branch, but was not. I killed it in this branch since that one had already landed.

To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote :

Please add an extra blank line before the definition of InvalidMergeQueueConfig, and make BranchMergeQueue.new a classmethod, not a staticmethod, so you don't need to repeat the class name.

Otherwise, this is good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/enums.py'
--- lib/lp/code/enums.py 2010-08-20 20:31:18 +0000
+++ lib/lp/code/enums.py 2010-10-20 17:48:56 +0000
@@ -7,7 +7,6 @@
7__all__ = [7__all__ = [
8 'BranchLifecycleStatus',8 'BranchLifecycleStatus',
9 'BranchLifecycleStatusFilter',9 'BranchLifecycleStatusFilter',
10 'BranchMergeControlStatus',
11 'BranchMergeProposalStatus',10 'BranchMergeProposalStatus',
12 'BranchSubscriptionDiffSize',11 'BranchSubscriptionDiffSize',
13 'BranchSubscriptionNotificationLevel',12 'BranchSubscriptionNotificationLevel',
@@ -77,44 +76,6 @@
77 ABANDONED = DBItem(80, "Abandoned")76 ABANDONED = DBItem(80, "Abandoned")
7877
7978
80class BranchMergeControlStatus(DBEnumeratedType):
81 """Branch Merge Control Status
82
83 Does the branch want Launchpad to manage a merge queue, and if it does,
84 how does the branch owner handle removing items from the queue.
85 """
86
87 NO_QUEUE = DBItem(1, """
88 Does not use a merge queue
89
90 The branch does not use the merge queue managed by Launchpad. Merges
91 are tracked and managed elsewhere. Users will not be able to queue up
92 approved branch merge proposals.
93 """)
94
95 MANUAL = DBItem(2, """
96 Manual processing of the merge queue
97
98 One or more people are responsible for manually processing the queued
99 branch merge proposals.
100 """)
101
102 ROBOT = DBItem(3, """
103 A branch merge robot is used to process the merge queue
104
105 An external application, like PQM, is used to merge in the queued
106 approved proposed merges.
107 """)
108
109 ROBOT_RESTRICTED = DBItem(4, """
110 The branch merge robot used to process the queue is in restricted mode
111
112 When the robot is in restricted mode, normal queued branches are not
113 returned for merging, only those with "Queued for Restricted
114 merging" will be.
115 """)
116
117
118class BranchType(DBEnumeratedType):79class BranchType(DBEnumeratedType):
119 """Branch Type80 """Branch Type
12081
12182
=== modified file 'lib/lp/code/errors.py'
--- lib/lp/code/errors.py 2010-09-02 14:30:47 +0000
+++ lib/lp/code/errors.py 2010-10-20 17:48:56 +0000
@@ -26,6 +26,7 @@
26 'CodeImportNotInReviewedState',26 'CodeImportNotInReviewedState',
27 'ClaimReviewFailed',27 'ClaimReviewFailed',
28 'InvalidBranchMergeProposal',28 'InvalidBranchMergeProposal',
29 'InvalidMergeQueueConfig',
29 'InvalidNamespace',30 'InvalidNamespace',
30 'NoLinkedBranch',31 'NoLinkedBranch',
31 'NoSuchBranch',32 'NoSuchBranch',
@@ -291,3 +292,13 @@
291 RecipeBuildException.__init__(292 RecipeBuildException.__init__(
292 self, recipe, distroseries,293 self, recipe, distroseries,
293 'A build against this distro is not allowed.')294 'A build against this distro is not allowed.')
295
296
297class InvalidMergeQueueConfig(Exception):
298 """The config specified is not a valid JSON string."""
299
300 webservice_error(400)
301
302 def __init__(self):
303 message = ('The configuration specified is not a valid JSON string.')
304 Exception.__init__(self, message)
294305
=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py 2010-10-08 04:19:45 +0000
+++ lib/lp/code/interfaces/branch.py 2010-10-20 17:48:56 +0000
@@ -75,13 +75,13 @@
75 )75 )
76from lp.code.enums import (76from lp.code.enums import (
77 BranchLifecycleStatus,77 BranchLifecycleStatus,
78 BranchMergeControlStatus,
79 BranchSubscriptionDiffSize,78 BranchSubscriptionDiffSize,
80 BranchSubscriptionNotificationLevel,79 BranchSubscriptionNotificationLevel,
81 CodeReviewNotificationLevel,80 CodeReviewNotificationLevel,
82 UICreatableBranchType,81 UICreatableBranchType,
83 )82 )
84from lp.code.interfaces.branchlookup import IBranchLookup83from lp.code.interfaces.branchlookup import IBranchLookup
84from lp.code.interfaces.branchmergequeue import IBranchMergeQueue
85from lp.code.interfaces.branchtarget import IHasBranchTarget85from lp.code.interfaces.branchtarget import IHasBranchTarget
86from lp.code.interfaces.hasbranches import IHasMergeProposals86from lp.code.interfaces.hasbranches import IHasMergeProposals
87from lp.code.interfaces.hasrecipes import IHasRecipes87from lp.code.interfaces.hasrecipes import IHasRecipes
@@ -591,9 +591,6 @@
591 def getStackedBranches():591 def getStackedBranches():
592 """The branches that are stacked on this one."""592 """The branches that are stacked on this one."""
593593
594 def getMergeQueue():
595 """The proposals that are QUEUED to land on this branch."""
596
597 def getMainlineBranchRevisions(start_date, end_date=None,594 def getMainlineBranchRevisions(start_date, end_date=None,
598 oldest_first=False):595 oldest_first=False):
599 """Return the matching mainline branch revision objects.596 """Return the matching mainline branch revision objects.
@@ -993,6 +990,18 @@
993 required=False, readonly=True,990 required=False, readonly=True,
994 vocabulary=ControlFormat))991 vocabulary=ControlFormat))
995992
993 merge_queue = Reference(
994 title=_('Branch Merge Queue'),
995 schema=IBranchMergeQueue, required=False, readonly=True,
996 description=_(
997 "The branch merge queue that manages merges for this branch."))
998
999 merge_queue_config = TextLine(
1000 title=_('Name'), required=True, constraint=branch_name_validator,
1001 description=_(
1002 "A JSON string of configuration values to send to a branch"
1003 "merge robot."))
1004
9961005
997class IBranchEdit(Interface):1006class IBranchEdit(Interface):
998 """IBranch attributes that require launchpad.Edit permission."""1007 """IBranch attributes that require launchpad.Edit permission."""
@@ -1066,6 +1075,23 @@
1066 :raise: CannotDeleteBranch if the branch cannot be deleted.1075 :raise: CannotDeleteBranch if the branch cannot be deleted.
1067 """1076 """
10681077
1078 def addToQueue(queue):
1079 """Add this branch to a specified queue.
1080
1081 A branch's merges can be managed by a queue.
1082
1083 :param queue: The branch merge queue that will manage the branch.
1084 """
1085
1086 def setMergeQueueConfig(config):
1087 """Set the merge_queue_config property.
1088
1089 A branch can store a JSON string of configuration data for a merge
1090 robot to retrieve.
1091
1092 :param config: A JSON string of data.
1093 """
1094
10691095
1070class IBranch(IBranchPublic, IBranchView, IBranchEdit,1096class IBranch(IBranchPublic, IBranchView, IBranchEdit,
1071 IBranchEditableAttributes, IBranchAnyone):1097 IBranchEditableAttributes, IBranchAnyone):
10721098
=== added file 'lib/lp/code/interfaces/branchmergequeue.py'
--- lib/lp/code/interfaces/branchmergequeue.py 1970-01-01 00:00:00 +0000
+++ lib/lp/code/interfaces/branchmergequeue.py 2010-10-20 17:48:56 +0000
@@ -0,0 +1,94 @@
1# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Branch merge queue interfaces."""
5
6__metaclass__ = type
7
8__all__ = [
9 'IBranchMergeQueue',
10 'IBranchMergeQueueSource',
11 ]
12
13from lazr.restful.fields import (
14 CollectionField,
15 Reference,
16 )
17from zope.interface import Interface
18from zope.schema import (
19 Datetime,
20 Int,
21 Text,
22 TextLine,
23 )
24
25from canonical.launchpad import _
26from lp.services.fields import (
27 PersonChoice,
28 PublicPersonChoice,
29 )
30
31
32class IBranchMergeQueue(Interface):
33 """An interface for managing branch merges."""
34
35 id = Int(title=_('ID'), readonly=True, required=True)
36
37 registrant = PublicPersonChoice(
38 title=_("The user that registered the branch."),
39 required=True, readonly=True,
40 vocabulary='ValidPersonOrTeam')
41
42 owner = PersonChoice(
43 title=_('Owner'),
44 required=True, readonly=True,
45 vocabulary='UserTeamsParticipationPlusSelf',
46 description=_("The owner of the merge queue."))
47
48 name = TextLine(
49 title=_('Name'), required=True,
50 description=_(
51 "Keep very short, unique, and descriptive, because it will "
52 "be used in URLs. "
53 "Examples: main, devel, release-1.0, gnome-vfs."))
54
55 description = Text(
56 title=_('Description'), required=False,
57 description=_(
58 'A short description of the purpose of this merge queue.'))
59
60 configuration = TextLine(
61 title=_('Configuration'), required=False,
62 description=_(
63 "A JSON string of configuration values."))
64
65 date_created = Datetime(
66 title=_('Date Created'),
67 required=True,
68 readonly=True)
69
70 branches = CollectionField(
71 title=_('Dependent Branches'),
72 description=_('A collection of branches that this queue manages.'),
73 readonly=True,
74 value_type=Reference(Interface))
75
76 def setMergeQueueConfig(config):
77 """Set the JSON string configuration of the merge queue.
78
79 :param config: A JSON string of configuration values.
80 """
81
82
83class IBranchMergeQueueSource(Interface):
84
85 def new(name, owner, registrant, description, configuration, branches):
86 """Create a new IBranchMergeQueue object.
87
88 :param name: The name of the branch merge queue.
89 :param description: A description of queue.
90 :param configuration: A JSON string of configuration values.
91 :param owner: The owner of the queue.
92 :param registrant: The registrant of the queue.
93 :param branches: A list of branches to add to the queue.
94 """
095
=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py 2010-10-18 21:18:03 +0000
+++ lib/lp/code/model/branch.py 2010-10-20 17:48:56 +0000
@@ -10,6 +10,7 @@
10 ]10 ]
1111
12from datetime import datetime12from datetime import datetime
13import simplejson
1314
14from bzrlib import urlutils15from bzrlib import urlutils
15from bzrlib.revision import NULL_REVISION16from bzrlib.revision import NULL_REVISION
@@ -31,7 +32,11 @@
31 Or,32 Or,
32 Select,33 Select,
33 )34 )
34from storm.locals import AutoReload35from storm.locals import (
36 AutoReload,
37 Int,
38 Reference,
39 )
35from storm.store import Store40from storm.store import Store
36from zope.component import getUtility41from zope.component import getUtility
37from zope.event import notify42from zope.event import notify
@@ -70,7 +75,6 @@
70 )75 )
71from lp.code.enums import (76from lp.code.enums import (
72 BranchLifecycleStatus,77 BranchLifecycleStatus,
73 BranchMergeControlStatus,
74 BranchMergeProposalStatus,78 BranchMergeProposalStatus,
75 BranchType,79 BranchType,
76 )80 )
@@ -82,6 +86,7 @@
82 BranchTypeError,86 BranchTypeError,
83 CannotDeleteBranch,87 CannotDeleteBranch,
84 InvalidBranchMergeProposal,88 InvalidBranchMergeProposal,
89 InvalidMergeQueueConfig,
85 )90 )
86from lp.code.event.branchmergeproposal import NewBranchMergeProposalEvent91from lp.code.event.branchmergeproposal import NewBranchMergeProposalEvent
87from lp.code.interfaces.branch import (92from lp.code.interfaces.branch import (
@@ -475,14 +480,6 @@
475 store = Store.of(self)480 store = Store.of(self)
476 return store.find(Branch, Branch.stacked_on == self)481 return store.find(Branch, Branch.stacked_on == self)
477482
478 def getMergeQueue(self):
479 """See `IBranch`."""
480 return BranchMergeProposal.select("""
481 BranchMergeProposal.target_branch = %s AND
482 BranchMergeProposal.queue_status = %s
483 """ % sqlvalues(self, BranchMergeProposalStatus.QUEUED),
484 orderBy="queue_position")
485
486 @property483 @property
487 def code_is_browseable(self):484 def code_is_browseable(self):
488 """See `IBranch`."""485 """See `IBranch`."""
@@ -1141,6 +1138,23 @@
1141 SourcePackageRecipeData)1138 SourcePackageRecipeData)
1142 return SourcePackageRecipeData.findRecipes(self)1139 return SourcePackageRecipeData.findRecipes(self)
11431140
1141 merge_queue_id = Int(name='merge_queue', allow_none=True)
1142 merge_queue = Reference(merge_queue_id, 'BranchMergeQueue.id')
1143
1144 merge_queue_config = StringCol(dbName='merge_queue_config')
1145
1146 def addToQueue(self, queue):
1147 """See `IBranchEdit`."""
1148 self.merge_queue = queue
1149
1150 def setMergeQueueConfig(self, config):
1151 """See `IBranchEdit`."""
1152 try:
1153 simplejson.loads(config)
1154 self.merge_queue_config = config
1155 except ValueError: # The json string is invalid
1156 raise InvalidMergeQueueConfig
1157
11441158
1145class DeletionOperation:1159class DeletionOperation:
1146 """Represent an operation to perform as part of branch deletion."""1160 """Represent an operation to perform as part of branch deletion."""
11471161
=== added file 'lib/lp/code/model/branchmergequeue.py'
--- lib/lp/code/model/branchmergequeue.py 1970-01-01 00:00:00 +0000
+++ lib/lp/code/model/branchmergequeue.py 2010-10-20 17:48:56 +0000
@@ -0,0 +1,79 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Implementation classes for IBranchMergeQueue, etc."""
5
6__metaclass__ = type
7__all__ = ['BranchMergeQueue']
8
9import simplejson
10
11from storm.locals import (
12 Int,
13 Reference,
14 Store,
15 Storm,
16 Unicode,
17 )
18from zope.interface import (
19 classProvides,
20 implements,
21 )
22
23from canonical.database.datetimecol import UtcDateTimeCol
24from lp.code.errors import InvalidMergeQueueConfig
25from lp.code.interfaces.branchmergequeue import (
26 IBranchMergeQueue,
27 IBranchMergeQueueSource,
28 )
29from lp.code.model.branch import Branch
30
31
32class BranchMergeQueue(Storm):
33 """See `IBranchMergeQueue`."""
34
35 __storm_table__ = 'BranchMergeQueue'
36 implements(IBranchMergeQueue)
37 classProvides(IBranchMergeQueueSource)
38
39 id = Int(primary=True)
40
41 registrant_id = Int(name='registrant', allow_none=True)
42 registrant = Reference(registrant_id, 'Person.id')
43
44 owner_id = Int(name='owner', allow_none=True)
45 owner = Reference(owner_id, 'Person.id')
46
47 name = Unicode(allow_none=False)
48 description = Unicode(allow_none=False)
49 configuration = Unicode(allow_none=False)
50
51 date_created = UtcDateTimeCol(notNull=True)
52
53 @property
54 def branches(self):
55 """See `IBranchMergeQueue`."""
56 return Store.of(self).find(
57 Branch,
58 Branch.merge_queue_id == self.id)
59
60 def setMergeQueueConfig(self, config):
61 """See `IBranchMergeQueue`."""
62 try:
63 simplejson.loads(config)
64 self.configuration = config
65 except ValueError: # The config string is not valid JSON
66 raise InvalidMergeQueueConfig
67
68 @classmethod
69 def new(cls, name, owner, registrant, description=None,
70 configuration=None):
71 """See `IBranchMergeQueueSource`."""
72 queue = cls()
73 queue.name = name
74 queue.owner = owner
75 queue.registrant = registrant
76 queue.description = description
77 queue.configuration = configuration
78
79 return queue
080
=== modified file 'lib/lp/code/model/branchnamespace.py'
--- lib/lp/code/model/branchnamespace.py 2010-10-06 02:14:18 +0000
+++ lib/lp/code/model/branchnamespace.py 2010-10-20 17:48:56 +0000
@@ -27,7 +27,6 @@
27 )27 )
28from lp.code.enums import (28from lp.code.enums import (
29 BranchLifecycleStatus,29 BranchLifecycleStatus,
30 BranchMergeControlStatus,
31 BranchSubscriptionDiffSize,30 BranchSubscriptionDiffSize,
32 BranchSubscriptionNotificationLevel,31 BranchSubscriptionNotificationLevel,
33 BranchVisibilityRule,32 BranchVisibilityRule,
3433
=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py 2010-10-04 19:50:45 +0000
+++ lib/lp/code/model/tests/test_branch.py 2010-10-20 17:48:56 +0000
@@ -14,6 +14,7 @@
14 datetime,14 datetime,
15 timedelta,15 timedelta,
16 )16 )
17import simplejson
17from unittest import TestLoader18from unittest import TestLoader
1819
19from bzrlib.bzrdir import BzrDir20from bzrlib.bzrdir import BzrDir
@@ -66,6 +67,7 @@
66 BranchTargetError,67 BranchTargetError,
67 CannotDeleteBranch,68 CannotDeleteBranch,
68 InvalidBranchMergeProposal,69 InvalidBranchMergeProposal,
70 InvalidMergeQueueConfig,
69 )71 )
70from lp.code.interfaces.branch import (72from lp.code.interfaces.branch import (
71 DEFAULT_BRANCH_STATUS_IN_LISTING,73 DEFAULT_BRANCH_STATUS_IN_LISTING,
@@ -2704,5 +2706,43 @@
2704 self.assertRaises(UnsafeUrlSeen, db_stacked.getBzrBranch)2706 self.assertRaises(UnsafeUrlSeen, db_stacked.getBzrBranch)
27052707
27062708
2709class TestMergeQueue(TestCaseWithFactory):
2710 """Tests for branch merge queue functionality in branches."""
2711
2712 layer = DatabaseFunctionalLayer
2713
2714 def test_addToQueue(self):
2715 """Test Branch.addToQueue."""
2716 branch = self.factory.makeBranch()
2717 queue = self.factory.makeBranchMergeQueue()
2718 with person_logged_in(branch.owner):
2719 branch.addToQueue(queue)
2720
2721 self.assertEqual(branch.merge_queue, queue)
2722
2723 def test_setMergeQueueConfig(self):
2724 """Test Branch.setMergeQueueConfig."""
2725 branch = self.factory.makeBranch()
2726 config = simplejson.dumps({
2727 'path': '/',
2728 'test': 'make test',})
2729
2730 with person_logged_in(branch.owner):
2731 branch.setMergeQueueConfig(config)
2732
2733 self.assertEqual(branch.merge_queue_config, config)
2734
2735 def test_setMergeQueueConfig_invalid(self):
2736 """Test that invalid JSON strings aren't added to the database."""
2737 branch = self.factory.makeBranch()
2738 config = 'abc'
2739
2740 with person_logged_in(branch.owner):
2741 self.assertRaises(
2742 InvalidMergeQueueConfig,
2743 branch.setMergeQueueConfig,
2744 config)
2745
2746
2707def test_suite():2747def test_suite():
2708 return TestLoader().loadTestsFromName(__name__)2748 return TestLoader().loadTestsFromName(__name__)
27092749
=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
--- lib/lp/code/model/tests/test_branchmergeproposal.py 2010-10-04 19:50:45 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposal.py 2010-10-20 17:48:56 +0000
@@ -558,83 +558,6 @@
558 self.assertIsNot(None, proposal.date_review_requested)558 self.assertIsNot(None, proposal.date_review_requested)
559559
560560
561class TestBranchMergeProposalQueueing(TestCase):
562 """Test the enqueueing and dequeueing of merge proposals."""
563
564 layer = DatabaseFunctionalLayer
565
566 def setUp(self):
567 TestCase.setUp(self)
568 login(ANONYMOUS)
569 factory = LaunchpadObjectFactory()
570 owner = factory.makePerson()
571 self.target_branch = factory.makeProductBranch(owner=owner)
572 login(self.target_branch.owner.preferredemail.email)
573 self.proposals = [
574 factory.makeBranchMergeProposal(self.target_branch)
575 for x in range(4)]
576
577 def test_empty_target_queue(self):
578 """If there are no proposals targeted to the branch, the queue has
579 nothing in it."""
580 queued_proposals = list(self.target_branch.getMergeQueue())
581 self.assertEqual(0, len(queued_proposals),
582 "The initial merge queue should be empty.")
583
584 def test_single_item_in_queue(self):
585 """Enqueing a proposal makes it visible in the target branch queue."""
586 proposal = self.proposals[0]
587 proposal.enqueue(self.target_branch.owner, 'some-revision-id')
588 queued_proposals = list(self.target_branch.getMergeQueue())
589 self.assertEqual(1, len(queued_proposals),
590 "Should have one entry in the queue, got %s."
591 % len(queued_proposals))
592
593 def test_queue_ordering(self):
594 """Assert that the queue positions are based on the order the
595 proposals were enqueued."""
596 enqueued_order = []
597 for proposal in self.proposals[:-1]:
598 enqueued_order.append(proposal.source_branch.unique_name)
599 proposal.enqueue(self.target_branch.owner, 'some-revision')
600 queued_proposals = list(self.target_branch.getMergeQueue())
601 queue_order = [proposal.source_branch.unique_name
602 for proposal in queued_proposals]
603 self.assertEqual(
604 enqueued_order, queue_order,
605 "The queue should be in the order they were added. "
606 "Expected %s, got %s" % (enqueued_order, queue_order))
607
608 # Move the last one to the front.
609 proposal = queued_proposals[-1]
610 proposal.moveToFrontOfQueue()
611
612 new_queue_order = enqueued_order[-1:] + enqueued_order[:-1]
613
614 queued_proposals = list(self.target_branch.getMergeQueue())
615 queue_order = [proposal.source_branch.unique_name
616 for proposal in queued_proposals]
617 self.assertEqual(
618 new_queue_order, queue_order,
619 "The last should now be at the front. "
620 "Expected %s, got %s" % (new_queue_order, queue_order))
621
622 # Remove the proposal from the middle of the queue.
623 proposal = queued_proposals[1]
624 proposal.dequeue()
625 syncUpdate(proposal)
626
627 del new_queue_order[1]
628
629 queued_proposals = list(self.target_branch.getMergeQueue())
630 queue_order = [proposal.source_branch.unique_name
631 for proposal in queued_proposals]
632 self.assertEqual(
633 new_queue_order, queue_order,
634 "There should be only two queued items now. "
635 "Expected %s, got %s" % (new_queue_order, queue_order))
636
637
638class TestCreateCommentNotifications(TestCaseWithFactory):561class TestCreateCommentNotifications(TestCaseWithFactory):
639 """Test the notifications are raised at the right times."""562 """Test the notifications are raised at the right times."""
640563
641564
=== added file 'lib/lp/code/model/tests/test_branchmergequeue.py'
--- lib/lp/code/model/tests/test_branchmergequeue.py 1970-01-01 00:00:00 +0000
+++ lib/lp/code/model/tests/test_branchmergequeue.py 2010-10-20 17:48:56 +0000
@@ -0,0 +1,91 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Unit tests for methods of BranchMergeQueue."""
5
6from __future__ import with_statement
7
8import simplejson
9
10from canonical.launchpad.interfaces.lpstorm import IStore
11from canonical.launchpad.webapp.testing import verifyObject
12from canonical.testing.layers import DatabaseFunctionalLayer
13from lp.code.errors import InvalidMergeQueueConfig
14from lp.code.interfaces.branchmergequeue import IBranchMergeQueue
15from lp.code.model.branchmergequeue import BranchMergeQueue
16from lp.testing import (
17 person_logged_in,
18 TestCaseWithFactory,
19 )
20
21
22class TestBranchMergeQueueInterface(TestCaseWithFactory):
23 """Test IBranchMergeQueue interface."""
24
25 layer = DatabaseFunctionalLayer
26
27 def test_implements_interface(self):
28 queue = self.factory.makeBranchMergeQueue()
29 IStore(BranchMergeQueue).add(queue)
30 verifyObject(IBranchMergeQueue, queue)
31
32
33class TestBranchMergeQueueSource(TestCaseWithFactory):
34 """Test the methods of IBranchMergeQueueSource."""
35
36 layer = DatabaseFunctionalLayer
37
38 def test_new(self):
39 owner = self.factory.makePerson()
40 name = u'SooperQueue'
41 description = u'This is Sooper Queue'
42 config = unicode(simplejson.dumps({'test': 'make check'}))
43
44 queue = BranchMergeQueue.new(
45 name, owner, owner, description, config)
46
47 self.assertEqual(queue.name, name)
48 self.assertEqual(queue.owner, owner)
49 self.assertEqual(queue.registrant, owner)
50 self.assertEqual(queue.description, description)
51 self.assertEqual(queue.configuration, config)
52
53
54class TestBranchMergeQueue(TestCaseWithFactory):
55 """Test the functions of the BranchMergeQueue."""
56
57 layer = DatabaseFunctionalLayer
58
59 def test_branches(self):
60 """Test that a merge queue can get all its managed branches."""
61 store = IStore(BranchMergeQueue)
62
63 queue = self.factory.makeBranchMergeQueue()
64 store.add(queue)
65
66 branch = self.factory.makeBranch()
67 store.add(branch)
68 with person_logged_in(branch.owner):
69 branch.addToQueue(queue)
70
71 self.assertEqual(
72 list(queue.branches),
73 [branch])
74
75 def test_setMergeQueueConfig(self):
76 """Test that the configuration is set properly."""
77 queue = self.factory.makeBranchMergeQueue()
78 config = unicode(simplejson.dumps({
79 'test': 'make test'}))
80
81 queue.setMergeQueueConfig(config)
82
83 self.assertEqual(queue.configuration, config)
84
85 def test_setMergeQueueConfig_invalid_json(self):
86 """Test that invalid json can't be set as the config."""
87 queue = self.factory.makeBranchMergeQueue()
88 self.assertRaises(
89 InvalidMergeQueueConfig,
90 queue.setMergeQueueConfig,
91 'abc')
092
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-10-18 23:19:16 +0000
+++ lib/lp/testing/factory.py 2010-10-20 17:48:56 +0000
@@ -36,6 +36,7 @@
36from operator import isSequenceType36from operator import isSequenceType
37import os37import os
38from random import randint38from random import randint
39import simplejson
39from StringIO import StringIO40from StringIO import StringIO
40from textwrap import dedent41from textwrap import dedent
41from threading import local42from threading import local
@@ -146,6 +147,7 @@
146 PreviewDiff,147 PreviewDiff,
147 StaticDiff,148 StaticDiff,
148 )149 )
150from lp.code.model.branchmergequeue import BranchMergeQueue
149from lp.codehosting.codeimport.worker import CodeImportSourceDetails151from lp.codehosting.codeimport.worker import CodeImportSourceDetails
150from lp.hardwaredb.interfaces.hwdb import (152from lp.hardwaredb.interfaces.hwdb import (
151 HWSubmissionFormat,153 HWSubmissionFormat,
@@ -1096,6 +1098,18 @@
1096 namespace = target.getNamespace(owner)1098 namespace = target.getNamespace(owner)
1097 return namespace.createBranch(branch_type, name, creator)1099 return namespace.createBranch(branch_type, name, creator)
10981100
1101 def makeBranchMergeQueue(self):
1102 """Create a BranchMergeQueue."""
1103 name = unicode(self.getUniqueString('queue'))
1104 owner = self.makePerson()
1105 description = unicode(self.getUniqueString('queue-description'))
1106 configuration = unicode(simplejson.dumps({
1107 self.getUniqueString('key'): self.getUniqueString('value')}))
1108
1109 queue = BranchMergeQueue.new(
1110 name, owner, owner, description, configuration)
1111 return queue
1112
1099 def enableDefaultStackingForProduct(self, product, branch=None):1113 def enableDefaultStackingForProduct(self, product, branch=None):
1100 """Give 'product' a default stacked-on branch.1114 """Give 'product' a default stacked-on branch.
11011115

Subscribers

People subscribed via source and target branches

to status/vote changes: