Merge lp:~julian-edwards/launchpad/publish-copy-archives-bug-520520-publish-distro into lp:launchpad/db-devel

Proposed by Julian Edwards
Status: Rejected
Rejected by: Julian Edwards
Proposed branch: lp:~julian-edwards/launchpad/publish-copy-archives-bug-520520-publish-distro
Merge into: lp:launchpad/db-devel
Diff against target: 1553 lines (+343/-460)
33 files modified
lib/canonical/launchpad/doc/product-update-remote-product.txt (+1/-6)
lib/lp/bugs/doc/bugzilla-import.txt (+2/-6)
lib/lp/bugs/doc/externalbugtracker.txt (+3/-27)
lib/lp/code/browser/branch.py (+3/-3)
lib/lp/code/doc/xmlrpc-branch-puller.txt (+1/-16)
lib/lp/code/enums.py (+1/-1)
lib/lp/code/interfaces/branchpuller.py (+0/-6)
lib/lp/code/interfaces/codehosting.py (+0/-14)
lib/lp/code/model/branchpuller.py (+0/-22)
lib/lp/code/model/tests/test_branchjob.py (+5/-24)
lib/lp/code/model/tests/test_branchpuller.py (+0/-82)
lib/lp/code/model/tests/test_codeimport.py (+9/-0)
lib/lp/code/model/tests/test_codeimportjob.py (+2/-1)
lib/lp/code/xmlrpc/codehosting.py (+1/-43)
lib/lp/code/xmlrpc/tests/test_codehosting.py (+0/-100)
lib/lp/codehosting/codeimport/tests/test_worker.py (+25/-0)
lib/lp/codehosting/codeimport/worker.py (+15/-5)
lib/lp/codehosting/inmemory.py (+0/-23)
lib/lp/soyuz/scripts/publishdistro.py (+19/-6)
lib/lp/soyuz/scripts/tests/test_publishdistro.py (+52/-1)
lib/lp/testing/faketransaction.py (+35/-0)
lib/lp/translations/browser/poexportrequest.py (+35/-0)
lib/lp/translations/browser/tests/test_baseexportview.py (+68/-2)
lib/lp/translations/doc/distroseries-translations-copy.txt (+2/-7)
lib/lp/translations/doc/gettext-check-messages.txt (+14/-22)
lib/lp/translations/doc/poexport-queue.txt (+6/-4)
lib/lp/translations/doc/poexport-request-productseries.txt (+2/-5)
lib/lp/translations/doc/poexport-request.txt (+3/-6)
lib/lp/translations/doc/poimport.txt (+2/-10)
lib/lp/translations/interfaces/poexportrequest.py (+7/-1)
lib/lp/translations/model/poexportrequest.py (+23/-7)
lib/lp/translations/scripts/tests/test_copy_distroseries_translations.py (+2/-10)
lib/lp/translations/templates/translations-export.pt (+5/-0)
To merge this branch: bzr merge lp:~julian-edwards/launchpad/publish-copy-archives-bug-520520-publish-distro
Reviewer Review Type Date Requested Status
Canonical Launchpad Engineering Pending
Review via email: mp+19987@code.launchpad.net

Commit message

Add support for publishing of copy archives to the publish-distro script.

To post a comment you must log in.
Revision history for this message
Julian Edwards (julian-edwards) wrote :

This is a simple branch that adds support for publishing of copy archives to
the publish-distro script.

It adds the option --copy-archive which will cause all archives with
ArchivePurpose.COPY to be published, provided their "publish" flag is set.

There's also a simple test case added.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/doc/product-update-remote-product.txt'
--- lib/canonical/launchpad/doc/product-update-remote-product.txt 2009-06-12 16:36:02 +0000
+++ lib/canonical/launchpad/doc/product-update-remote-product.txt 2010-02-24 10:59:32 +0000
@@ -17,12 +17,7 @@
17 ... FakeLogger, QuietFakeLogger)17 ... FakeLogger, QuietFakeLogger)
18 >>> from canonical.launchpad.scripts.updateremoteproduct import (18 >>> from canonical.launchpad.scripts.updateremoteproduct import (
19 ... RemoteProductUpdater)19 ... RemoteProductUpdater)
20 >>> class FakeTransaction:20 >>> from lp.testing.faketransaction import FakeTransaction
21 ... def __init__(self, log_calls=False):
22 ... self.log_calls = log_calls
23 ... def commit(self):
24 ... if self.log_calls:
25 ... print "COMMIT"
26 >>> updater = RemoteProductUpdater(FakeTransaction(), QuietFakeLogger())21 >>> updater = RemoteProductUpdater(FakeTransaction(), QuietFakeLogger())
2722
2823
2924
=== modified file 'lib/lp/bugs/doc/bugzilla-import.txt'
--- lib/lp/bugs/doc/bugzilla-import.txt 2009-06-12 16:36:02 +0000
+++ lib/lp/bugs/doc/bugzilla-import.txt 2010-02-24 10:59:32 +0000
@@ -522,12 +522,8 @@
522The Bugzilla duplicate bugs table can be used to mark the522The Bugzilla duplicate bugs table can be used to mark the
523corresponding Launchpad bugs as duplicates too:523corresponding Launchpad bugs as duplicates too:
524524
525 >>> class FakeTransactionManager:525 >>> from lp.testing.faketransaction import FakeTransaction
526 ... def begin(self):526 >>> bz.processDuplicates(FakeTransaction())
527 ... pass
528 ... abort = commit = begin
529 ...
530 >>> bz.processDuplicates(FakeTransactionManager())
531527
532Now check that the bugs have been marked duplicate:528Now check that the bugs have been marked duplicate:
533529
534530
=== modified file 'lib/lp/bugs/doc/externalbugtracker.txt'
--- lib/lp/bugs/doc/externalbugtracker.txt 2010-02-19 12:05:10 +0000
+++ lib/lp/bugs/doc/externalbugtracker.txt 2010-02-24 10:59:32 +0000
@@ -27,17 +27,9 @@
27 ... print "initializeRemoteBugDB() called: %r" % (27 ... print "initializeRemoteBugDB() called: %r" % (
28 ... remote_bug_ids, )28 ... remote_bug_ids, )
2929
30 >>> class FakeTransaction:30 >>> from lp.testing.faketransaction import FakeTransaction
31 ... """Transaction class to track transaction boundaries."""
32 ... def commit(self):
33 ... print "COMMIT"
34 ... def abort(self):
35 ... print "ABORT"
36 ... def begin(self):
37 ... print "BEGIN"
38
39 >>> from lp.bugs.scripts.checkwatches import BugWatchUpdater31 >>> from lp.bugs.scripts.checkwatches import BugWatchUpdater
40 >>> bug_watch_updater = BugWatchUpdater(FakeTransaction())32 >>> bug_watch_updater = BugWatchUpdater(FakeTransaction(log_calls=True))
41 >>> bug_watch_updater.updateBugWatches(33 >>> bug_watch_updater.updateBugWatches(
42 ... InitializingExternalBugTracker(), [])34 ... InitializingExternalBugTracker(), [])
43 COMMIT35 COMMIT
@@ -305,7 +297,7 @@
305and the remote system is never asked about product information.297and the remote system is never asked about product information.
306298
307 >>> bug_watch_updater = BugWatchUpdater(299 >>> bug_watch_updater = BugWatchUpdater(
308 ... FakeTransaction(), syncable_gnome_products=[])300 ... FakeTransaction(log_calls=True), syncable_gnome_products=[])
309301
310 >>> trackers_and_watches = get_trackers_and_watches(302 >>> trackers_and_watches = get_trackers_and_watches(
311 ... gnome_bugzilla, bug_watches)303 ... gnome_bugzilla, bug_watches)
@@ -506,16 +498,12 @@
506 ... external_bugtracker, bug_watches)498 ... external_bugtracker, bug_watches)
507 COMMIT499 COMMIT
508 initializeRemoteBugDB() called: [u'1', u'2', u'3', u'4']500 initializeRemoteBugDB() called: [u'1', u'2', u'3', u'4']
509 BEGIN
510 getRemoteStatus() called: u'1'501 getRemoteStatus() called: u'1'
511 COMMIT502 COMMIT
512 BEGIN
513 getRemoteStatus() called: u'2'503 getRemoteStatus() called: u'2'
514 COMMIT504 COMMIT
515 BEGIN
516 getRemoteStatus() called: u'3'505 getRemoteStatus() called: u'3'
517 COMMIT506 COMMIT
518 BEGIN
519 getRemoteStatus() called: u'4'507 getRemoteStatus() called: u'4'
520 COMMIT508 COMMIT
521509
@@ -549,14 +537,10 @@
549 last_checked: 2007-03-17 15:...:...537 last_checked: 2007-03-17 15:...:...
550 getModifiedRemoteBugs() called: [u'1', u'2', u'3', u'4']538 getModifiedRemoteBugs() called: [u'1', u'2', u'3', u'4']
551 initializeRemoteBugDB() called: [u'1', u'4']539 initializeRemoteBugDB() called: [u'1', u'4']
552 BEGIN
553 getRemoteStatus() called: u'1'540 getRemoteStatus() called: u'1'
554 COMMIT541 COMMIT
555 BEGIN
556 getRemoteStatus() called: u'4'542 getRemoteStatus() called: u'4'
557 COMMIT543 COMMIT
558 BEGIN
559 BEGIN
560544
561The bug watches that are deemed as not being modified are still marked545The bug watches that are deemed as not being modified are still marked
562as being checked.546as being checked.
@@ -604,16 +588,12 @@
604 last_checked: 2007-03-16 15:...:...588 last_checked: 2007-03-16 15:...:...
605 getModifiedRemoteBugs() called: [u'1', u'4']589 getModifiedRemoteBugs() called: [u'1', u'4']
606 initializeRemoteBugDB() called: [u'1', u'2', u'3', u'4']590 initializeRemoteBugDB() called: [u'1', u'2', u'3', u'4']
607 BEGIN
608 getRemoteStatus() called: u'1'591 getRemoteStatus() called: u'1'
609 COMMIT592 COMMIT
610 BEGIN
611 getRemoteStatus() called: u'2'593 getRemoteStatus() called: u'2'
612 COMMIT594 COMMIT
613 BEGIN
614 getRemoteStatus() called: u'3'595 getRemoteStatus() called: u'3'
615 COMMIT596 COMMIT
616 BEGIN
617 getRemoteStatus() called: u'4'597 getRemoteStatus() called: u'4'
618 COMMIT598 COMMIT
619599
@@ -631,16 +611,12 @@
631 ... TimeUnknownExternalBugTracker(), bug_watches)611 ... TimeUnknownExternalBugTracker(), bug_watches)
632 COMMIT612 COMMIT
633 initializeRemoteBugDB() called: [u'1', u'2', u'3', u'4']613 initializeRemoteBugDB() called: [u'1', u'2', u'3', u'4']
634 BEGIN
635 getRemoteStatus() called: u'1'614 getRemoteStatus() called: u'1'
636 COMMIT615 COMMIT
637 BEGIN
638 getRemoteStatus() called: u'2'616 getRemoteStatus() called: u'2'
639 COMMIT617 COMMIT
640 BEGIN
641 getRemoteStatus() called: u'3'618 getRemoteStatus() called: u'3'
642 COMMIT619 COMMIT
643 BEGIN
644 getRemoteStatus() called: u'4'620 getRemoteStatus() called: u'4'
645 COMMIT621 COMMIT
646622
647623
=== modified file 'lib/lp/code/browser/branch.py'
--- lib/lp/code/browser/branch.py 2010-02-24 10:18:16 +0000
+++ lib/lp/code/browser/branch.py 2010-02-24 10:59:32 +0000
@@ -514,10 +514,10 @@
514514
515 def iconForCodeImportResultStatus(self, status):515 def iconForCodeImportResultStatus(self, status):
516 """The icon to represent the `CodeImportResultStatus` `status`."""516 """The icon to represent the `CodeImportResultStatus` `status`."""
517 if status in CodeImportResultStatus.successes:517 if status == CodeImportResultStatus.SUCCESS_PARTIAL:
518 return "/@@/yes-gray"
519 elif status in CodeImportResultStatus.successes:
518 return "/@@/yes"520 return "/@@/yes"
519 elif status == CodeImportResultStatus.SUCCESS_PARTIAL:
520 return "/@@/yes-gray"
521 else:521 else:
522 return "/@@/no"522 return "/@@/no"
523523
524524
=== modified file 'lib/lp/code/doc/xmlrpc-branch-puller.txt'
--- lib/lp/code/doc/xmlrpc-branch-puller.txt 2009-10-22 11:55:51 +0000
+++ lib/lp/code/doc/xmlrpc-branch-puller.txt 2010-02-24 10:59:32 +0000
@@ -28,19 +28,4 @@
28 True28 True
2929
30The IBranchPuller interface defines some methods, for which see the unit30The IBranchPuller interface defines some methods, for which see the unit
31tests. To allow a minimal test here, we call getBranchPullQueue,31tests.
32which will return an empty list.
33
34 >>> from lp.code.enums import BranchType
35 >>> branch_puller.getBranchPullQueue(BranchType.HOSTED.name)
36 []
37
38This remains true when it is accessed over XMLRPC.
39
40 >>> import xmlrpclib
41 >>> from canonical.functional import XMLRPCTestTransport
42 >>> puller = xmlrpclib.ServerProxy(
43 ... 'http://xmlrpc-private.launchpad.dev:8087/branch_puller',
44 ... transport=XMLRPCTestTransport())
45 >>> puller.getBranchPullQueue(BranchType.HOSTED.name)
46 []
4732
=== modified file 'lib/lp/code/enums.py'
--- lib/lp/code/enums.py 2010-02-17 04:28:48 +0000
+++ lib/lp/code/enums.py 2010-02-24 10:59:32 +0000
@@ -871,7 +871,7 @@
871 job, or the deletion of a CodeImport which had a running job.871 job, or the deletion of a CodeImport which had a running job.
872 """)872 """)
873873
874 successes = [SUCCESS, SUCCESS_NOCHANGE]874 successes = [SUCCESS, SUCCESS_NOCHANGE, SUCCESS_PARTIAL]
875875
876876
877class CodeReviewVote(DBEnumeratedType):877class CodeReviewVote(DBEnumeratedType):
878878
=== modified file 'lib/lp/code/interfaces/branchpuller.py'
--- lib/lp/code/interfaces/branchpuller.py 2009-06-30 16:56:07 +0000
+++ lib/lp/code/interfaces/branchpuller.py 2010-02-24 10:59:32 +0000
@@ -23,12 +23,6 @@
23 MIRROR_TIME_INCREMENT = Attribute(23 MIRROR_TIME_INCREMENT = Attribute(
24 "How frequently we mirror branches.")24 "How frequently we mirror branches.")
2525
26 def getPullQueue(branch_type):
27 """Return a queue of branches to mirror using the puller.
28
29 :param branch_type: A value from the `BranchType` enum.
30 """
31
32 def acquireBranchToPull():26 def acquireBranchToPull():
33 """Return a Branch to pull and mark it as mirror-started.27 """Return a Branch to pull and mark it as mirror-started.
3428
3529
=== modified file 'lib/lp/code/interfaces/codehosting.py'
--- lib/lp/code/interfaces/codehosting.py 2009-06-25 04:06:00 +0000
+++ lib/lp/code/interfaces/codehosting.py 2010-02-24 10:59:32 +0000
@@ -58,20 +58,6 @@
58 Published at 'branch_puller' on the private XML-RPC server.58 Published at 'branch_puller' on the private XML-RPC server.
59 """59 """
6060
61 def getBranchPullQueue(branch_type):
62 """Get the list of branches to be mirrored.
63
64 :param branch_type: One of 'HOSTED', 'MIRRORED', or 'IMPORTED'.
65
66 :raise UnknownBranchTypeError: if the branch type is unrecognized.
67
68 :returns: a list of (branch_id, pull_url, unique_name, default_branch)
69 4-tuples. branch_id is the database id of the branch, pull_url is
70 where to pull from, unique_name is the unique_name of the branch
71 and default_branch is the default stacked on branch for the
72 branch's target.
73 """
74
75 def acquireBranchToPull():61 def acquireBranchToPull():
76 """Return a Branch to pull and mark it as mirror-started.62 """Return a Branch to pull and mark it as mirror-started.
7763
7864
=== modified file 'lib/lp/code/model/branchpuller.py'
--- lib/lp/code/model/branchpuller.py 2009-08-04 05:14:32 +0000
+++ lib/lp/code/model/branchpuller.py 2010-02-24 10:59:32 +0000
@@ -9,17 +9,13 @@
99
10from datetime import timedelta10from datetime import timedelta
1111
12from storm.expr import LeftJoin, Join
13from zope.component import getUtility12from zope.component import getUtility
14from zope.interface import implements13from zope.interface import implements
1514
16from canonical.database.constants import UTC_NOW15from canonical.database.constants import UTC_NOW
17from lp.code.enums import BranchType16from lp.code.enums import BranchType
18from lp.code.model.branch import Branch17from lp.code.model.branch import Branch
19from lp.code.interfaces.branch import BranchTypeError
20from lp.code.interfaces.branchpuller import IBranchPuller18from lp.code.interfaces.branchpuller import IBranchPuller
21from lp.registry.model.person import Owner
22from lp.registry.model.product import Product
23from canonical.launchpad.webapp.interfaces import (19from canonical.launchpad.webapp.interfaces import (
24 IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)20 IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
2521
@@ -32,24 +28,6 @@
32 MAXIMUM_MIRROR_FAILURES = 528 MAXIMUM_MIRROR_FAILURES = 5
33 MIRROR_TIME_INCREMENT = timedelta(hours=6)29 MIRROR_TIME_INCREMENT = timedelta(hours=6)
3430
35 def getPullQueue(self, branch_type):
36 """See `IBranchPuller`."""
37 if branch_type == BranchType.REMOTE:
38 raise BranchTypeError("No pull queue for REMOTE branches.")
39 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
40 # Prejoin on owner and product to preserve existing behaviour.
41 # XXX: JonathanLange 2009-03-22 spec=package-branches: This prejoin is
42 # inappropriate in the face of package branches.
43 prejoin = store.using(
44 Branch,
45 LeftJoin(Product, Branch.product == Product.id),
46 Join(Owner, Branch.owner == Owner.id))
47 return prejoin.find(
48 Branch,
49 Branch.branch_type == branch_type,
50 Branch.next_mirror_time <= UTC_NOW).order_by(
51 Branch.next_mirror_time)
52
53 def acquireBranchToPull(self):31 def acquireBranchToPull(self):
54 """See `IBranchPuller`."""32 """See `IBranchPuller`."""
55 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)33 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
5634
=== modified file 'lib/lp/code/model/tests/test_branchjob.py'
--- lib/lp/code/model/tests/test_branchjob.py 2010-02-22 12:26:18 +0000
+++ lib/lp/code/model/tests/test_branchjob.py 2010-02-24 10:59:32 +0000
@@ -867,22 +867,6 @@
867 self.assertFalse(job.generateDiffs())867 self.assertFalse(job.generateDiffs())
868868
869869
870def all_dirs(directory):
871 """Generate all parent directories and the directory itself.
872
873 Passing 'a/b/c/d' produces ['a', 'a/b', 'a/b/c', 'a/b/c/d'].
874 """
875 if directory == '':
876 return []
877 dirs = [directory]
878 while(1):
879 head, tail = os.path.split(directory)
880 if head == '':
881 return reversed(dirs)
882 directory = head
883 dirs.append(directory)
884
885
886class TestRosettaUploadJob(TestCaseWithFactory):870class TestRosettaUploadJob(TestCaseWithFactory):
887 """Tests for RosettaUploadJob."""871 """Tests for RosettaUploadJob."""
888872
@@ -937,21 +921,18 @@
937 seen_dirs = set()921 seen_dirs = set()
938 for file_pair in files:922 for file_pair in files:
939 file_name = file_pair[0]923 file_name = file_pair[0]
940 dname, fname = os.path.split(file_name)
941 for adir in all_dirs(dname):
942 if adir in seen_dirs:
943 continue
944 self.tree.bzrdir.root_transport.mkdir(adir)
945 self.tree.add(adir)
946 seen_dirs.add(adir)
947 try:924 try:
948 file_content = file_pair[1]925 file_content = file_pair[1]
949 if file_content is None:926 if file_content is None:
950 raise IndexError # Same as if missing.927 raise IndexError # Same as if missing.
951 except IndexError:928 except IndexError:
952 file_content = self.factory.getUniqueString()929 file_content = self.factory.getUniqueString()
930 dname = os.path.dirname(file_name)
931 self.tree.bzrdir.root_transport.clone(dname).create_prefix()
953 self.tree.bzrdir.root_transport.put_bytes(file_name, file_content)932 self.tree.bzrdir.root_transport.put_bytes(file_name, file_content)
954 self.tree.add(file_name)933 if len(files) > 0:
934 self.tree.smart_add(
935 [self.tree.abspath(file_pair[0]) for file_pair in files])
955 if commit_message is None:936 if commit_message is None:
956 commit_message = self.factory.getUniqueString('commit')937 commit_message = self.factory.getUniqueString('commit')
957 revision_id = self.tree.commit(commit_message)938 revision_id = self.tree.commit(commit_message)
958939
=== modified file 'lib/lp/code/model/tests/test_branchpuller.py'
--- lib/lp/code/model/tests/test_branchpuller.py 2009-09-28 23:51:54 +0000
+++ lib/lp/code/model/tests/test_branchpuller.py 2010-02-24 10:59:32 +0000
@@ -17,7 +17,6 @@
17from canonical.database.constants import UTC_NOW17from canonical.database.constants import UTC_NOW
18from canonical.testing.layers import DatabaseFunctionalLayer18from canonical.testing.layers import DatabaseFunctionalLayer
19from lp.code.enums import BranchType19from lp.code.enums import BranchType
20from lp.code.interfaces.branch import BranchTypeError
21from lp.code.interfaces.branchpuller import IBranchPuller20from lp.code.interfaces.branchpuller import IBranchPuller
22from lp.testing import TestCaseWithFactory, login_person21from lp.testing import TestCaseWithFactory, login_person
2322
@@ -78,37 +77,6 @@
78 branch.requestMirror()77 branch.requestMirror()
79 self.assertEqual(UTC_NOW, branch.next_mirror_time)78 self.assertEqual(UTC_NOW, branch.next_mirror_time)
8079
81 def test_requestMirrorDuringPull(self):
82 """Branches can have mirrors requested while they are being mirrored.
83 If so, they should not be removed from the pull queue when the mirror
84 is complete.
85 """
86 # We run these in separate transactions so as to have the times set to
87 # different values. This is closer to what happens in production.
88 branch = self.makeAnyBranch()
89 branch.startMirroring()
90 self.assertEqual(
91 [], list(self.branch_puller.getPullQueue(branch.branch_type)))
92 branch.requestMirror()
93 self.assertEqual(
94 [branch],
95 list(self.branch_puller.getPullQueue(branch.branch_type)))
96 branch.mirrorComplete('rev1')
97 self.assertEqual(
98 [branch],
99 list(self.branch_puller.getPullQueue(branch.branch_type)))
100
101 def test_startMirroringRemovesFromPullQueue(self):
102 # Starting a mirror removes the branch from the pull queue.
103 branch = self.makeAnyBranch()
104 branch.requestMirror()
105 self.assertEqual(
106 set([branch]),
107 set(self.branch_puller.getPullQueue(branch.branch_type)))
108 branch.startMirroring()
109 self.assertEqual(
110 set(), set(self.branch_puller.getPullQueue(branch.branch_type)))
111
112 def test_mirroringResetsMirrorRequest(self):80 def test_mirroringResetsMirrorRequest(self):
113 """Mirroring branches resets their mirror request times."""81 """Mirroring branches resets their mirror request times."""
114 branch = self.makeAnyBranch()82 branch = self.makeAnyBranch()
@@ -129,44 +97,6 @@
129 self.assertEqual(1, branch.mirror_failures)97 self.assertEqual(1, branch.mirror_failures)
130 self.assertEqual(None, branch.next_mirror_time)98 self.assertEqual(None, branch.next_mirror_time)
13199
132 def test_pullQueueEmpty(self):
133 """Branches with no next_mirror_time are not in the pull queue."""
134 branch = self.makeAnyBranch()
135 self.assertIs(None, branch.next_mirror_time)
136 self.assertEqual(
137 [], list(self.branch_puller.getPullQueue(self.branch_type)))
138
139 def test_pastNextMirrorTimeInQueue(self):
140 """Branches with next_mirror_time in the past are mirrored."""
141 transaction.begin()
142 branch = self.makeAnyBranch()
143 branch.requestMirror()
144 queue = self.branch_puller.getPullQueue(branch.branch_type)
145 self.assertEqual([branch], list(queue))
146
147 def test_futureNextMirrorTimeInQueue(self):
148 """Branches with next_mirror_time in the future are not mirrored."""
149 transaction.begin()
150 branch = removeSecurityProxy(self.makeAnyBranch())
151 tomorrow = self.getNow() + timedelta(1)
152 branch.next_mirror_time = tomorrow
153 branch.syncUpdate()
154 transaction.commit()
155 self.assertEqual(
156 [], list(self.branch_puller.getPullQueue(branch.branch_type)))
157
158 def test_pullQueueOrder(self):
159 """Pull queue has the oldest mirror request times first."""
160 branches = []
161 for i in range(3):
162 branch = removeSecurityProxy(self.makeAnyBranch())
163 branch.next_mirror_time = self.getNow() - timedelta(hours=i+1)
164 branch.sync()
165 branches.append(branch)
166 self.assertEqual(
167 list(reversed(branches)),
168 list(self.branch_puller.getPullQueue(self.branch_type)))
169
170100
171class TestMirroringForMirroredBranches(TestMirroringForHostedBranches):101class TestMirroringForMirroredBranches(TestMirroringForHostedBranches):
172102
@@ -231,18 +161,6 @@
231 branch_type = BranchType.IMPORTED161 branch_type = BranchType.IMPORTED
232162
233163
234class TestRemoteBranches(TestCaseWithFactory):
235
236 layer = DatabaseFunctionalLayer
237
238 def test_raises_branch_type_error(self):
239 # getPullQueue raises `BranchTypeError` if passed BranchType.REMOTE.
240 # It's impossible to mirror remote branches, so we shouldn't even try.
241 puller = getUtility(IBranchPuller)
242 self.assertRaises(
243 BranchTypeError, puller.getPullQueue, BranchType.REMOTE)
244
245
246class AcquireBranchToPullTests:164class AcquireBranchToPullTests:
247 """Tests for acquiring branches to pull.165 """Tests for acquiring branches to pull.
248166
249167
=== modified file 'lib/lp/code/model/tests/test_codeimport.py'
--- lib/lp/code/model/tests/test_codeimport.py 2010-02-22 10:31:50 +0000
+++ lib/lp/code/model/tests/test_codeimport.py 2010-02-24 10:59:32 +0000
@@ -432,6 +432,15 @@
432 code_import, CodeImportResultStatus.SUCCESS_NOCHANGE)432 code_import, CodeImportResultStatus.SUCCESS_NOCHANGE)
433 self.assertEqual(0, code_import.consecutive_failure_count)433 self.assertEqual(0, code_import.consecutive_failure_count)
434434
435 def test_consecutive_failure_count_succeed_succeed_partial(self):
436 # A code import that has succeeded then succeeded with no changes has
437 # a consecutive_failure_count of 0.
438 code_import = self.factory.makeCodeImport()
439 self.succeedImport(code_import)
440 self.succeedImport(
441 code_import, CodeImportResultStatus.SUCCESS_NOCHANGE)
442 self.assertEqual(0, code_import.consecutive_failure_count)
443
435 def test_consecutive_failure_count_fail_fail(self):444 def test_consecutive_failure_count_fail_fail(self):
436 # A code import that has failed twice has a consecutive_failure_count445 # A code import that has failed twice has a consecutive_failure_count
437 # of 2.446 # of 2.
438447
=== modified file 'lib/lp/code/model/tests/test_codeimportjob.py'
--- lib/lp/code/model/tests/test_codeimportjob.py 2010-02-24 10:18:16 +0000
+++ lib/lp/code/model/tests/test_codeimportjob.py 2010-02-24 10:59:32 +0000
@@ -946,7 +946,8 @@
946 code_import = job.code_import946 code_import = job.code_import
947 self.assertTrue(code_import.date_last_successful is None)947 self.assertTrue(code_import.date_last_successful is None)
948 getUtility(ICodeImportJobWorkflow).finishJob(job, status, None)948 getUtility(ICodeImportJobWorkflow).finishJob(job, status, None)
949 if status in CodeImportResultStatus.successes:949 if status in [CodeImportResultStatus.SUCCESS,
950 CodeImportResultStatus.SUCCESS_NOCHANGE]:
950 self.assertTrue(code_import.date_last_successful is not None)951 self.assertTrue(code_import.date_last_successful is not None)
951 else:952 else:
952 self.assertTrue(code_import.date_last_successful is None)953 self.assertTrue(code_import.date_last_successful is None)
953954
=== modified file 'lib/lp/code/xmlrpc/codehosting.py'
--- lib/lp/code/xmlrpc/codehosting.py 2009-11-23 22:39:21 +0000
+++ lib/lp/code/xmlrpc/codehosting.py 2010-02-24 10:59:32 +0000
@@ -12,7 +12,6 @@
1212
1313
14import datetime14import datetime
15import urllib
1615
17import pytz16import pytz
1817
@@ -25,8 +24,7 @@
2524
26from canonical.launchpad.ftests import login_person, logout25from canonical.launchpad.ftests import login_person, logout
27from lp.code.enums import BranchType26from lp.code.enums import BranchType
28from lp.code.interfaces.branch import (27from lp.code.interfaces.branch import BranchCreationException
29 BranchCreationException, UnknownBranchTypeError)
30from lp.code.interfaces.branchlookup import IBranchLookup28from lp.code.interfaces.branchlookup import IBranchLookup
31from lp.code.interfaces.branchnamespace import (29from lp.code.interfaces.branchnamespace import (
32 InvalidNamespace, lookup_branch_namespace, split_unique_name)30 InvalidNamespace, lookup_branch_namespace, split_unique_name)
@@ -56,46 +54,6 @@
5654
57 implements(IBranchPuller)55 implements(IBranchPuller)
5856
59 def _getBranchPullInfo(self, branch):
60 """Return information the branch puller needs to pull this branch.
61
62 This is outside of the IBranch interface so that the authserver can
63 access the information without logging in as a particular user.
64
65 :return: (id, url, unique_name, default_stacked_on_url), where 'id'
66 is the branch database ID, 'url' is the URL to pull from,
67 'unique_name' is the `unique_name` property and
68 'default_stacked_on_url' is the URL of the branch to stack on by
69 default (normally of the form '/~foo/bar/baz'). If there is no
70 default stacked-on branch, then it's ''.
71 """
72 branch = removeSecurityProxy(branch)
73 if branch.branch_type == BranchType.REMOTE:
74 raise AssertionError(
75 'Remote branches should never be in the pull queue.')
76 default_branch = branch.target.default_stacked_on_branch
77 if default_branch is None:
78 default_branch = ''
79 elif (branch.branch_type == BranchType.MIRRORED
80 and default_branch.private):
81 default_branch = ''
82 else:
83 default_branch = '/' + default_branch.unique_name
84 return (
85 branch.id, branch.getPullURL(), branch.unique_name,
86 default_branch)
87
88 def getBranchPullQueue(self, branch_type):
89 """See `IBranchPuller`."""
90 try:
91 branch_type = BranchType.items[branch_type]
92 except KeyError:
93 raise UnknownBranchTypeError(
94 'Unknown branch type: %r' % (branch_type,))
95 branches = getUtility(branchpuller.IBranchPuller).getPullQueue(
96 branch_type)
97 return [self._getBranchPullInfo(branch) for branch in branches]
98
99 def acquireBranchToPull(self):57 def acquireBranchToPull(self):
100 """See `IBranchPuller`."""58 """See `IBranchPuller`."""
101 branch = getUtility(branchpuller.IBranchPuller).acquireBranchToPull()59 branch = getUtility(branchpuller.IBranchPuller).acquireBranchToPull()
10260
=== modified file 'lib/lp/code/xmlrpc/tests/test_codehosting.py'
--- lib/lp/code/xmlrpc/tests/test_codehosting.py 2010-02-18 03:11:03 +0000
+++ lib/lp/code/xmlrpc/tests/test_codehosting.py 2010-02-24 10:59:32 +0000
@@ -399,105 +399,6 @@
399 self.assertFaultEqual(faults.NoBranchWithID(branch_id), fault)399 self.assertFaultEqual(faults.NoBranchWithID(branch_id), fault)
400400
401401
402class BranchPullQueueTest(TestCaseWithFactory):
403 """Tests for the pull queue methods of `IBranchPuller`."""
404
405 def setUp(self):
406 super(BranchPullQueueTest, self).setUp()
407 frontend = self.frontend()
408 self.storage = frontend.getPullerEndpoint()
409 self.factory = frontend.getLaunchpadObjectFactory()
410
411 def assertBranchQueues(self, hosted, mirrored, imported):
412 expected_hosted = [
413 self.storage._getBranchPullInfo(branch) for branch in hosted]
414 expected_mirrored = [
415 self.storage._getBranchPullInfo(branch) for branch in mirrored]
416 expected_imported = [
417 self.storage._getBranchPullInfo(branch) for branch in imported]
418 self.assertEqual(
419 expected_hosted, self.storage.getBranchPullQueue('HOSTED'))
420 self.assertEqual(
421 expected_mirrored, self.storage.getBranchPullQueue('MIRRORED'))
422 self.assertEqual(
423 expected_imported, self.storage.getBranchPullQueue('IMPORTED'))
424
425 def test_pullQueuesEmpty(self):
426 """getBranchPullQueue returns an empty list when there are no branches
427 to pull.
428 """
429 self.assertBranchQueues([], [], [])
430
431 def makeBranchAndRequestMirror(self, branch_type):
432 """Make a branch of the given type and call requestMirror on it."""
433 branch = self.factory.makeAnyBranch(branch_type=branch_type)
434 branch.requestMirror()
435 # The pull queues contain branches that have next_mirror_time strictly
436 # in the past, but requestMirror sets this field to UTC_NOW, so we
437 # push the time back slightly here to get the branch to show up in the
438 # queue.
439 naked_branch = removeSecurityProxy(branch)
440 naked_branch.next_mirror_time -= datetime.timedelta(seconds=1)
441 return branch
442
443 def test_getBranchPullInfo_no_default_stacked_branch(self):
444 # If there's no default stacked branch for the project that a branch
445 # is on, then _getBranchPullInfo returns (id, url, unique_name, '').
446 branch = self.factory.makeAnyBranch()
447 info = self.storage._getBranchPullInfo(branch)
448 self.assertEqual(
449 (branch.id, branch.getPullURL(), branch.unique_name, ''), info)
450
451 def test_getBranchPullInfo_default_stacked_branch(self):
452 # If there's a default stacked branch for the project that a branch is
453 # on, then _getBranchPullInfo returns (id, url, unique_name,
454 # default_branch_unique_name).
455 product = self.factory.makeProduct()
456 default_branch = self.factory.enableDefaultStackingForProduct(product)
457 branch = self.factory.makeProductBranch(product=product)
458 info = self.storage._getBranchPullInfo(branch)
459 self.assertEqual(
460 (branch.id, branch.getPullURL(), branch.unique_name,
461 '/' + default_branch.unique_name), info)
462
463 def test_getBranchPullInfo_private_branch(self):
464 # We don't want to stack mirrored branches onto private branches:
465 # mirrored branches are public by their nature. Thus, if the default
466 # stacked-on branch for the project is private and the branch is
467 # MIRRORED then we don't include the default stacked-on branch's
468 # details in the tuple.
469 product = self.factory.makeProduct()
470 default_branch = self.factory.makeProductBranch(
471 product=product, private=True)
472 self.factory.enableDefaultStackingForProduct(product, default_branch)
473 mirrored_branch = self.factory.makeProductBranch(
474 branch_type=BranchType.MIRRORED, product=product)
475 info = self.storage._getBranchPullInfo(mirrored_branch)
476 self.assertEqual(
477 (mirrored_branch.id, mirrored_branch.getPullURL(),
478 mirrored_branch.unique_name, ''), info)
479
480 def test_getBranchPullInfo_junk(self):
481 # _getBranchPullInfo returns (id, url, unique_name, '') for junk
482 # branches.
483 branch = self.factory.makePersonalBranch()
484 info = self.storage._getBranchPullInfo(branch)
485 self.assertEqual(
486 (branch.id, branch.getPullURL(), branch.unique_name, ''), info)
487
488 def test_requestMirrorPutsBranchInQueue_hosted(self):
489 branch = self.makeBranchAndRequestMirror(BranchType.HOSTED)
490 self.assertBranchQueues([branch], [], [])
491
492 def test_requestMirrorPutsBranchInQueue_mirrored(self):
493 branch = self.makeBranchAndRequestMirror(BranchType.MIRRORED)
494 self.assertBranchQueues([], [branch], [])
495
496 def test_requestMirrorPutsBranchInQueue_imported(self):
497 branch = self.makeBranchAndRequestMirror(BranchType.IMPORTED)
498 self.assertBranchQueues([], [], [branch])
499
500
501class AcquireBranchToPullTestsViaEndpoint(TestCaseWithFactory,402class AcquireBranchToPullTestsViaEndpoint(TestCaseWithFactory,
502 AcquireBranchToPullTests):403 AcquireBranchToPullTests):
503 """Tests for `acquireBranchToPull` method of `IBranchPuller`."""404 """Tests for `acquireBranchToPull` method of `IBranchPuller`."""
@@ -1175,7 +1076,6 @@
1175 suite = unittest.TestSuite()1076 suite = unittest.TestSuite()
1176 puller_tests = unittest.TestSuite(1077 puller_tests = unittest.TestSuite(
1177 [loader.loadTestsFromTestCase(BranchPullerTest),1078 [loader.loadTestsFromTestCase(BranchPullerTest),
1178 loader.loadTestsFromTestCase(BranchPullQueueTest),
1179 loader.loadTestsFromTestCase(AcquireBranchToPullTestsViaEndpoint),1079 loader.loadTestsFromTestCase(AcquireBranchToPullTestsViaEndpoint),
1180 loader.loadTestsFromTestCase(BranchFileSystemTest),1080 loader.loadTestsFromTestCase(BranchFileSystemTest),
1181 ])1081 ])
11821082
=== modified file 'lib/lp/codehosting/codeimport/tests/test_worker.py'
--- lib/lp/codehosting/codeimport/tests/test_worker.py 2010-02-22 05:37:36 +0000
+++ lib/lp/codehosting/codeimport/tests/test_worker.py 2010-02-24 10:59:32 +0000
@@ -254,6 +254,31 @@
254 store._getMirrorURL(self.arbitrary_branch_id),254 store._getMirrorURL(self.arbitrary_branch_id),
255 sftp_prefix_noslash + '/' + '%08x' % self.arbitrary_branch_id)255 sftp_prefix_noslash + '/' + '%08x' % self.arbitrary_branch_id)
256256
257 def test_all_revisions_saved(self):
258 # All revisions in the branch's repo are transferred, not just those
259 # in the ancestry of the tip.
260 # Consider a branch with two heads in its repo:
261 # revid
262 # / \
263 # revid1 revid2 <- branch tip
264 # A naive push/pull would just store 'revid' and 'revid2' in the
265 # branch store -- we need to make sure all three revisions are stored
266 # and retrieved.
267 builder = self.make_branch_builder('tree')
268 revid = builder.build_snapshot(
269 None, None, [('add', ('', 'root-id', 'directory', ''))])
270 revid1 = builder.build_snapshot(None, [revid], [])
271 revid2 = builder.build_snapshot(None, [revid], [])
272 branch = builder.get_branch()
273 source_tree = branch.bzrdir.create_workingtree()
274 store = self.makeBranchStore()
275 store.push(self.arbitrary_branch_id, source_tree, default_format)
276 retrieved_tree = store.pull(
277 self.arbitrary_branch_id, 'pulled', default_format)
278 self.assertEqual(
279 set([revid, revid1, revid2]),
280 set(retrieved_tree.branch.repository.all_revision_ids()))
281
257282
258class TestImportDataStore(WorkerTest):283class TestImportDataStore(WorkerTest):
259 """Tests for `ImportDataStore`."""284 """Tests for `ImportDataStore`."""
260285
=== modified file 'lib/lp/codehosting/codeimport/worker.py'
--- lib/lp/codehosting/codeimport/worker.py 2010-02-19 03:32:39 +0000
+++ lib/lp/codehosting/codeimport/worker.py 2010-02-24 10:59:32 +0000
@@ -71,20 +71,26 @@
71 """71 """
72 remote_url = self._getMirrorURL(db_branch_id)72 remote_url = self._getMirrorURL(db_branch_id)
73 try:73 try:
74 bzr_dir = BzrDir.open(remote_url)74 remote_bzr_dir = BzrDir.open(remote_url)
75 except NotBranchError:75 except NotBranchError:
76 return BzrDir.create_standalone_workingtree(76 return BzrDir.create_standalone_workingtree(
77 target_path, required_format)77 target_path, required_format)
78 # XXX Tim Penhey 2009-09-18 bug 432217 Automatic upgrade of import78 # XXX Tim Penhey 2009-09-18 bug 432217 Automatic upgrade of import
79 # branches disabled. Need an orderly upgrade process.79 # branches disabled. Need an orderly upgrade process.
80 if False and bzr_dir.needs_format_conversion(format=required_format):80 if False and remote_bzr_dir.needs_format_conversion(
81 format=required_format):
81 try:82 try:
82 bzr_dir.root_transport.delete_tree('backup.bzr')83 remote_bzr_dir.root_transport.delete_tree('backup.bzr')
83 except NoSuchFile:84 except NoSuchFile:
84 pass85 pass
85 upgrade(remote_url, required_format)86 upgrade(remote_url, required_format)
86 bzr_dir.sprout(target_path)87 local_bzr_dir = remote_bzr_dir.sprout(target_path)
87 return BzrDir.open(target_path).open_workingtree()88 # Because of the way we do incremental imports, there may be revisions
89 # in the branch's repo that are not in the ancestry of the branch tip.
90 # We need to transfer them too.
91 local_bzr_dir.open_repository().fetch(
92 remote_bzr_dir.open_repository())
93 return local_bzr_dir.open_workingtree()
8894
89 def push(self, db_branch_id, bzr_tree, required_format):95 def push(self, db_branch_id, bzr_tree, required_format):
90 """Push up `bzr_tree` as the Bazaar branch for `code_import`.96 """Push up `bzr_tree` as the Bazaar branch for `code_import`.
@@ -101,6 +107,10 @@
101 branch_to = BzrDir.create_branch_and_repo(107 branch_to = BzrDir.create_branch_and_repo(
102 target_url, format=required_format)108 target_url, format=required_format)
103 pull_result = branch_to.pull(branch_from, overwrite=True)109 pull_result = branch_to.pull(branch_from, overwrite=True)
110 # Because of the way we do incremental imports, there may be revisions
111 # in the branch's repo that are not in the ancestry of the branch tip.
112 # We need to transfer them too.
113 branch_to.repository.fetch(branch_from.repository)
104 return pull_result.old_revid != pull_result.new_revid114 return pull_result.old_revid != pull_result.new_revid
105115
106116
107117
=== modified file 'lib/lp/codehosting/inmemory.py'
--- lib/lp/codehosting/inmemory.py 2010-02-19 03:06:12 +0000
+++ lib/lp/codehosting/inmemory.py 2010-02-24 10:59:32 +0000
@@ -442,29 +442,6 @@
442 self._branch_set = branch_set442 self._branch_set = branch_set
443 self._script_activity_set = script_activity_set443 self._script_activity_set = script_activity_set
444444
445 def _getBranchPullInfo(self, branch):
446 default_branch = ''
447 if branch.product is not None:
448 series = branch.product.development_focus
449 user_branch = series.branch
450 if (user_branch is not None
451 and not (
452 user_branch.private
453 and branch.branch_type == BranchType.MIRRORED)):
454 default_branch = '/' + user_branch.unique_name
455 return (
456 branch.id, branch.getPullURL(), branch.unique_name,
457 default_branch)
458
459 def getBranchPullQueue(self, branch_type):
460 queue = []
461 branch_type = BranchType.items[branch_type]
462 for branch in self._branch_set:
463 if (branch.branch_type == branch_type
464 and branch.next_mirror_time < UTC_NOW):
465 queue.append(self._getBranchPullInfo(branch))
466 return queue
467
468 def acquireBranchToPull(self):445 def acquireBranchToPull(self):
469 branches = sorted(446 branches = sorted(
470 [branch for branch in self._branch_set447 [branch for branch in self._branch_set
471448
=== modified file 'lib/lp/soyuz/scripts/publishdistro.py'
--- lib/lp/soyuz/scripts/publishdistro.py 2009-06-25 04:06:00 +0000
+++ lib/lp/soyuz/scripts/publishdistro.py 2010-02-24 10:59:32 +0000
@@ -69,6 +69,11 @@
69 dest="partner", metavar="PARTNER", default=False,69 dest="partner", metavar="PARTNER", default=False,
70 help="Run only over the partner archive.")70 help="Run only over the partner archive.")
7171
72 parser.add_option("--copy-archive", action="store_true",
73 dest="copy_archive", metavar="COPYARCHIVE",
74 default=False,
75 help="Run only over the copy archives.")
76
72 parser.add_option(77 parser.add_option(
73 "--primary-debug", action="store_true", default=False,78 "--primary-debug", action="store_true", default=False,
74 dest="primary_debug", metavar="PRIMARYDEBUG",79 dest="primary_debug", metavar="PRIMARYDEBUG",
@@ -103,12 +108,13 @@
103108
104 exclusive_options = (109 exclusive_options = (
105 options.partner, options.ppa, options.private_ppa,110 options.partner, options.ppa, options.private_ppa,
106 options.primary_debug)111 options.primary_debug, options.copy_archive)
112
107 num_exclusive = [flag for flag in exclusive_options if flag]113 num_exclusive = [flag for flag in exclusive_options if flag]
108 if len(num_exclusive) > 1:114 if len(num_exclusive) > 1:
109 raise LaunchpadScriptFailure(115 raise LaunchpadScriptFailure(
110 "Can only specify one of partner, ppa, private-ppa and "116 "Can only specify one of partner, ppa, private-ppa, copy-archive"
111 "primary-debug.")117 " and primary-debug.")
112118
113 log.debug(" Distribution: %s" % options.distribution)119 log.debug(" Distribution: %s" % options.distribution)
114 log.debug(" Publishing: %s" % careful_msg(options.careful_publishing))120 log.debug(" Publishing: %s" % careful_msg(options.careful_publishing))
@@ -161,6 +167,13 @@
161 raise LaunchpadScriptFailure(167 raise LaunchpadScriptFailure(
162 "Could not find DEBUG archive for %s" % distribution.name)168 "Could not find DEBUG archive for %s" % distribution.name)
163 archives = [debug_archive]169 archives = [debug_archive]
170 elif options.copy_archive:
171 archives = getUtility(IArchiveSet).getArchivesForDistribution(
172 distribution, purposes=[ArchivePurpose.COPY])
173 # Fix this to use bool when Storm fixes __nonzero__ on sqlobj
174 # result sets.
175 if archives.count() == 0:
176 raise LaunchpadScriptFailure("Could not find any COPY archives")
164 else:177 else:
165 archives = [distribution.main_archive]178 archives = [distribution.main_archive]
166179
@@ -185,9 +198,9 @@
185 try_and_commit("dominating", publisher.B_dominate,198 try_and_commit("dominating", publisher.B_dominate,
186 options.careful or options.careful_domination)199 options.careful or options.careful_domination)
187200
188 # The primary archive uses apt-ftparchive to generate the indexes,201 # The primary and copy archives use apt-ftparchive to generate the
189 # everything else uses the newer internal LP code.202 # indexes, everything else uses the newer internal LP code.
190 if archive.purpose == ArchivePurpose.PRIMARY:203 if archive.purpose in (ArchivePurpose.PRIMARY, ArchivePurpose.COPY):
191 try_and_commit("doing apt-ftparchive", publisher.C_doFTPArchive,204 try_and_commit("doing apt-ftparchive", publisher.C_doFTPArchive,
192 options.careful or options.careful_apt)205 options.careful or options.careful_apt)
193 else:206 else:
194207
=== modified file 'lib/lp/soyuz/scripts/tests/test_publishdistro.py'
--- lib/lp/soyuz/scripts/tests/test_publishdistro.py 2010-01-11 05:01:32 +0000
+++ lib/lp/soyuz/scripts/tests/test_publishdistro.py 2010-02-24 10:59:32 +0000
@@ -308,6 +308,47 @@
308 self.assertEqual(308 self.assertEqual(
309 open(debug_index_path).readlines()[0], 'Package: foo-bin\n')309 open(debug_index_path).readlines()[0], 'Package: foo-bin\n')
310310
311 def testPublishCopyArchive(self):
312 """Run publish-distro in copy archive mode.
313
314 It should only publish copy archives.
315 """
316 ubuntutest = getUtility(IDistributionSet)['ubuntutest']
317 cprov = getUtility(IPersonSet).getByName('cprov')
318 copy_archive_name = 'test-copy-publish'
319
320 # The COPY repository path is not created yet.
321 repo_path = os.path.join(
322 config.archivepublisher.root,
323 ubuntutest.name + '-' + copy_archive_name)
324 self.assertNotExists(repo_path)
325
326 copy_archive = getUtility(IArchiveSet).new(
327 distribution=ubuntutest, owner=cprov, name=copy_archive_name,
328 purpose=ArchivePurpose.COPY, enabled=True)
329 # Save some test CPU cycles by avoiding logging in as the user
330 # necessary to alter the publish flag.
331 removeSecurityProxy(copy_archive).publish = True
332
333 # Publish something.
334 pub_source = self.getPubSource(
335 sourcename='baz', filecontent='baz', archive=copy_archive)
336
337 # Try a plain PPA run, to ensure the copy archive is not published.
338 self.runPublishDistro(['--ppa'])
339
340 self.assertEqual(pub_source.status, PackagePublishingStatus.PENDING)
341
342 # Now publish the copy archives and make sure they are really
343 # published.
344 self.runPublishDistro(['--copy-archive'])
345
346 self.assertEqual(pub_source.status, PackagePublishingStatus.PUBLISHED)
347
348 # Make sure that the files were published in the right place.
349 pool_path = os.path.join(repo_path, 'pool/main/b/baz/baz_666.dsc')
350 self.assertExists(pool_path)
351
311 def testRunWithEmptySuites(self):352 def testRunWithEmptySuites(self):
312 """Try a publish-distro run on empty suites in careful_apt mode353 """Try a publish-distro run on empty suites in careful_apt mode
313354
@@ -347,7 +388,8 @@
347 """Test that some command line options are mutually exclusive."""388 """Test that some command line options are mutually exclusive."""
348 self.assertRaises(389 self.assertRaises(
349 LaunchpadScriptFailure,390 LaunchpadScriptFailure,
350 self.runPublishDistro, ['--ppa', '--partner', '--primary-debug'])391 self.runPublishDistro,
392 ['--ppa', '--partner', '--primary-debug', '--copy-archive'])
351 self.assertRaises(393 self.assertRaises(
352 LaunchpadScriptFailure,394 LaunchpadScriptFailure,
353 self.runPublishDistro, ['--ppa', '--partner'])395 self.runPublishDistro, ['--ppa', '--partner'])
@@ -359,10 +401,19 @@
359 self.runPublishDistro, ['--ppa', '--primary-debug'])401 self.runPublishDistro, ['--ppa', '--primary-debug'])
360 self.assertRaises(402 self.assertRaises(
361 LaunchpadScriptFailure,403 LaunchpadScriptFailure,
404 self.runPublishDistro, ['--ppa', '--copy-archive'])
405 self.assertRaises(
406 LaunchpadScriptFailure,
362 self.runPublishDistro, ['--partner', '--private-ppa'])407 self.runPublishDistro, ['--partner', '--private-ppa'])
363 self.assertRaises(408 self.assertRaises(
364 LaunchpadScriptFailure,409 LaunchpadScriptFailure,
365 self.runPublishDistro, ['--partner', '--primary-debug'])410 self.runPublishDistro, ['--partner', '--primary-debug'])
411 self.assertRaises(
412 LaunchpadScriptFailure,
413 self.runPublishDistro, ['--partner', '--copy-archive'])
414 self.assertRaises(
415 LaunchpadScriptFailure,
416 self.runPublishDistro, ['--primary-debug', '--copy-archive'])
366417
367418
368def test_suite():419def test_suite():
369420
=== added file 'lib/lp/testing/faketransaction.py'
--- lib/lp/testing/faketransaction.py 1970-01-01 00:00:00 +0000
+++ lib/lp/testing/faketransaction.py 2010-02-24 10:59:32 +0000
@@ -0,0 +1,35 @@
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"""Fake transaction manager."""
5
6__metaclass__ = type
7__all__ = ['FakeTransaction']
8
9
10class FakeTransaction:
11 """Fake transaction manager.
12
13 Use this instead of `transaction` (or the old Zopeless transaction
14 manager) in tests if you don't really want to commit anything.
15
16 Set `log_calls` to True to enable printing of commits and aborts.
17 """
18 def __init__(self, log_calls=False):
19 self.log_calls = log_calls
20
21 def _log(self, call):
22 """Print calls that are being made, if desired."""
23 if self.log_calls:
24 print call
25
26 def begin(self):
27 """Pretend to begin a transaction. Does not log."""
28
29 def commit(self):
30 """Pretend to commit."""
31 self._log("COMMIT")
32
33 def abort(self):
34 """Pretend to roll back."""
35 self._log("ABORT")
036
=== modified file 'lib/lp/translations/browser/poexportrequest.py'
--- lib/lp/translations/browser/poexportrequest.py 2009-07-17 00:26:05 +0000
+++ lib/lp/translations/browser/poexportrequest.py 2010-02-24 10:59:32 +0000
@@ -7,10 +7,13 @@
7__all__ = ['BaseExportView']7__all__ = ['BaseExportView']
88
99
10from datetime import timedelta
11
10from zope.component import getUtility12from zope.component import getUtility
1113
12from canonical.cachedproperty import cachedproperty14from canonical.cachedproperty import cachedproperty
13from canonical.launchpad import _15from canonical.launchpad import _
16from canonical.launchpad.webapp.tales import DurationFormatterAPI
14from lp.translations.interfaces.poexportrequest import (17from lp.translations.interfaces.poexportrequest import (
15 IPOExportRequestSet)18 IPOExportRequestSet)
16from lp.translations.interfaces.potemplate import (19from lp.translations.interfaces.potemplate import (
@@ -29,6 +32,38 @@
29 def uses_translations(self):32 def uses_translations(self):
30 return self.context.has_current_translation_templates33 return self.context.has_current_translation_templates
3134
35 @property
36 def export_queue_status(self):
37 """Summary of queue status."""
38 queue_size = self.request_set.entry_count
39 estimated_backlog = self.request_set.estimateBacklog()
40
41 size_text = self.describeQueueSize(queue_size)
42 backlog_text = self.describeBacklog(estimated_backlog)
43
44 return " ".join((size_text, backlog_text))
45
46 def describeQueueSize(self, queue_size):
47 """Return string describing the given queue size."""
48 if queue_size == 0:
49 return "The export queue is currently empty."
50 elif queue_size == 1:
51 return "There is 1 file request on the export queue."
52 else:
53 return (
54 "There are %d file requests on the export queue."
55 % queue_size)
56
57 def describeBacklog(self, estimated_backlog):
58 """Return string describing the current export backlog."""
59 threshold = timedelta(minutes=10)
60 if estimated_backlog is None or estimated_backlog < threshold:
61 return ""
62
63 formatter = DurationFormatterAPI(estimated_backlog)
64 time_string = formatter.approximateduration()
65 return "The backlog is approximately %s." % time_string
66
32 def getDefaultFormat(self):67 def getDefaultFormat(self):
33 """Overridable: return default file format to use for the export."""68 """Overridable: return default file format to use for the export."""
34 if not IHasTranslationTemplates.providedBy(self.context):69 if not IHasTranslationTemplates.providedBy(self.context):
3570
=== modified file 'lib/lp/translations/browser/tests/test_baseexportview.py'
--- lib/lp/translations/browser/tests/test_baseexportview.py 2009-07-17 02:25:09 +0000
+++ lib/lp/translations/browser/tests/test_baseexportview.py 2010-02-24 10:59:32 +0000
@@ -1,8 +1,10 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
55
6from datetime import timedelta
7import transaction
6import unittest8import unittest
79
8from canonical.launchpad.webapp.servers import LaunchpadTestRequest10from canonical.launchpad.webapp.servers import LaunchpadTestRequest
@@ -16,6 +18,12 @@
16from lp.testing import TestCaseWithFactory18from lp.testing import TestCaseWithFactory
1719
1820
21def wipe_queue(queue):
22 """Erase all export queue entries."""
23 while queue.entry_count > 0:
24 queue.popRequest()
25
26
19class BaseExportViewMixin(TestCaseWithFactory):27class BaseExportViewMixin(TestCaseWithFactory):
20 """Test behaviour of objects subclassing BaseExportView."""28 """Test behaviour of objects subclassing BaseExportView."""
2129
@@ -122,7 +130,6 @@
122 [pofile_sr.id, pofile_es.id, pofile_sr2.id],130 [pofile_sr.id, pofile_es.id, pofile_sr2.id],
123 translations)131 translations)
124132
125
126class TestProductSeries(BaseExportViewMixin):133class TestProductSeries(BaseExportViewMixin):
127 """Test implementation of BaseExportView on ProductSeries."""134 """Test implementation of BaseExportView on ProductSeries."""
128135
@@ -158,9 +165,68 @@
158 self.container, LaunchpadTestRequest())165 self.container, LaunchpadTestRequest())
159166
160167
168class TestPOExportQueueStatusDescriptions(TestCaseWithFactory):
169
170 layer = ZopelessDatabaseLayer
171
172 def setUp(self):
173 super(TestPOExportQueueStatusDescriptions, self).setUp()
174 self.container = self.factory.makeProductSeries()
175 self.container.product.official_rosetta = True
176 self.view = ProductSeriesTranslationsExportView(
177 self.container, LaunchpadTestRequest())
178
179 def test_describeQueueSize(self):
180 self.assertEqual(
181 "The export queue is currently empty.",
182 self.view.describeQueueSize(0))
183
184 self.assertEqual(
185 "There is 1 file request on the export queue.",
186 self.view.describeQueueSize(1))
187
188 self.assertEqual(
189 "There are 2 file requests on the export queue.",
190 self.view.describeQueueSize(2))
191
192 def test_describeBacklog(self):
193 backlog = None
194 self.assertEqual("", self.view.describeBacklog(backlog).strip())
195
196 backlog = timedelta(hours=2)
197 self.assertEqual(
198 "The backlog is approximately two hours.",
199 self.view.describeBacklog(backlog).strip())
200
201 def test_export_queue_status(self):
202 self.view.initialize()
203 queue = self.view.request_set
204 wipe_queue(queue)
205
206 requester = self.factory.makePerson()
207
208 size = self.view.describeQueueSize(0)
209 backlog = self.view.describeBacklog(None)
210 status = "%s %s" % (size, backlog)
211 self.assertEqual(
212 status.strip(), self.view.export_queue_status.strip())
213
214 potemplate = self.factory.makePOTemplate()
215 queue.addRequest(requester, potemplates=[potemplate])
216 transaction.commit()
217
218 size = self.view.describeQueueSize(1)
219 backlog = self.view.describeBacklog(queue.estimateBacklog())
220 status = "%s %s" % (size, backlog)
221 self.assertEqual(
222 status.strip(), self.view.export_queue_status.strip())
223
224
161def test_suite():225def test_suite():
162 suite = unittest.TestSuite()226 suite = unittest.TestSuite()
163 loader = unittest.TestLoader()227 loader = unittest.TestLoader()
164 suite.addTest(loader.loadTestsFromTestCase(TestProductSeries))228 suite.addTest(loader.loadTestsFromTestCase(TestProductSeries))
165 suite.addTest(loader.loadTestsFromTestCase(TestSourcePackage))229 suite.addTest(loader.loadTestsFromTestCase(TestSourcePackage))
230 suite.addTest(loader.loadTestsFromTestCase(
231 TestPOExportQueueStatusDescriptions))
166 return suite232 return suite
167233
=== modified file 'lib/lp/translations/doc/distroseries-translations-copy.txt'
--- lib/lp/translations/doc/distroseries-translations-copy.txt 2009-07-03 17:01:24 +0000
+++ lib/lp/translations/doc/distroseries-translations-copy.txt 2010-02-24 10:59:32 +0000
@@ -65,13 +65,8 @@
6565
66We need a transaction manager (in this case a fake one) to make the copy work.66We need a transaction manager (in this case a fake one) to make the copy work.
6767
68 >>> class FakeTransactionManager:68 >>> from lp.testing.faketransaction import FakeTransaction
69 ... """Mock transaction manager for test."""69 >>> transaction_stub = FakeTransaction()
70 ... def begin(self):
71 ... pass
72 ... def commit(self):
73 ... pass
74 >>> transaction_stub = FakeTransactionManager()
7570
7671
77== Preconditions for migrating translations between distro series ==72== Preconditions for migrating translations between distro series ==
7873
=== modified file 'lib/lp/translations/doc/gettext-check-messages.txt'
--- lib/lp/translations/doc/gettext-check-messages.txt 2009-07-01 20:45:39 +0000
+++ lib/lp/translations/doc/gettext-check-messages.txt 2010-02-24 10:59:32 +0000
@@ -29,15 +29,7 @@
29 >>> from lp.translations.scripts.gettext_check_messages import (29 >>> from lp.translations.scripts.gettext_check_messages import (
30 ... GettextCheckMessages)30 ... GettextCheckMessages)
31 >>> from canonical.launchpad.scripts.logger import FakeLogger31 >>> from canonical.launchpad.scripts.logger import FakeLogger
3232 >>> from lp.testing.faketransaction import FakeTransaction
33 >>> class MockTransactionManager:
34 ... """"Print out commits and aborts, ignore them otherwise."""
35 ... def begin(self):
36 ... pass
37 ... def commit(self):
38 ... print "Committing."
39 ... def abort(self):
40 ... print "Aborting."
4133
42 >>> class InstrumentedGettextCheckMessages(GettextCheckMessages):34 >>> class InstrumentedGettextCheckMessages(GettextCheckMessages):
43 ... _commit_interval = 335 ... _commit_interval = 3
@@ -49,7 +41,7 @@
49 ... checker = InstrumentedGettextCheckMessages(41 ... checker = InstrumentedGettextCheckMessages(
50 ... 'gettext-check-messages-test', test_args=options)42 ... 'gettext-check-messages-test', test_args=options)
51 ... checker.logger = FakeLogger()43 ... checker.logger = FakeLogger()
52 ... checker.txn = MockTransactionManager()44 ... checker.txn = FakeTransaction(log_calls=True)
53 ... if commit_interval is not None:45 ... if commit_interval is not None:
54 ... checker._commit_interval = commit_interval46 ... checker._commit_interval = commit_interval
55 ... checker.main()47 ... checker.main()
@@ -101,9 +93,9 @@
10193
102 >>> run_checker(['-vv', "-w id=%s" % quote(current_message.id)])94 >>> run_checker(['-vv', "-w id=%s" % quote(current_message.id)])
103 DEBUG Checking messages matching: id=...95 DEBUG Checking messages matching: id=...
104 DEBUG Checking message ....96 DEBUG Checking message ...
105 DEBUG Commit point.97 DEBUG Commit point.
106 Committing.98 COMMIT
107 INFO Done.99 INFO Done.
108 INFO Messages checked: 1100 INFO Messages checked: 1
109 INFO Validation errors: 0101 INFO Validation errors: 0
@@ -128,7 +120,7 @@
128 INFO ... (current): format specifications ... are not the same120 INFO ... (current): format specifications ... are not the same
129 INFO ...: unmasked ...121 INFO ...: unmasked ...
130 DEBUG Commit point.122 DEBUG Commit point.
131 Committing.123 COMMIT
132 INFO Done.124 INFO Done.
133 INFO Messages checked: 1125 INFO Messages checked: 1
134 INFO Validation errors: 1126 INFO Validation errors: 1
@@ -160,9 +152,9 @@
160 DEBUG Checking message ...152 DEBUG Checking message ...
161 INFO ... (current): format specifications ... are not the same153 INFO ... (current): format specifications ... are not the same
162 DEBUG Commit point.154 DEBUG Commit point.
163 Committing.155 COMMIT
164 DEBUG Commit point.156 DEBUG Commit point.
165 Committing.157 COMMIT
166 INFO Done.158 INFO Done.
167 INFO Messages checked: 1159 INFO Messages checked: 1
168 INFO Validation errors: 2160 INFO Validation errors: 2
@@ -189,11 +181,11 @@
189 DEBUG Checking message ...181 DEBUG Checking message ...
190 INFO ... (unused): format specifications ... are not the same182 INFO ... (unused): format specifications ... are not the same
191 DEBUG Commit point.183 DEBUG Commit point.
192 Committing.184 COMMIT
193 DEBUG Checking message ...185 DEBUG Checking message ...
194 INFO ... (imported): number of format specifications ... does not match...186 INFO ... (imported): number of format specifications ... does not match...
195 DEBUG Commit point.187 DEBUG Commit point.
196 Committing.188 COMMIT
197 INFO Done.189 INFO Done.
198 INFO Messages checked: 2190 INFO Messages checked: 2
199 INFO Validation errors: 3191 INFO Validation errors: 3
@@ -226,9 +218,9 @@
226 DEBUG Checking message ...218 DEBUG Checking message ...
227 INFO ... (current): format specifications ... are not the same219 INFO ... (current): format specifications ... are not the same
228 DEBUG Commit point.220 DEBUG Commit point.
229 Aborting.221 ABORT
230 DEBUG Commit point.222 DEBUG Commit point.
231 Aborting.223 ABORT
232 INFO Done.224 INFO Done.
233 INFO Messages checked: 1225 INFO Messages checked: 1
234 INFO Validation errors: 2226 INFO Validation errors: 2
@@ -250,13 +242,13 @@
250 DEBUG Checking message ...242 DEBUG Checking message ...
251 INFO ... (...): number of format specifications ...243 INFO ... (...): number of format specifications ...
252 DEBUG Commit point.244 DEBUG Commit point.
253 Committing.245 COMMIT
254 DEBUG Checking message ...246 DEBUG Checking message ...
255 INFO ... (...): format specifications ... are not the same247 INFO ... (...): format specifications ... are not the same
256 DEBUG Commit point.248 DEBUG Commit point.
257 Committing.249 COMMIT
258 DEBUG Commit point.250 DEBUG Commit point.
259 Committing.251 COMMIT
260 INFO Done.252 INFO Done.
261 INFO Messages checked: 2253 INFO Messages checked: 2
262 INFO Validation errors: 3254 INFO Validation errors: 3
263255
=== modified file 'lib/lp/translations/doc/poexport-queue.txt'
--- lib/lp/translations/doc/poexport-queue.txt 2009-08-17 16:54:40 +0000
+++ lib/lp/translations/doc/poexport-queue.txt 2010-02-24 10:59:32 +0000
@@ -12,10 +12,12 @@
12 >>> import transaction12 >>> import transaction
13 >>> from zope.component import getUtility13 >>> from zope.component import getUtility
14 >>> from canonical.launchpad.interfaces import IPersonSet14 >>> from canonical.launchpad.interfaces import IPersonSet
15 >>> from lp.testing.faketransaction import FakeTransaction
15 >>> from lp.testing.mail_helpers import pop_notifications, print_emails16 >>> from lp.testing.mail_helpers import pop_notifications, print_emails
16 >>> from lp.translations.scripts.po_export_queue import ExportResult17 >>> from lp.translations.scripts.po_export_queue import ExportResult
17 >>> import logging18 >>> import logging
18 >>> logger = logging.getLogger()19 >>> logger = logging.getLogger()
20 >>> fake_transaction = FakeTransaction()
1921
20When there is an error, the system will notify it.22When there is an error, the system will notify it.
2123
@@ -273,7 +275,7 @@
273275
274Once the queue is processed, the queue is empty again.276Once the queue is processed, the queue is empty again.
275277
276 >>> process_queue(transaction, logging.getLogger())278 >>> process_queue(fake_transaction, logging.getLogger())
277 INFO:...Stored file at http://.../po_evolution-2.2.pot279 INFO:...Stored file at http://.../po_evolution-2.2.pot
278280
279 >>> export_request_set.entry_count281 >>> export_request_set.entry_count
@@ -331,7 +333,7 @@
331333
332 >>> export_request_set.addRequest(334 >>> export_request_set.addRequest(
333 ... carlos, pofiles=[pofile], format=TranslationFileFormat.PO)335 ... carlos, pofiles=[pofile], format=TranslationFileFormat.PO)
334 >>> process_queue(transaction, logging.getLogger())336 >>> process_queue(fake_transaction, logging.getLogger())
335 INFO:root:Stored file at http://...eo.po337 INFO:root:Stored file at http://...eo.po
336338
337 >>> transaction.commit()339 >>> transaction.commit()
@@ -352,7 +354,7 @@
352354
353 >>> export_request_set.addRequest(355 >>> export_request_set.addRequest(
354 ... carlos, pofiles=[pofile], format=TranslationFileFormat.POCHANGED)356 ... carlos, pofiles=[pofile], format=TranslationFileFormat.POCHANGED)
355 >>> process_queue(transaction, logging.getLogger())357 >>> process_queue(fake_transaction, logging.getLogger())
356 INFO:root:Stored file at http://...eo.po358 INFO:root:Stored file at http://...eo.po
357359
358 >>> transaction.commit()360 >>> transaction.commit()
@@ -372,6 +374,6 @@
372Finally, if we try to do an export with an empty queue, we don't do374Finally, if we try to do an export with an empty queue, we don't do
373anything:375anything:
374376
375 >>> process_queue(transaction, logging.getLogger())377 >>> process_queue(fake_transaction, logging.getLogger())
376 >>> len(pop_notifications())378 >>> len(pop_notifications())
377 0379 0
378380
=== modified file 'lib/lp/translations/doc/poexport-request-productseries.txt'
--- lib/lp/translations/doc/poexport-request-productseries.txt 2009-08-17 13:42:00 +0000
+++ lib/lp/translations/doc/poexport-request-productseries.txt 2010-02-24 10:59:32 +0000
@@ -34,15 +34,12 @@
3434
35Now we request that the queue be processed.35Now we request that the queue be processed.
3636
37 >>> class MockTransactionManager:
38 ... def commit(self):
39 ... pass
40
41 >>> import logging37 >>> import logging
38 >>> from lp.testing.faketransaction import FakeTransaction
42 >>> from lp.translations.scripts.po_export_queue import process_queue39 >>> from lp.translations.scripts.po_export_queue import process_queue
43 >>> logger = MockLogger()40 >>> logger = MockLogger()
44 >>> logger.setLevel(logging.DEBUG)41 >>> logger.setLevel(logging.DEBUG)
45 >>> process_queue(MockTransactionManager(), logger)42 >>> process_queue(FakeTransaction(), logger)
46 log> Exporting objects for ..., related to template evolution-2.2 in43 log> Exporting objects for ..., related to template evolution-2.2 in
47 Evolution trunk44 Evolution trunk
48 log> Exporting objects for ..., related to template evolution-2.2-test in45 log> Exporting objects for ..., related to template evolution-2.2-test in
4946
=== modified file 'lib/lp/translations/doc/poexport-request.txt'
--- lib/lp/translations/doc/poexport-request.txt 2009-08-17 23:37:19 +0000
+++ lib/lp/translations/doc/poexport-request.txt 2010-02-24 10:59:32 +0000
@@ -53,12 +53,9 @@
5353
54Now we request that the queue be processed.54Now we request that the queue be processed.
5555
56 >>> class MockTransactionManager:56 >>> from lp.testing.faketransaction import FakeTransaction
57 ... def commit(self):
58 ... pass
59
60 >>> from lp.translations.scripts.po_export_queue import process_queue57 >>> from lp.translations.scripts.po_export_queue import process_queue
61 >>> process_queue(MockTransactionManager(), MockLogger())58 >>> process_queue(FakeTransaction(), MockLogger())
62 log> Exporting objects for Happy Downloader, related to template pmount59 log> Exporting objects for Happy Downloader, related to template pmount
63 in Ubuntu Hoary package "pmount"60 in Ubuntu Hoary package "pmount"
64 log> Stored file at http://.../launchpad-export.tar.gz61 log> Stored file at http://.../launchpad-export.tar.gz
@@ -188,7 +185,7 @@
188 >>> from lp.translations.interfaces.translationfileformat import (185 >>> from lp.translations.interfaces.translationfileformat import (
189 ... TranslationFileFormat)186 ... TranslationFileFormat)
190 >>> request_set.addRequest(person, None, [cs], TranslationFileFormat.MO)187 >>> request_set.addRequest(person, None, [cs], TranslationFileFormat.MO)
191 >>> process_queue(MockTransactionManager(), MockLogger())188 >>> process_queue(FakeTransaction(), MockLogger())
192 log> Exporting objects for Happy Downloader, related to template pmount189 log> Exporting objects for Happy Downloader, related to template pmount
193 in Ubuntu Hoary package "pmount"190 in Ubuntu Hoary package "pmount"
194 log> Stored file at http://.../cs_LC_MESSAGES_pmount.mo191 log> Stored file at http://.../cs_LC_MESSAGES_pmount.mo
195192
=== modified file 'lib/lp/translations/doc/poimport.txt'
--- lib/lp/translations/doc/poimport.txt 2009-11-17 09:51:40 +0000
+++ lib/lp/translations/doc/poimport.txt 2010-02-24 10:59:32 +0000
@@ -103,14 +103,7 @@
103To prevent this, the importer now does intermediate commits while103To prevent this, the importer now does intermediate commits while
104recomputing statistics.104recomputing statistics.
105105
106 >>> class FakeTransactionManager:106 >>> from lp.testing.faketransaction import FakeTransaction
107 ... """Pretend to manage a transaction, log what happens."""
108 ... def begin(self):
109 ... pass
110 ... def commit(self):
111 ... print "Committing."
112 ... def abort(self):
113 ... print "Aborting."
114107
115Attach the import to the translations import queue:108Attach the import to the translations import queue:
116109
@@ -136,8 +129,7 @@
136Now, we tell the PO template to import from the file data it has.129Now, we tell the PO template to import from the file data it has.
137130
138 >>> (subject, body) = potemplate.importFromQueue(131 >>> (subject, body) = potemplate.importFromQueue(
139 ... entry, FakeLogger(), txn=FakeTransactionManager())132 ... entry, FakeLogger(), txn=FakeTransaction())
140 Committing.
141133
142Our request has now been serviced.134Our request has now been serviced.
143135
144136
=== modified file 'lib/lp/translations/interfaces/poexportrequest.py'
--- lib/lp/translations/interfaces/poexportrequest.py 2009-07-17 00:26:05 +0000
+++ lib/lp/translations/interfaces/poexportrequest.py 2010-02-24 10:59:32 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4# pylint: disable-msg=E0211,E02134# pylint: disable-msg=E0211,E0213
@@ -18,11 +18,16 @@
18from lp.translations.interfaces.potemplate import IPOTemplate18from lp.translations.interfaces.potemplate import IPOTemplate
19from lp.translations.interfaces.translationfileformat import (19from lp.translations.interfaces.translationfileformat import (
20 TranslationFileFormat)20 TranslationFileFormat)
21
22
21class IPOExportRequestSet(Interface):23class IPOExportRequestSet(Interface):
22 entry_count = Int(24 entry_count = Int(
23 title=u'Number of entries waiting in the queue.',25 title=u'Number of entries waiting in the queue.',
24 required=True, readonly=True)26 required=True, readonly=True)
2527
28 def estimateBacklog():
29 """Return approximate age of oldest request on the export queue."""
30
26 def addRequest(person, potemplates=None, pofiles=None,31 def addRequest(person, potemplates=None, pofiles=None,
27 format=TranslationFileFormat.PO):32 format=TranslationFileFormat.PO):
28 """Add a request to export a set of files.33 """Add a request to export a set of files.
@@ -40,6 +45,7 @@
40 objects to export.45 objects to export.
41 """46 """
4247
48
43class IPOExportRequest(Interface):49class IPOExportRequest(Interface):
44 person = Object(50 person = Object(
45 title=u'The person who made the request.',51 title=u'The person who made the request.',
4652
=== modified file 'lib/lp/translations/model/poexportrequest.py'
--- lib/lp/translations/model/poexportrequest.py 2009-07-17 00:26:05 +0000
+++ lib/lp/translations/model/poexportrequest.py 2010-02-24 10:59:32 +0000
@@ -1,17 +1,21 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4# pylint: disable-msg=E0611,W02124# pylint: disable-msg=E0611,W0212
55
6__metaclass__ = type6__metaclass__ = type
77
8__all__ = ('POExportRequestSet', 'POExportRequest')8__all__ = [
9 'POExportRequest',
10 'POExportRequestSet',
11 ]
912
10from sqlobject import ForeignKey13from sqlobject import ForeignKey
1114
15from zope.component import getUtility
12from zope.interface import implements16from zope.interface import implements
1317
14from canonical.database.sqlbase import cursor, quote, SQLBase, sqlvalues18from canonical.database.sqlbase import quote, SQLBase, sqlvalues
15from canonical.database.enumcol import EnumCol19from canonical.database.enumcol import EnumCol
1620
17from lp.translations.interfaces.poexportrequest import (21from lp.translations.interfaces.poexportrequest import (
@@ -19,6 +23,8 @@
19from lp.translations.interfaces.potemplate import IPOTemplate23from lp.translations.interfaces.potemplate import IPOTemplate
20from lp.translations.interfaces.translationfileformat import (24from lp.translations.interfaces.translationfileformat import (
21 TranslationFileFormat)25 TranslationFileFormat)
26from canonical.launchpad.webapp.interfaces import (
27 DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE, MASTER_FLAVOR)
22from lp.registry.interfaces.person import validate_public_person28from lp.registry.interfaces.person import validate_public_person
2329
2430
@@ -28,7 +34,17 @@
28 @property34 @property
29 def entry_count(self):35 def entry_count(self):
30 """See `IPOExportRequestSet`."""36 """See `IPOExportRequestSet`."""
31 return POExportRequest.select().count()37 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
38 return store.find(POExportRequest, True).count()
39
40 def estimateBacklog(self):
41 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
42 row = store.execute(
43 "SELECT now() - min(date_created) FROM POExportRequest").get_one()
44 if row is None:
45 return None
46 else:
47 return row[0]
3248
33 def addRequest(self, person, potemplates=None, pofiles=None,49 def addRequest(self, person, potemplates=None, pofiles=None,
34 format=TranslationFileFormat.PO):50 format=TranslationFileFormat.PO):
@@ -58,13 +74,13 @@
58 'pofiles': pofile_ids,74 'pofiles': pofile_ids,
59 }75 }
6076
61 cur = cursor()77 store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
6278
63 if potemplates:79 if potemplates:
64 # Create requests for all these templates, insofar as the same80 # Create requests for all these templates, insofar as the same
65 # user doesn't already have requests pending for them in the same81 # user doesn't already have requests pending for them in the same
66 # format.82 # format.
67 cur.execute("""83 store.execute("""
68 INSERT INTO POExportRequest(person, potemplate, format)84 INSERT INTO POExportRequest(person, potemplate, format)
69 SELECT %(person)s, template.id, %(format)s85 SELECT %(person)s, template.id, %(format)s
70 FROM POTemplate AS template86 FROM POTemplate AS template
@@ -81,7 +97,7 @@
81 if pofiles:97 if pofiles:
82 # Create requests for all these translations, insofar as the same98 # Create requests for all these translations, insofar as the same
83 # user doesn't already have identical requests pending.99 # user doesn't already have identical requests pending.
84 cur.execute("""100 store.execute("""
85 INSERT INTO POExportRequest(101 INSERT INTO POExportRequest(
86 person, potemplate, pofile, format)102 person, potemplate, pofile, format)
87 SELECT %(person)s, template.id, pofile.id, %(format)s103 SELECT %(person)s, template.id, pofile.id, %(format)s
88104
=== modified file 'lib/lp/translations/scripts/tests/test_copy_distroseries_translations.py'
--- lib/lp/translations/scripts/tests/test_copy_distroseries_translations.py 2009-07-17 00:26:05 +0000
+++ lib/lp/translations/scripts/tests/test_copy_distroseries_translations.py 2010-02-24 10:59:32 +0000
@@ -13,24 +13,16 @@
1313
14from canonical.launchpad.ftests import syncUpdate14from canonical.launchpad.ftests import syncUpdate
15from lp.registry.interfaces.distroseries import IDistroSeriesSet15from lp.registry.interfaces.distroseries import IDistroSeriesSet
16from lp.testing.faketransaction import FakeTransaction
16from lp.translations.scripts.copy_distroseries_translations import (17from lp.translations.scripts.copy_distroseries_translations import (
17 copy_distroseries_translations)18 copy_distroseries_translations)
1819
19from canonical.testing import LaunchpadZopelessLayer20from canonical.testing import LaunchpadZopelessLayer
2021
2122
22class MockTransactionManager:
23 def begin(self):
24 pass
25 def commit(self):
26 pass
27 def abort(self):
28 pass
29
30
31class TestCopying(TestCase):23class TestCopying(TestCase):
32 layer = LaunchpadZopelessLayer24 layer = LaunchpadZopelessLayer
33 txn = MockTransactionManager()25 txn = FakeTransaction()
3426
35 def test_flagsHandling(self):27 def test_flagsHandling(self):
36 """Flags are correctly restored, no matter what their values."""28 """Flags are correctly restored, no matter what their values."""
3729
=== modified file 'lib/lp/translations/templates/translations-export.pt'
--- lib/lp/translations/templates/translations-export.pt 2009-09-17 21:07:36 +0000
+++ lib/lp/translations/templates/translations-export.pt 2010-02-24 10:59:32 +0000
@@ -38,6 +38,11 @@
38 This message will tell you where you can download your file.38 This message will tell you where you can download your file.
39 </p>39 </p>
4040
41 <p tal:content="view/export_queue_status">
42 There are 201 file reequests on the export queue. The backlog is
43 approximately 25 minutes.
44 </p>
45
41 <div class="actions">46 <div class="actions">
42 <p>47 <p>
43 <input type="submit" value="Request Download" />48 <input type="submit" value="Request Download" />

Subscribers

People subscribed via source and target branches

to status/vote changes: