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
1=== modified file 'lib/lp/code/enums.py'
2--- lib/lp/code/enums.py 2010-08-20 20:31:18 +0000
3+++ lib/lp/code/enums.py 2010-10-20 17:48:56 +0000
4@@ -7,7 +7,6 @@
5 __all__ = [
6 'BranchLifecycleStatus',
7 'BranchLifecycleStatusFilter',
8- 'BranchMergeControlStatus',
9 'BranchMergeProposalStatus',
10 'BranchSubscriptionDiffSize',
11 'BranchSubscriptionNotificationLevel',
12@@ -77,44 +76,6 @@
13 ABANDONED = DBItem(80, "Abandoned")
14
15
16-class BranchMergeControlStatus(DBEnumeratedType):
17- """Branch Merge Control Status
18-
19- Does the branch want Launchpad to manage a merge queue, and if it does,
20- how does the branch owner handle removing items from the queue.
21- """
22-
23- NO_QUEUE = DBItem(1, """
24- Does not use a merge queue
25-
26- The branch does not use the merge queue managed by Launchpad. Merges
27- are tracked and managed elsewhere. Users will not be able to queue up
28- approved branch merge proposals.
29- """)
30-
31- MANUAL = DBItem(2, """
32- Manual processing of the merge queue
33-
34- One or more people are responsible for manually processing the queued
35- branch merge proposals.
36- """)
37-
38- ROBOT = DBItem(3, """
39- A branch merge robot is used to process the merge queue
40-
41- An external application, like PQM, is used to merge in the queued
42- approved proposed merges.
43- """)
44-
45- ROBOT_RESTRICTED = DBItem(4, """
46- The branch merge robot used to process the queue is in restricted mode
47-
48- When the robot is in restricted mode, normal queued branches are not
49- returned for merging, only those with "Queued for Restricted
50- merging" will be.
51- """)
52-
53-
54 class BranchType(DBEnumeratedType):
55 """Branch Type
56
57
58=== modified file 'lib/lp/code/errors.py'
59--- lib/lp/code/errors.py 2010-09-02 14:30:47 +0000
60+++ lib/lp/code/errors.py 2010-10-20 17:48:56 +0000
61@@ -26,6 +26,7 @@
62 'CodeImportNotInReviewedState',
63 'ClaimReviewFailed',
64 'InvalidBranchMergeProposal',
65+ 'InvalidMergeQueueConfig',
66 'InvalidNamespace',
67 'NoLinkedBranch',
68 'NoSuchBranch',
69@@ -291,3 +292,13 @@
70 RecipeBuildException.__init__(
71 self, recipe, distroseries,
72 'A build against this distro is not allowed.')
73+
74+
75+class InvalidMergeQueueConfig(Exception):
76+ """The config specified is not a valid JSON string."""
77+
78+ webservice_error(400)
79+
80+ def __init__(self):
81+ message = ('The configuration specified is not a valid JSON string.')
82+ Exception.__init__(self, message)
83
84=== modified file 'lib/lp/code/interfaces/branch.py'
85--- lib/lp/code/interfaces/branch.py 2010-10-08 04:19:45 +0000
86+++ lib/lp/code/interfaces/branch.py 2010-10-20 17:48:56 +0000
87@@ -75,13 +75,13 @@
88 )
89 from lp.code.enums import (
90 BranchLifecycleStatus,
91- BranchMergeControlStatus,
92 BranchSubscriptionDiffSize,
93 BranchSubscriptionNotificationLevel,
94 CodeReviewNotificationLevel,
95 UICreatableBranchType,
96 )
97 from lp.code.interfaces.branchlookup import IBranchLookup
98+from lp.code.interfaces.branchmergequeue import IBranchMergeQueue
99 from lp.code.interfaces.branchtarget import IHasBranchTarget
100 from lp.code.interfaces.hasbranches import IHasMergeProposals
101 from lp.code.interfaces.hasrecipes import IHasRecipes
102@@ -591,9 +591,6 @@
103 def getStackedBranches():
104 """The branches that are stacked on this one."""
105
106- def getMergeQueue():
107- """The proposals that are QUEUED to land on this branch."""
108-
109 def getMainlineBranchRevisions(start_date, end_date=None,
110 oldest_first=False):
111 """Return the matching mainline branch revision objects.
112@@ -993,6 +990,18 @@
113 required=False, readonly=True,
114 vocabulary=ControlFormat))
115
116+ merge_queue = Reference(
117+ title=_('Branch Merge Queue'),
118+ schema=IBranchMergeQueue, required=False, readonly=True,
119+ description=_(
120+ "The branch merge queue that manages merges for this branch."))
121+
122+ merge_queue_config = TextLine(
123+ title=_('Name'), required=True, constraint=branch_name_validator,
124+ description=_(
125+ "A JSON string of configuration values to send to a branch"
126+ "merge robot."))
127+
128
129 class IBranchEdit(Interface):
130 """IBranch attributes that require launchpad.Edit permission."""
131@@ -1066,6 +1075,23 @@
132 :raise: CannotDeleteBranch if the branch cannot be deleted.
133 """
134
135+ def addToQueue(queue):
136+ """Add this branch to a specified queue.
137+
138+ A branch's merges can be managed by a queue.
139+
140+ :param queue: The branch merge queue that will manage the branch.
141+ """
142+
143+ def setMergeQueueConfig(config):
144+ """Set the merge_queue_config property.
145+
146+ A branch can store a JSON string of configuration data for a merge
147+ robot to retrieve.
148+
149+ :param config: A JSON string of data.
150+ """
151+
152
153 class IBranch(IBranchPublic, IBranchView, IBranchEdit,
154 IBranchEditableAttributes, IBranchAnyone):
155
156=== added file 'lib/lp/code/interfaces/branchmergequeue.py'
157--- lib/lp/code/interfaces/branchmergequeue.py 1970-01-01 00:00:00 +0000
158+++ lib/lp/code/interfaces/branchmergequeue.py 2010-10-20 17:48:56 +0000
159@@ -0,0 +1,94 @@
160+# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
161+# GNU Affero General Public License version 3 (see the file LICENSE).
162+
163+"""Branch merge queue interfaces."""
164+
165+__metaclass__ = type
166+
167+__all__ = [
168+ 'IBranchMergeQueue',
169+ 'IBranchMergeQueueSource',
170+ ]
171+
172+from lazr.restful.fields import (
173+ CollectionField,
174+ Reference,
175+ )
176+from zope.interface import Interface
177+from zope.schema import (
178+ Datetime,
179+ Int,
180+ Text,
181+ TextLine,
182+ )
183+
184+from canonical.launchpad import _
185+from lp.services.fields import (
186+ PersonChoice,
187+ PublicPersonChoice,
188+ )
189+
190+
191+class IBranchMergeQueue(Interface):
192+ """An interface for managing branch merges."""
193+
194+ id = Int(title=_('ID'), readonly=True, required=True)
195+
196+ registrant = PublicPersonChoice(
197+ title=_("The user that registered the branch."),
198+ required=True, readonly=True,
199+ vocabulary='ValidPersonOrTeam')
200+
201+ owner = PersonChoice(
202+ title=_('Owner'),
203+ required=True, readonly=True,
204+ vocabulary='UserTeamsParticipationPlusSelf',
205+ description=_("The owner of the merge queue."))
206+
207+ name = TextLine(
208+ title=_('Name'), required=True,
209+ description=_(
210+ "Keep very short, unique, and descriptive, because it will "
211+ "be used in URLs. "
212+ "Examples: main, devel, release-1.0, gnome-vfs."))
213+
214+ description = Text(
215+ title=_('Description'), required=False,
216+ description=_(
217+ 'A short description of the purpose of this merge queue.'))
218+
219+ configuration = TextLine(
220+ title=_('Configuration'), required=False,
221+ description=_(
222+ "A JSON string of configuration values."))
223+
224+ date_created = Datetime(
225+ title=_('Date Created'),
226+ required=True,
227+ readonly=True)
228+
229+ branches = CollectionField(
230+ title=_('Dependent Branches'),
231+ description=_('A collection of branches that this queue manages.'),
232+ readonly=True,
233+ value_type=Reference(Interface))
234+
235+ def setMergeQueueConfig(config):
236+ """Set the JSON string configuration of the merge queue.
237+
238+ :param config: A JSON string of configuration values.
239+ """
240+
241+
242+class IBranchMergeQueueSource(Interface):
243+
244+ def new(name, owner, registrant, description, configuration, branches):
245+ """Create a new IBranchMergeQueue object.
246+
247+ :param name: The name of the branch merge queue.
248+ :param description: A description of queue.
249+ :param configuration: A JSON string of configuration values.
250+ :param owner: The owner of the queue.
251+ :param registrant: The registrant of the queue.
252+ :param branches: A list of branches to add to the queue.
253+ """
254
255=== modified file 'lib/lp/code/model/branch.py'
256--- lib/lp/code/model/branch.py 2010-10-18 21:18:03 +0000
257+++ lib/lp/code/model/branch.py 2010-10-20 17:48:56 +0000
258@@ -10,6 +10,7 @@
259 ]
260
261 from datetime import datetime
262+import simplejson
263
264 from bzrlib import urlutils
265 from bzrlib.revision import NULL_REVISION
266@@ -31,7 +32,11 @@
267 Or,
268 Select,
269 )
270-from storm.locals import AutoReload
271+from storm.locals import (
272+ AutoReload,
273+ Int,
274+ Reference,
275+ )
276 from storm.store import Store
277 from zope.component import getUtility
278 from zope.event import notify
279@@ -70,7 +75,6 @@
280 )
281 from lp.code.enums import (
282 BranchLifecycleStatus,
283- BranchMergeControlStatus,
284 BranchMergeProposalStatus,
285 BranchType,
286 )
287@@ -82,6 +86,7 @@
288 BranchTypeError,
289 CannotDeleteBranch,
290 InvalidBranchMergeProposal,
291+ InvalidMergeQueueConfig,
292 )
293 from lp.code.event.branchmergeproposal import NewBranchMergeProposalEvent
294 from lp.code.interfaces.branch import (
295@@ -475,14 +480,6 @@
296 store = Store.of(self)
297 return store.find(Branch, Branch.stacked_on == self)
298
299- def getMergeQueue(self):
300- """See `IBranch`."""
301- return BranchMergeProposal.select("""
302- BranchMergeProposal.target_branch = %s AND
303- BranchMergeProposal.queue_status = %s
304- """ % sqlvalues(self, BranchMergeProposalStatus.QUEUED),
305- orderBy="queue_position")
306-
307 @property
308 def code_is_browseable(self):
309 """See `IBranch`."""
310@@ -1141,6 +1138,23 @@
311 SourcePackageRecipeData)
312 return SourcePackageRecipeData.findRecipes(self)
313
314+ merge_queue_id = Int(name='merge_queue', allow_none=True)
315+ merge_queue = Reference(merge_queue_id, 'BranchMergeQueue.id')
316+
317+ merge_queue_config = StringCol(dbName='merge_queue_config')
318+
319+ def addToQueue(self, queue):
320+ """See `IBranchEdit`."""
321+ self.merge_queue = queue
322+
323+ def setMergeQueueConfig(self, config):
324+ """See `IBranchEdit`."""
325+ try:
326+ simplejson.loads(config)
327+ self.merge_queue_config = config
328+ except ValueError: # The json string is invalid
329+ raise InvalidMergeQueueConfig
330+
331
332 class DeletionOperation:
333 """Represent an operation to perform as part of branch deletion."""
334
335=== added file 'lib/lp/code/model/branchmergequeue.py'
336--- lib/lp/code/model/branchmergequeue.py 1970-01-01 00:00:00 +0000
337+++ lib/lp/code/model/branchmergequeue.py 2010-10-20 17:48:56 +0000
338@@ -0,0 +1,79 @@
339+# Copyright 2010 Canonical Ltd. This software is licensed under the
340+# GNU Affero General Public License version 3 (see the file LICENSE).
341+
342+"""Implementation classes for IBranchMergeQueue, etc."""
343+
344+__metaclass__ = type
345+__all__ = ['BranchMergeQueue']
346+
347+import simplejson
348+
349+from storm.locals import (
350+ Int,
351+ Reference,
352+ Store,
353+ Storm,
354+ Unicode,
355+ )
356+from zope.interface import (
357+ classProvides,
358+ implements,
359+ )
360+
361+from canonical.database.datetimecol import UtcDateTimeCol
362+from lp.code.errors import InvalidMergeQueueConfig
363+from lp.code.interfaces.branchmergequeue import (
364+ IBranchMergeQueue,
365+ IBranchMergeQueueSource,
366+ )
367+from lp.code.model.branch import Branch
368+
369+
370+class BranchMergeQueue(Storm):
371+ """See `IBranchMergeQueue`."""
372+
373+ __storm_table__ = 'BranchMergeQueue'
374+ implements(IBranchMergeQueue)
375+ classProvides(IBranchMergeQueueSource)
376+
377+ id = Int(primary=True)
378+
379+ registrant_id = Int(name='registrant', allow_none=True)
380+ registrant = Reference(registrant_id, 'Person.id')
381+
382+ owner_id = Int(name='owner', allow_none=True)
383+ owner = Reference(owner_id, 'Person.id')
384+
385+ name = Unicode(allow_none=False)
386+ description = Unicode(allow_none=False)
387+ configuration = Unicode(allow_none=False)
388+
389+ date_created = UtcDateTimeCol(notNull=True)
390+
391+ @property
392+ def branches(self):
393+ """See `IBranchMergeQueue`."""
394+ return Store.of(self).find(
395+ Branch,
396+ Branch.merge_queue_id == self.id)
397+
398+ def setMergeQueueConfig(self, config):
399+ """See `IBranchMergeQueue`."""
400+ try:
401+ simplejson.loads(config)
402+ self.configuration = config
403+ except ValueError: # The config string is not valid JSON
404+ raise InvalidMergeQueueConfig
405+
406+ @classmethod
407+ def new(cls, name, owner, registrant, description=None,
408+ configuration=None):
409+ """See `IBranchMergeQueueSource`."""
410+ queue = cls()
411+ queue.name = name
412+ queue.owner = owner
413+ queue.registrant = registrant
414+ queue.description = description
415+ queue.configuration = configuration
416+
417+ return queue
418
419=== modified file 'lib/lp/code/model/branchnamespace.py'
420--- lib/lp/code/model/branchnamespace.py 2010-10-06 02:14:18 +0000
421+++ lib/lp/code/model/branchnamespace.py 2010-10-20 17:48:56 +0000
422@@ -27,7 +27,6 @@
423 )
424 from lp.code.enums import (
425 BranchLifecycleStatus,
426- BranchMergeControlStatus,
427 BranchSubscriptionDiffSize,
428 BranchSubscriptionNotificationLevel,
429 BranchVisibilityRule,
430
431=== modified file 'lib/lp/code/model/tests/test_branch.py'
432--- lib/lp/code/model/tests/test_branch.py 2010-10-04 19:50:45 +0000
433+++ lib/lp/code/model/tests/test_branch.py 2010-10-20 17:48:56 +0000
434@@ -14,6 +14,7 @@
435 datetime,
436 timedelta,
437 )
438+import simplejson
439 from unittest import TestLoader
440
441 from bzrlib.bzrdir import BzrDir
442@@ -66,6 +67,7 @@
443 BranchTargetError,
444 CannotDeleteBranch,
445 InvalidBranchMergeProposal,
446+ InvalidMergeQueueConfig,
447 )
448 from lp.code.interfaces.branch import (
449 DEFAULT_BRANCH_STATUS_IN_LISTING,
450@@ -2704,5 +2706,43 @@
451 self.assertRaises(UnsafeUrlSeen, db_stacked.getBzrBranch)
452
453
454+class TestMergeQueue(TestCaseWithFactory):
455+ """Tests for branch merge queue functionality in branches."""
456+
457+ layer = DatabaseFunctionalLayer
458+
459+ def test_addToQueue(self):
460+ """Test Branch.addToQueue."""
461+ branch = self.factory.makeBranch()
462+ queue = self.factory.makeBranchMergeQueue()
463+ with person_logged_in(branch.owner):
464+ branch.addToQueue(queue)
465+
466+ self.assertEqual(branch.merge_queue, queue)
467+
468+ def test_setMergeQueueConfig(self):
469+ """Test Branch.setMergeQueueConfig."""
470+ branch = self.factory.makeBranch()
471+ config = simplejson.dumps({
472+ 'path': '/',
473+ 'test': 'make test',})
474+
475+ with person_logged_in(branch.owner):
476+ branch.setMergeQueueConfig(config)
477+
478+ self.assertEqual(branch.merge_queue_config, config)
479+
480+ def test_setMergeQueueConfig_invalid(self):
481+ """Test that invalid JSON strings aren't added to the database."""
482+ branch = self.factory.makeBranch()
483+ config = 'abc'
484+
485+ with person_logged_in(branch.owner):
486+ self.assertRaises(
487+ InvalidMergeQueueConfig,
488+ branch.setMergeQueueConfig,
489+ config)
490+
491+
492 def test_suite():
493 return TestLoader().loadTestsFromName(__name__)
494
495=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
496--- lib/lp/code/model/tests/test_branchmergeproposal.py 2010-10-04 19:50:45 +0000
497+++ lib/lp/code/model/tests/test_branchmergeproposal.py 2010-10-20 17:48:56 +0000
498@@ -558,83 +558,6 @@
499 self.assertIsNot(None, proposal.date_review_requested)
500
501
502-class TestBranchMergeProposalQueueing(TestCase):
503- """Test the enqueueing and dequeueing of merge proposals."""
504-
505- layer = DatabaseFunctionalLayer
506-
507- def setUp(self):
508- TestCase.setUp(self)
509- login(ANONYMOUS)
510- factory = LaunchpadObjectFactory()
511- owner = factory.makePerson()
512- self.target_branch = factory.makeProductBranch(owner=owner)
513- login(self.target_branch.owner.preferredemail.email)
514- self.proposals = [
515- factory.makeBranchMergeProposal(self.target_branch)
516- for x in range(4)]
517-
518- def test_empty_target_queue(self):
519- """If there are no proposals targeted to the branch, the queue has
520- nothing in it."""
521- queued_proposals = list(self.target_branch.getMergeQueue())
522- self.assertEqual(0, len(queued_proposals),
523- "The initial merge queue should be empty.")
524-
525- def test_single_item_in_queue(self):
526- """Enqueing a proposal makes it visible in the target branch queue."""
527- proposal = self.proposals[0]
528- proposal.enqueue(self.target_branch.owner, 'some-revision-id')
529- queued_proposals = list(self.target_branch.getMergeQueue())
530- self.assertEqual(1, len(queued_proposals),
531- "Should have one entry in the queue, got %s."
532- % len(queued_proposals))
533-
534- def test_queue_ordering(self):
535- """Assert that the queue positions are based on the order the
536- proposals were enqueued."""
537- enqueued_order = []
538- for proposal in self.proposals[:-1]:
539- enqueued_order.append(proposal.source_branch.unique_name)
540- proposal.enqueue(self.target_branch.owner, 'some-revision')
541- queued_proposals = list(self.target_branch.getMergeQueue())
542- queue_order = [proposal.source_branch.unique_name
543- for proposal in queued_proposals]
544- self.assertEqual(
545- enqueued_order, queue_order,
546- "The queue should be in the order they were added. "
547- "Expected %s, got %s" % (enqueued_order, queue_order))
548-
549- # Move the last one to the front.
550- proposal = queued_proposals[-1]
551- proposal.moveToFrontOfQueue()
552-
553- new_queue_order = enqueued_order[-1:] + enqueued_order[:-1]
554-
555- queued_proposals = list(self.target_branch.getMergeQueue())
556- queue_order = [proposal.source_branch.unique_name
557- for proposal in queued_proposals]
558- self.assertEqual(
559- new_queue_order, queue_order,
560- "The last should now be at the front. "
561- "Expected %s, got %s" % (new_queue_order, queue_order))
562-
563- # Remove the proposal from the middle of the queue.
564- proposal = queued_proposals[1]
565- proposal.dequeue()
566- syncUpdate(proposal)
567-
568- del new_queue_order[1]
569-
570- queued_proposals = list(self.target_branch.getMergeQueue())
571- queue_order = [proposal.source_branch.unique_name
572- for proposal in queued_proposals]
573- self.assertEqual(
574- new_queue_order, queue_order,
575- "There should be only two queued items now. "
576- "Expected %s, got %s" % (new_queue_order, queue_order))
577-
578-
579 class TestCreateCommentNotifications(TestCaseWithFactory):
580 """Test the notifications are raised at the right times."""
581
582
583=== added file 'lib/lp/code/model/tests/test_branchmergequeue.py'
584--- lib/lp/code/model/tests/test_branchmergequeue.py 1970-01-01 00:00:00 +0000
585+++ lib/lp/code/model/tests/test_branchmergequeue.py 2010-10-20 17:48:56 +0000
586@@ -0,0 +1,91 @@
587+# Copyright 2010 Canonical Ltd. This software is licensed under the
588+# GNU Affero General Public License version 3 (see the file LICENSE).
589+
590+"""Unit tests for methods of BranchMergeQueue."""
591+
592+from __future__ import with_statement
593+
594+import simplejson
595+
596+from canonical.launchpad.interfaces.lpstorm import IStore
597+from canonical.launchpad.webapp.testing import verifyObject
598+from canonical.testing.layers import DatabaseFunctionalLayer
599+from lp.code.errors import InvalidMergeQueueConfig
600+from lp.code.interfaces.branchmergequeue import IBranchMergeQueue
601+from lp.code.model.branchmergequeue import BranchMergeQueue
602+from lp.testing import (
603+ person_logged_in,
604+ TestCaseWithFactory,
605+ )
606+
607+
608+class TestBranchMergeQueueInterface(TestCaseWithFactory):
609+ """Test IBranchMergeQueue interface."""
610+
611+ layer = DatabaseFunctionalLayer
612+
613+ def test_implements_interface(self):
614+ queue = self.factory.makeBranchMergeQueue()
615+ IStore(BranchMergeQueue).add(queue)
616+ verifyObject(IBranchMergeQueue, queue)
617+
618+
619+class TestBranchMergeQueueSource(TestCaseWithFactory):
620+ """Test the methods of IBranchMergeQueueSource."""
621+
622+ layer = DatabaseFunctionalLayer
623+
624+ def test_new(self):
625+ owner = self.factory.makePerson()
626+ name = u'SooperQueue'
627+ description = u'This is Sooper Queue'
628+ config = unicode(simplejson.dumps({'test': 'make check'}))
629+
630+ queue = BranchMergeQueue.new(
631+ name, owner, owner, description, config)
632+
633+ self.assertEqual(queue.name, name)
634+ self.assertEqual(queue.owner, owner)
635+ self.assertEqual(queue.registrant, owner)
636+ self.assertEqual(queue.description, description)
637+ self.assertEqual(queue.configuration, config)
638+
639+
640+class TestBranchMergeQueue(TestCaseWithFactory):
641+ """Test the functions of the BranchMergeQueue."""
642+
643+ layer = DatabaseFunctionalLayer
644+
645+ def test_branches(self):
646+ """Test that a merge queue can get all its managed branches."""
647+ store = IStore(BranchMergeQueue)
648+
649+ queue = self.factory.makeBranchMergeQueue()
650+ store.add(queue)
651+
652+ branch = self.factory.makeBranch()
653+ store.add(branch)
654+ with person_logged_in(branch.owner):
655+ branch.addToQueue(queue)
656+
657+ self.assertEqual(
658+ list(queue.branches),
659+ [branch])
660+
661+ def test_setMergeQueueConfig(self):
662+ """Test that the configuration is set properly."""
663+ queue = self.factory.makeBranchMergeQueue()
664+ config = unicode(simplejson.dumps({
665+ 'test': 'make test'}))
666+
667+ queue.setMergeQueueConfig(config)
668+
669+ self.assertEqual(queue.configuration, config)
670+
671+ def test_setMergeQueueConfig_invalid_json(self):
672+ """Test that invalid json can't be set as the config."""
673+ queue = self.factory.makeBranchMergeQueue()
674+ self.assertRaises(
675+ InvalidMergeQueueConfig,
676+ queue.setMergeQueueConfig,
677+ 'abc')
678
679=== modified file 'lib/lp/testing/factory.py'
680--- lib/lp/testing/factory.py 2010-10-18 23:19:16 +0000
681+++ lib/lp/testing/factory.py 2010-10-20 17:48:56 +0000
682@@ -36,6 +36,7 @@
683 from operator import isSequenceType
684 import os
685 from random import randint
686+import simplejson
687 from StringIO import StringIO
688 from textwrap import dedent
689 from threading import local
690@@ -146,6 +147,7 @@
691 PreviewDiff,
692 StaticDiff,
693 )
694+from lp.code.model.branchmergequeue import BranchMergeQueue
695 from lp.codehosting.codeimport.worker import CodeImportSourceDetails
696 from lp.hardwaredb.interfaces.hwdb import (
697 HWSubmissionFormat,
698@@ -1096,6 +1098,18 @@
699 namespace = target.getNamespace(owner)
700 return namespace.createBranch(branch_type, name, creator)
701
702+ def makeBranchMergeQueue(self):
703+ """Create a BranchMergeQueue."""
704+ name = unicode(self.getUniqueString('queue'))
705+ owner = self.makePerson()
706+ description = unicode(self.getUniqueString('queue-description'))
707+ configuration = unicode(simplejson.dumps({
708+ self.getUniqueString('key'): self.getUniqueString('value')}))
709+
710+ queue = BranchMergeQueue.new(
711+ name, owner, owner, description, configuration)
712+ return queue
713+
714 def enableDefaultStackingForProduct(self, product, branch=None):
715 """Give 'product' a default stacked-on branch.
716

Subscribers

People subscribed via source and target branches

to status/vote changes: