launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #00273
[Merge] lp:~thumper/launchpad/no-sample-data-doctest into lp:launchpad/devel
Tim Penhey has proposed merging lp:~thumper/launchpad/no-sample-data-doctest into lp:launchpad/devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
#609942 branch merge proposal doctest uses sample data
https://bugs.launchpad.net/bugs/609942
Initially I was only going to change the use of sample data in the doc test.
However on reading the test I realised it was mostly crap. Almost all of what it was talking about was tested in unit tests, and the narrative didn't really tell the reader anything about the concept.
I've pretty much gutted it, and got it to a better state where it could be expanded upon.
I also renamed the file to more match the naming convention of the files for more discover-ability.
--
https://code.launchpad.net/~thumper/launchpad/no-sample-data-doctest/+merge/30913
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~thumper/launchpad/no-sample-data-doctest into lp:launchpad/devel.
=== renamed file 'lib/lp/code/doc/branch-merge-proposals.txt' => 'lib/lp/code/doc/branchmergeproposal.txt'
--- lib/lp/code/doc/branch-merge-proposals.txt 2010-04-16 05:51:02 +0000
+++ lib/lp/code/doc/branchmergeproposal.txt 2010-07-26 04:52:48 +0000
@@ -1,569 +1,100 @@
-= Branch merge proposals =
-
-Branch merge proposals are a way to show intent of one branch to land
-code on another branch. The database object is called a merge proposal
-as it has a source branch and a target branch, but when looking at
-branch merge proposals through the UI we are looking from a particular
-context, and as such they are referred to as landing targets or landing
-candidates.
-
- Landing Targets - these are the branches (or the merge proposals) for
- which the branch that is being looked at is the source branch. It is
- called that due to the idea that the target branch is where the code
- intentds to 'land' or 'merge'.
-
- Landing Candidates - these are the branches (or the merge proposals)
- that want to land on the branch being looked at.
-
-Branch merge proposals are created by calling the `addLandingTarget`
-method on a branch. Junk branches cannot have landing targets.
-
- * The target branch and prerequisite branch (if it has one) must both
- have the same product as the source branch.
-
- * There must not already exist a branch merge proposal for the source
- branch and target branch pair.
-
-
-== Registering a landing target ==
-
-All merge proposals have to be registered by a Person.
-
- >>> from zope.component import getUtility
- >>> from lp.registry.interfaces.person import IPersonSet
- >>> from lp.code.interfaces.branchlookup import IBranchLookup
- >>> person_set = getUtility(IPersonSet)
- >>> login('test@xxxxxxxxxxxxx')
- >>> sample_person = person_set.getByEmail('test@xxxxxxxxxxxxx')
- >>> from lp.testing import time_counter
- >>> now = time_counter()
-
-In order to register a merge proposal there has to be a source branch
-and a target branch.
-
- >>> branch_lookup = getUtility(IBranchLookup)
- >>> source_branch = branch_lookup.getByUniqueName(
- ... '~name12/gnome-terminal/klingon')
- >>> target_branch = branch_lookup.getByUniqueName(
- ... '~name12/gnome-terminal/main')
- >>> scanned_branch = branch_lookup.getByUniqueName(
- ... '~name12/gnome-terminal/scanned')
-
- >>> merge_proposal = source_branch.addLandingTarget(
- ... sample_person, target_branch, prerequisite_branch=scanned_branch,
- ... date_created=now.next())
-
-This merge proposal is now listing against both the source and target
-branches.
-
- >>> for proposal in source_branch.landing_targets:
- ... print proposal.target_branch.unique_name
- ~name12/gnome-terminal/main
-
- >>> for proposal in target_branch.landing_candidates:
- ... print proposal.source_branch.unique_name
- ~name12/gnome-terminal/klingon
-
-All the code paths through `addLandingTarget` are exercised by the UnitTest
-lp.code.model.tests.test_branch.BranchAddLandingTarget.
-
-In normal project circumstances there will normally be only one landing
-target registered for any given branch. Sometimes there may be two or more
-specified but that would be in the situation where a single patch or fix
-is going to land on multiple supported branches. It is often desirable to
-see the landing target information even after the merge has taken place.
-
-In comparison though, a trunk branch will have a significant number of
-merge candidates. From the target branch point of view, it is normally
-only the source branches where the proposals are not in one of the
-terminal states (merged, rejected, superseded) that are of interest.
-The landing_candidates only returns unmerged proposals, with the most
-recent branch merge proposals first.
-
- >>> proposal = factory.makeBranchMergeProposal(
- ... target_branch=target_branch, prerequisite_branch=scanned_branch)
- >>> proposal = proposal.resubmit(proposal.registrant)
- >>> proposal.rejectBranch(sample_person, 'some-revision')
- >>> from lp.code.enums import BranchMergeProposalStatus
- >>> proposal = factory.makeBranchMergeProposal(
- ... target_branch=target_branch, prerequisite_branch=scanned_branch,
- ... set_state=BranchMergeProposalStatus.MERGED)
-
- >>> branch = branch_lookup.getByUniqueName(
- ... '~launchpad/gnome-terminal/launchpad')
- >>> proposal = branch.addLandingTarget(
- ... sample_person, target_branch, prerequisite_branch=scanned_branch,
- ... date_created=now.next())
-
- >>> flush_database_updates()
- >>> from lp.code.model.branchmergeproposal import (
- ... BranchMergeProposal)
- >>> # Need to specify orderby - default order is creation date
- >>> # and objects created in the same transaction have identical
- >>> # creation dates
- >>> for proposal in BranchMergeProposal.selectBy(
- ... target_branch=target_branch, orderBy=['id']):
- ... print proposal.queue_status.title
- Work in progress
- Superseded
- Rejected
- Merged
- Work in progress
-
- >>> for proposal in target_branch.landing_candidates:
- ... print proposal.queue_status.title
- Work in progress
- Work in progress
-
- >>> for proposal in BranchMergeProposal.selectBy(
- ... prerequisite_branch=scanned_branch, orderBy=['id']):
- ... print proposal.queue_status.title
- Work in progress
- Superseded
- Rejected
- Merged
- Work in progress
-
- >>> for proposal in scanned_branch.dependent_branches:
- ... print proposal.queue_status.title
- Work in progress
- Work in progress
-
-
-== Merge proposal states ==
-
-A branch merge proposal can be in one of several states:
-
- >>> for state in BranchMergeProposalStatus:
- ... print state.title
- Work in progress
- Needs review
- Approved
- Rejected
- Merged
- Code failed to merge
- Queued
- Superseded
-
-When a merge proposal is initially created, it is in the "Work in
-progress" state. The only state that is a final state is "Merged".
-
- >>> source_branch = factory.makeProductBranch(owner=sample_person)
- >>> # Needs to be the same product to merge into.
- >>> target_branch = factory.makeProductBranch(
- ... owner=sample_person, product=source_branch.product)
- >>> proposal = source_branch.addLandingTarget(
- ... sample_person, target_branch, date_created=now.next())
- >>> print proposal.queue_status.title
- Work in progress
-
-Adjusting the state is done through methods on the merge proposal.
-
- >>> print proposal.date_review_requested
- None
- >>> proposal.requestReview()
- >>> print proposal.queue_status.title
- Needs review
-
-When requesting a review, a timestamp is also recorded.
-
- >>> proposal.date_review_requested is not None
- True
-
-Directly setting the queue_status is not possible.
-
- >>> proposal.queue_status = BranchMergeProposalStatus.CODE_APPROVED
- Traceback (most recent call last):
- ...
- ForbiddenAttribute: ('queue_status', <BranchMergeProposal at ...>)
-
-
-=== Approving or rejecting code ===
-
-Once code has been reviewed the result of the code review can be
-recorded either as approved or not approved.
-
-There is a limited number of people that are able to make code as
-approved or not. By default the owner of the target branch can sign-off
-code. If the target branch has specified a review team, then we use that
-to control who can sign-off code.
-
-Initially both the reviewer and date_reviewed for a merge proposal
-are not set.
-
- >>> print proposal.reviewer
- None
- >>> print proposal.date_reviewed
- None
-
-Since Sample Person is the owner of the target branch they can authorise
-their own code.
-
-When approving code the revision id is recorded as the last approved
-revision. This is to record that the reviewer has approved up to a
-particular revision, and that subsequent revsions added by the branch
-author have not been approved.
-
- >>> def add_some_revisions_to_branch(branch, factory, branch_lookup):
- ... # Revisions can only be added by the scanner, so switch dbuser.
- ... from canonical.database.sqlbase import commit
- ... from canonical.testing import LaunchpadZopelessLayer
- ... from canonical.config import config
- ... branch_unique_name = branch.unique_name
- ... launchpad_dbuser = config.launchpad.dbuser
- ... commit()
- ... LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
- ... factory.makeRevisionsForBranch(branch)
- ... commit()
- ... LaunchpadZopelessLayer.switchDbUser(launchpad_dbuser)
- ... return branch_lookup.getByUniqueName(branch_unique_name)
- >>> source_branch = add_some_revisions_to_branch(
- ... source_branch, factory, branch_lookup)
- >>> proposal = source_branch.landing_targets[0]
- >>> source_branch.revision_count
- 5
-
- >>> # Reget the sample person as we shouldn't be reusing objects across
- >>> # transaction boundaries.
- >>> sample_person = person_set.getByEmail('test@xxxxxxxxxxxxx')
- >>> tip = source_branch.getTipRevision()
- >>> proposal.approveBranch(sample_person, tip.revision_id)
- >>> print proposal.reviewer.displayname
- Sample Person
- >>> proposal.date_reviewed is not None
- True
- >>> print proposal.queue_status.title
- Approved
- >>> proposal.reviewed_revision_id == tip.revision_id
- True
-
-Code that has been accepted can also be set to rejected. Rejected
-branches also record the revision that was rejected.
-
- >>> proposal.rejectBranch(sample_person, tip.revision_id)
- >>> print proposal.queue_status.title
- Rejected
- >>> proposal.reviewed_revision_id == tip.revision_id
- True
-
-Proposals that are `Approved` can be moved back into the
-`Needs review` state.
-
- >>> proposal = proposal.resubmit(sample_person)
- >>> proposal.approveBranch(sample_person, tip.revision_id)
- >>> print proposal.queue_status.title
- Approved
- >>> proposal.requestReview()
- >>> print proposal.queue_status.title
- Needs review
-
-Branches that had been approved that have subsequently been set back into the
-needs review state have their reviewed revision cleared.
-
- >>> print proposal.reviewed_revision_id
- None
-
-If the target branch has specified a specific reviewer, then either the owner
-of the target branch or the reviewer is able to approve or reject the
-proposal.
-
- >>> eric_the_reviewer = factory.makePerson(name="eric-reviewer")
- >>> target_branch = proposal.target_branch
- >>> target_branch.reviewer = eric_the_reviewer
- >>> proposal.rejectBranch(sample_person, tip.revision_id)
- >>> print proposal.queue_status.title
- Rejected
- >>> proposal.approveBranch(eric_the_reviewer, tip.revision_id)
- >>> print proposal.queue_status.title
- Approved
-
-
-== Queueing up a branch that has been approved ==
-
-A branch proposal to merge that has been approved can be added to the
-merge queue for the target branch. The proposal to merge will be given
-a queue position which has no significance other than defining the
-order in the queue. There can be no assumptions on the actual value
-of the queue position as any integer value is valid, and a smaller
-integer value comes before a larger integer value in the queue.
-
-When a proposal is queued, the person that queues up the branch is
-recorded, along with the revision_id of the tip revision. This revision_id
-is the revision that will be merged into the target branch.
-
- >>> proposal.enqueue(sample_person, tip.revision_id)
- >>> print proposal.queue_status.title
- Queued
- >>> print proposal.queuer.displayname
- Sample Person
- >>> proposal.queued_revision_id == tip.revision_id
- True
- >>> proposal.queue_position is not None
- True
-
-
-== Removing a branch from the merge queue ==
-
-A branch proposal to merge that has been queued can also be removed
-from the queue. Doing so moves the state of the proposal back to
-Approved.
-
- >>> proposal.dequeue()
- >>> from canonical.launchpad.ftests import syncUpdate
- >>> syncUpdate(proposal)
- >>> print proposal.queue_status.title
- Approved
- >>> print proposal.queue_position
- None
- >>> print proposal.queuer
- None
-
-
-== Transitioning through code approved when queueing ==
-
-If the proposal to merge was not in the approved state before attempting
-to queue the branch, then an implicit approval is attempted. If the user
-queueing the branch is not a reviewer, an exception is raised.
-
- >>> proposal.requestReview()
- >>> print proposal.queue_status.title
- Needs review
-
- >>> random_user = factory.makePerson()
- >>> proposal.enqueue(random_user, tip.revision_id)
- Traceback (most recent call last):
- UserNotBranchReviewer
-
- >>> proposal.enqueue(eric_the_reviewer, tip.revision_id)
- >>> print proposal.queue_status.title
- Queued
- >>> print proposal.queuer.name
- eric-reviewer
- >>> print proposal.reviewer.name
- eric-reviewer
-
-
-== Marking as failed to merge ==
-
-Anyone that has rights to edit the merge proposal can say that the
-merge failed.
-
- >>> proposal.setStatus(BranchMergeProposalStatus.MERGE_FAILED)
- >>> print proposal.queue_status.title
- Code failed to merge
-
-Proposals that are in the "Code failed to merge" state can be set back
-to any other state.
-
- >>> proposal.setAsWorkInProgress()
- >>> print proposal.queue_status.title
- Work in progress
-
-
-== Getting a list of previous merge targets ==
-
-In order to make it easier to propose branches to merge on similar
-branches, there is a method on the branch set that returns the branches that a
-user has previously targeted for merging.
-
- >>> collection = branch.target.collection.targetedBy(sample_person)
- >>> collection = collection.visibleByUser(sample_person)
- >>> branches = collection.getBranches().config(distinct=True)
- >>> for branch in branches:
- ... print branch.unique_name
- ~name12/gnome-terminal/main
-
-
-== Marking as merged ==
-
-A merge proposal may be marked as merged by calling the `markAsMerged` method
-on the `BranchMergeProposal`.
-
-If a revision number is supplied, the method looks for a `BranchRevision` in
-the target branch that matches. If one is found then the revision date from
-the associated `Revision` is used as the merge date. This allows branches
-that are not necessarily available to launchpad to manually mark the merge
-proposals as merged.
-
-The user that marked the branch as merged can also be recorded.
-
- >>> branch = branch_lookup.getByUniqueName(
- ... '~launchpad/gnome-terminal/launchpad')
- >>> merge_proposal = branch.landing_targets[0]
- >>> reporter = person_set.getByName('mark')
- >>> merge_proposal.markAsMerged(1234, now.next(), reporter)
- >>> merge_proposal.merged_revno
- 1234
- >>> print merge_proposal.merge_reporter.displayname
- Mark Shuttleworth
- >>> print merge_proposal.queue_status.title
- Merged
- >>> flush_database_updates()
-
-The merged proposal is no longer listed amongst the target's candidates.
-
- >>> print merge_proposal.source_branch.unique_name
- ~launchpad/gnome-terminal/launchpad
- >>> for proposal in merge_proposal.target_branch.landing_candidates:
- ... print proposal.source_branch.unique_name
- ~name12/gnome-terminal/klingon
-
-But the merge proposal is still listed in the source's targets.
-
- >>> for proposal in merge_proposal.source_branch.landing_targets:
- ... print proposal.target_branch.unique_name
- ~name12/gnome-terminal/main
-
-The firefox branches are the branches that are attached to a product
-and have revisions (well, a revision).
-
- >>> source_branch = branch_lookup.getByUniqueName(
- ... '~mark/firefox/release-0.9')
- >>> target_branch = branch_lookup.getByUniqueName(
- ... '~mark/firefox/release-0.9.2')
- >>> merge_proposal = source_branch.addLandingTarget(
- ... sample_person, target_branch, date_created=now.next())
-
-The target branch has a revision 1, so the merge time is taken form
-that revision.
-
- >>> history = list(target_branch.revision_history)
- >>> branch_revision = history[0]
- >>> branch_revision.sequence
- 1
- >>> print branch_revision.revision.revision_date
- 2005-03-09 15:40:00+00:00
-
- >>> merge_proposal.markAsMerged(1, now.next(), reporter)
- >>> print merge_proposal.date_merged
- 2005-03-09 15:40:00+00:00
- >>> print merge_proposal.merge_reporter.displayname
- Mark Shuttleworth
-
-
-== Interfaces ==
-
-The BranchMergeProposal must implement the IBranchMergeProposal interface.
-
- >>> from canonical.launchpad.webapp.testing import verifyObject
- >>> from lp.code.interfaces.branchmergeproposal import (
- ... IBranchMergeProposal)
- >>> verifyObject(IBranchMergeProposal, merge_proposal)
- True
-
-
-== Canonical URL ==
-
-The URL of a branch merge proposal is based on the source branch.
-In order to keep the URL managable, the database ID of the merge
-proposal is used.
-
- >>> login('test@xxxxxxxxxxxxx')
- >>> from canonical.launchpad.webapp import canonical_url
- >>> merge_proposal = source_branch.landing_targets[0]
- >>> url = canonical_url(merge_proposal)
- >>> url[url.rfind('/')+1:] == str(merge_proposal.id)
- True
- >>> print url
- http://code.launchpad.dev/~mark/firefox/release-0.9/+merge/...
-
-
-== Deleting merge proposals ==
-
-Deleting merge proposals is done using the deleteProposal() method on
-the merge proposal itself. The `deleteProposal` method is protected by
-launchpad.Edit, which restricts access to the owner of the source
-branch, the owner of the target branch, or the registrant (and LP
-admins).
-
- >>> source_branch.landing_targets.count()
- 1
- >>> merge_proposal = source_branch.landing_targets[0]
- >>> login('no-priv@xxxxxxxxxxxxx')
- >>> merge_proposal.deleteProposal()
- Traceback (most recent call last):
- ...
- Unauthorized: (<BranchMergeProposal ...>,
- 'deleteProposal', 'launchpad.Edit')
-
-Sample Person is the registrant, so they can delete it.
-
- >>> login('test@xxxxxxxxxxxxx')
- >>> merge_proposal.deleteProposal()
- >>> source_branch.landing_targets.count()
- 0
-
-
-== Resubmitting merge proposals ==
-
-If a proposal has been rejected, the proposal can be resubmitted
-(normally after the branch author has fixed the problems). When a
-proposal is resubmitted the original proposal is marked as SUPERSEDED
-and a new proposal is created which has the same details as the
-original. The new proposal has an attribute called
-`supersedes` which is the proposal it superseded (i.e. the
-original proposal). The original proposal also has an attribute
-`superseded_by` which is the proposal that supersedes it (i.e. the
-new proposal).
-
- >>> proposal = factory.makeBranchMergeProposal(registrant=sample_person)
- >>> proposal.rejectBranch(proposal.target_branch.owner, 'some-revision')
- >>> print proposal.queue_status.title
- Rejected
-
- >>> new_proposal = proposal.resubmit(sample_person)
- >>> print new_proposal.queue_status.title
- Needs review
- >>> new_proposal == proposal
- False
- >>> new_proposal.supersedes == proposal
- True
- >>> flush_database_updates()
- >>> proposal.superseded_by == new_proposal
- True
-
-If the new proposal is deleted, the old proposal stays in the
-superseded state, but no longer has a `superseded_by` proposal.
-
- >>> new_proposal.deleteProposal()
- >>> print proposal.queue_status.title
- Superseded
- >>> print proposal.superseded_by
- None
-
-If the old proposal is deleted, the new proposal no longer shows
-the supersedes.
-
- >>> proposal = factory.makeBranchMergeProposal(registrant=sample_person)
- >>> new_proposal = proposal.resubmit(sample_person)
- >>> new_proposal.supersedes == proposal
- True
-
- >>> flush_database_updates()
- >>> proposal.deleteProposal()
- >>> print new_proposal.supersedes
- None
-
-If there is a chain of superseded proposals, and the one in the middle
-is deleted, then the chain is just made shorter.
-
- >>> original = factory.makeBranchMergeProposal(registrant=sample_person)
- >>> middle = original.resubmit(sample_person)
- >>> last = middle.resubmit(sample_person)
- >>> flush_database_updates()
-
- >>> original.superseded_by == middle
- True
- >>> middle.supersedes == original
- True
- >>> middle.superseded_by == last
- True
- >>> last.supersedes == middle
- True
-
- >>> middle.deleteProposal()
-
- >>> original.superseded_by == last
- True
- >>> last.supersedes == original
- True
+Branch merge proposals
+======================
+
+The purpose of branch merge proposals are to indicate that the one branch
+should be merged into another branch.
+
+The branch to be merged is referred to as the "source branch", and the branch
+that is being merged into is referred to as the "target branch".
+
+In the early phases of development of this feature there was often a mixing of
+terms where the term "merge proposal" was used interchangably with "code
+review" which caused some confusion.
+
+- **Merge Proposal:** indicates that one branch should be merged with another.
+- **Code Review:** a discussion that takes place about the changes that would
+ occur should the proposed merge happen
+
+A merge proposal may have a code reivew, but a code review always happens with
+respect to a merge proposal.
+
+
+Other Terms
+-----------
+
+Landing Targets
+ These are the merge proposals related to the branch for which the branch
+ that is being looked at is the source branch. It is called that due to the
+ idea that the target branch is where the code intentds to 'land' or 'merge'.
+
+Landing Candidates
+ These are the merge proposals that indicate the intent to merge with the
+ branch being looked at.
+
+
+Creating a Merge Proposal
+-------------------------
+
+All merge proposals are created from the source branch using a method called
+``addLandingTarget``.
+
+ >>> fooix = factory.makeProduct(name='fooix')
+ >>> source_branch = factory.makeProductBranch(product=fooix)
+ >>> target_branch = factory.makeProductBranch(product=fooix)
+ >>> merge_proposal = source_branch.addLandingTarget(
+ ... registrant=source_branch.owner,
+ ... target_branch=target_branch)
+
+The bare minimum that needs to be specified is the person that is proposing
+the merge, the ``registrant``, and the branch that the registrant wants the
+branch to be merged into, the ``target_branch``.
+
+There are other optional parameters to initialize other merge proposal
+attributes such as the description, commit message or requested reviewers.
+
+It is considered good form to set at least one of the commit message or
+description. For very simple branches a commit message might be enough, but
+for non-trivial branches a description is often needed to describe the intent
+of the changes.
+
+
+States of a Merge Proposal
+--------------------------
+
+During the life time of a merge proposal it will go through many states.
+
+Work in Progress
+ The source branch is intended to be merged into the target, but the work has
+ not yet been fully completed.
+
+Needs Review
+ The work has been completed to the satisfaction of the branch owner, or at
+ least some form of review of the change is being requested.
+
+Approved
+ The reviewer is happy with the change.
+
+Rejected
+ The reviews is not happy with the change.
+
+Merged
+ The source branch has been merged into the target. The branch scanner also
+ sets the ``merge_revno`` of the merge proposal to indicate which revision
+ number on the target branch has this merge in it.
+
+Superseded
+ The intent of superseded proposals has changed somewhat over time, and needs
+ some rework (bugs 383352, 397444, 400030, 488544)
+
+
+There are also some other states that are not yet in general use:
+
+Queued
+ The merge proposal is queued for merging. The proposal will be part of some
+ merge queue which a person or script (like tarmac) will process and do the
+ merges.
+
+Merge Failed
+ A script tried to merge the branch but it either failed to merge cleanly
+ (had conflicts) or failed some tests as defined by the script. There is the
+ facility to add a log file containing the failures here.
=== modified file 'lib/lp/code/model/tests/test_branchmergeproposals.py'
--- lib/lp/code/model/tests/test_branchmergeproposals.py 2010-05-27 01:34:14 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposals.py 2010-07-26 04:52:48 +0000
@@ -25,6 +25,7 @@
from canonical.launchpad.interfaces import IPrivacy
from canonical.launchpad.interfaces.message import IMessageJob
+from canonical.launchpad.webapp import canonical_url
from canonical.launchpad.webapp.testing import verifyObject
from lp.code.errors import (
BadStateTransition, WrongBranchMergeProposal)
@@ -64,6 +65,27 @@
verifyObject(IBranchMergeProposal, bmp)
+class TestBranchMergeProposalCanonicalUrl(TestCaseWithFactory):
+ """Tests canonical_url for merge proposals."""
+
+ layer = DatabaseFunctionalLayer
+
+ def test_BranchMergeProposal_canoncial_url_base(self):
+ # The URL for a merge proposal starts with the source branch.
+ bmp = self.factory.makeBranchMergeProposal()
+ url = canonical_url(bmp)
+ source_branch_url = canonical_url(bmp.source_branch)
+ self.assertTrue(url.startswith(source_branch_url))
+
+ def test_BranchMergeProposal_canoncial_url_rest(self):
+ # The rest of the URL for a merge proposal is +merge followed by the db id.
+ bmp = self.factory.makeBranchMergeProposal()
+ url = canonical_url(bmp)
+ source_branch_url = canonical_url(bmp.source_branch)
+ rest = url[len(source_branch_url):]
+ self.assertEqual('/+merge/%s' % bmp.id, rest)
+
+
class TestBranchMergeProposalPrivacy(TestCaseWithFactory):
"""Ensure that BranchMergeProposal implements privacy."""
=== modified file 'lib/lp/code/tests/test_doc.py'
--- lib/lp/code/tests/test_doc.py 2010-01-13 02:13:19 +0000
+++ lib/lp/code/tests/test_doc.py 2010-07-26 04:52:48 +0000
@@ -45,12 +45,6 @@
tearDown=zopelessLaunchpadSecurityTearDown,
layer=LaunchpadZopelessLayer,
),
- 'branch-merge-proposals.txt': LayeredDocFileSuite(
- '../doc/branch-merge-proposals.txt',
- setUp=zopelessLaunchpadSecuritySetUp,
- tearDown=zopelessLaunchpadSecurityTearDown,
- layer=LaunchpadZopelessLayer,
- ),
'revision.txt': LayeredDocFileSuite(
'../doc/revision.txt',
setUp=branchscannerSetUp, tearDown=tearDown,