Merge lp:~cjwatson/launchpad/branch-unscan-affordances into lp:launchpad
- branch-unscan-affordances
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 18497 |
Proposed branch: | lp:~cjwatson/launchpad/branch-unscan-affordances |
Merge into: | lp:launchpad |
Diff against target: |
509 lines (+161/-60) 15 files modified
lib/lp/code/browser/branch.py (+3/-3) lib/lp/code/browser/branchmergeproposal.py (+3/-3) lib/lp/code/browser/tests/test_branch.py (+1/-1) lib/lp/code/browser/tests/test_branchmergeproposal.py (+20/-0) lib/lp/code/interfaces/branch.py (+6/-0) lib/lp/code/interfaces/gitref.py (+1/-1) lib/lp/code/interfaces/gitrepository.py (+1/-1) lib/lp/code/model/branch.py (+37/-9) lib/lp/code/model/gitref.py (+3/-3) lib/lp/code/model/gitrepository.py (+1/-1) lib/lp/code/model/tests/test_branch.py (+73/-26) lib/lp/code/model/tests/test_gitrepository.py (+7/-7) lib/lp/code/templates/branch-index.pt (+2/-2) lib/lp/code/templates/gitrepository-index.pt (+2/-2) lib/lp/codehosting/tests/test_branchdistro.py (+1/-1) |
To merge this branch: | bzr merge lp:~cjwatson/launchpad/branch-unscan-affordances |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+333251@code.launchpad.net |
Commit message
Improve handling of branches with various kinds of partial data.
Description of the change
This tidies up OOPSes on e.g. dogfood, and allows us to unscan branches without having the branches and any associated merge proposals displaying permanent "Updating" UI indications.
I could probably have made this shorter by renaming the other half of the pending_
William Grant (wgrant) : | # |
Preview Diff
1 | === modified file 'lib/lp/code/browser/branch.py' | |||
2 | --- lib/lp/code/browser/branch.py 2016-11-11 12:51:58 +0000 | |||
3 | +++ lib/lp/code/browser/branch.py 2017-11-08 11:16:30 +0000 | |||
4 | @@ -451,9 +451,9 @@ | |||
5 | 451 | return self.context.control_format is None | 451 | return self.context.control_format is None |
6 | 452 | 452 | ||
7 | 453 | @property | 453 | @property |
11 | 454 | def pending_writes(self): | 454 | def pending_updates(self): |
12 | 455 | """Whether or not there are pending writes for this branch.""" | 455 | """Whether or not there are pending updates for this branch.""" |
13 | 456 | return self.context.pending_writes | 456 | return self.context.pending_updates |
14 | 457 | 457 | ||
15 | 458 | def bzr_download_url(self): | 458 | def bzr_download_url(self): |
16 | 459 | """Return the generic URL for downloading the branch.""" | 459 | """Return the generic URL for downloading the branch.""" |
17 | 460 | 460 | ||
18 | === modified file 'lib/lp/code/browser/branchmergeproposal.py' | |||
19 | --- lib/lp/code/browser/branchmergeproposal.py 2017-05-24 12:04:18 +0000 | |||
20 | +++ lib/lp/code/browser/branchmergeproposal.py 2017-11-08 11:16:30 +0000 | |||
21 | @@ -370,7 +370,7 @@ | |||
22 | 370 | return [] | 370 | return [] |
23 | 371 | 371 | ||
24 | 372 | @property | 372 | @property |
26 | 373 | def pending_writes(self): | 373 | def pending_updates(self): |
27 | 374 | """Needed to make the branch-revisions metal macro work.""" | 374 | """Needed to make the branch-revisions metal macro work.""" |
28 | 375 | return False | 375 | return False |
29 | 376 | 376 | ||
30 | @@ -532,7 +532,7 @@ | |||
31 | 532 | diff = preview_diff.text.decode('utf-8') | 532 | diff = preview_diff.text.decode('utf-8') |
32 | 533 | except UnicodeDecodeError: | 533 | except UnicodeDecodeError: |
33 | 534 | diff = preview_diff.text.decode('windows-1252', 'replace') | 534 | diff = preview_diff.text.decode('windows-1252', 'replace') |
35 | 535 | except LibrarianServerError: | 535 | except (LookupError, LibrarianServerError): |
36 | 536 | self._diff_available = False | 536 | self._diff_available = False |
37 | 537 | diff = '' | 537 | diff = '' |
38 | 538 | # Strip off the trailing carriage returns. | 538 | # Strip off the trailing carriage returns. |
39 | @@ -736,7 +736,7 @@ | |||
40 | 736 | def pending_diff(self): | 736 | def pending_diff(self): |
41 | 737 | return ( | 737 | return ( |
42 | 738 | self.context.next_preview_diff_job is not None or | 738 | self.context.next_preview_diff_job is not None or |
44 | 739 | self.context.merge_source.pending_writes) | 739 | self.context.merge_source.pending_updates) |
45 | 740 | 740 | ||
46 | 741 | @cachedproperty | 741 | @cachedproperty |
47 | 742 | def preview_diff(self): | 742 | def preview_diff(self): |
48 | 743 | 743 | ||
49 | === modified file 'lib/lp/code/browser/tests/test_branch.py' | |||
50 | --- lib/lp/code/browser/tests/test_branch.py 2017-10-04 01:16:22 +0000 | |||
51 | +++ lib/lp/code/browser/tests/test_branch.py 2017-11-08 11:16:30 +0000 | |||
52 | @@ -607,7 +607,7 @@ | |||
53 | 607 | logout() | 607 | logout() |
54 | 608 | with StormStatementRecorder() as recorder: | 608 | with StormStatementRecorder() as recorder: |
55 | 609 | browser.open(branch_url) | 609 | browser.open(branch_url) |
57 | 610 | self.assertThat(recorder, HasQueryCount(Equals(27))) | 610 | self.assertThat(recorder, HasQueryCount(Equals(28))) |
58 | 611 | 611 | ||
59 | 612 | 612 | ||
60 | 613 | class TestBranchViewPrivateArtifacts(BrowserTestCase): | 613 | class TestBranchViewPrivateArtifacts(BrowserTestCase): |
61 | 614 | 614 | ||
62 | === modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py' | |||
63 | --- lib/lp/code/browser/tests/test_branchmergeproposal.py 2017-10-04 01:16:22 +0000 | |||
64 | +++ lib/lp/code/browser/tests/test_branchmergeproposal.py 2017-11-08 11:16:30 +0000 | |||
65 | @@ -1395,6 +1395,26 @@ | |||
66 | 1395 | markup = view() | 1395 | markup = view() |
67 | 1396 | self.assertIn('The diff is not available at this time.', markup) | 1396 | self.assertIn('The diff is not available at this time.', markup) |
68 | 1397 | 1397 | ||
69 | 1398 | def test_preview_diff_lookup_error(self): | ||
70 | 1399 | # The preview_diff will recover from a LookupError while getting the | ||
71 | 1400 | # librarian content. (This can happen e.g. on staging replicas of | ||
72 | 1401 | # the production database.) | ||
73 | 1402 | text = b''.join(chr(x) for x in range(255)) | ||
74 | 1403 | diff_bytes = b''.join(unified_diff(b'', text)) | ||
75 | 1404 | preview_diff = self.setPreviewDiff(diff_bytes) | ||
76 | 1405 | transaction.commit() | ||
77 | 1406 | |||
78 | 1407 | def fake_open(*args): | ||
79 | 1408 | raise LookupError | ||
80 | 1409 | |||
81 | 1410 | lfa = preview_diff.diff.diff_text | ||
82 | 1411 | with monkey_patch(lfa, open=fake_open): | ||
83 | 1412 | view = create_initialized_view(preview_diff, '+diff') | ||
84 | 1413 | self.assertEqual('', view.preview_diff_text) | ||
85 | 1414 | self.assertFalse(view.diff_available) | ||
86 | 1415 | markup = view() | ||
87 | 1416 | self.assertIn('The diff is not available at this time.', markup) | ||
88 | 1417 | |||
89 | 1398 | def setPreviewDiff(self, preview_diff_bytes): | 1418 | def setPreviewDiff(self, preview_diff_bytes): |
90 | 1399 | return PreviewDiff.create( | 1419 | return PreviewDiff.create( |
91 | 1400 | self.bmp, preview_diff_bytes, 'a', 'b', None, '') | 1420 | self.bmp, preview_diff_bytes, 'a', 'b', None, '') |
92 | 1401 | 1421 | ||
93 | === modified file 'lib/lp/code/interfaces/branch.py' | |||
94 | --- lib/lp/code/interfaces/branch.py 2016-11-11 12:51:58 +0000 | |||
95 | +++ lib/lp/code/interfaces/branch.py 2017-11-08 11:16:30 +0000 | |||
96 | @@ -538,6 +538,12 @@ | |||
97 | 538 | pending_writes = Attribute( | 538 | pending_writes = Attribute( |
98 | 539 | "Whether there is new Bazaar data for this branch.") | 539 | "Whether there is new Bazaar data for this branch.") |
99 | 540 | 540 | ||
100 | 541 | pending_updates = Attribute( | ||
101 | 542 | "Whether there is an update job of some kind (mirroring or scanning) " | ||
102 | 543 | "pending for the Bazaar data in this branch. Note that " | ||
103 | 544 | "pending_writes may be True and pending_updates False if a branch " | ||
104 | 545 | "has been unscanned.") | ||
105 | 546 | |||
106 | 541 | def latest_revisions(quantity=10): | 547 | def latest_revisions(quantity=10): |
107 | 542 | """A specific number of the latest revisions in that branch.""" | 548 | """A specific number of the latest revisions in that branch.""" |
108 | 543 | 549 | ||
109 | 544 | 550 | ||
110 | === modified file 'lib/lp/code/interfaces/gitref.py' | |||
111 | --- lib/lp/code/interfaces/gitref.py 2016-12-05 14:46:40 +0000 | |||
112 | +++ lib/lp/code/interfaces/gitref.py 2017-11-08 11:16:30 +0000 | |||
113 | @@ -358,7 +358,7 @@ | |||
114 | 358 | eager_load=False): | 358 | eager_load=False): |
115 | 359 | """Return BranchMergeProposals dependent on merging this reference.""" | 359 | """Return BranchMergeProposals dependent on merging this reference.""" |
116 | 360 | 360 | ||
118 | 361 | pending_writes = Attribute( | 361 | pending_updates = Attribute( |
119 | 362 | "Whether there are recent changes in this repository that have not " | 362 | "Whether there are recent changes in this repository that have not " |
120 | 363 | "yet been scanned.") | 363 | "yet been scanned.") |
121 | 364 | 364 | ||
122 | 365 | 365 | ||
123 | === modified file 'lib/lp/code/interfaces/gitrepository.py' | |||
124 | --- lib/lp/code/interfaces/gitrepository.py 2017-02-10 12:52:07 +0000 | |||
125 | +++ lib/lp/code/interfaces/gitrepository.py 2017-11-08 11:16:30 +0000 | |||
126 | @@ -537,7 +537,7 @@ | |||
127 | 537 | def isRepositoryMergeable(other): | 537 | def isRepositoryMergeable(other): |
128 | 538 | """Is the other repository mergeable into this one (or vice versa)?""" | 538 | """Is the other repository mergeable into this one (or vice versa)?""" |
129 | 539 | 539 | ||
131 | 540 | pending_writes = Attribute( | 540 | pending_updates = Attribute( |
132 | 541 | "Whether there are recent changes in this repository that have not " | 541 | "Whether there are recent changes in this repository that have not " |
133 | 542 | "yet been scanned.") | 542 | "yet been scanned.") |
134 | 543 | 543 | ||
135 | 544 | 544 | ||
136 | === modified file 'lib/lp/code/model/branch.py' | |||
137 | --- lib/lp/code/model/branch.py 2016-11-11 14:24:38 +0000 | |||
138 | +++ lib/lp/code/model/branch.py 2017-11-08 11:16:30 +0000 | |||
139 | @@ -1163,25 +1163,53 @@ | |||
140 | 1163 | return recipients | 1163 | return recipients |
141 | 1164 | 1164 | ||
142 | 1165 | @property | 1165 | @property |
145 | 1166 | def pending_writes(self): | 1166 | def _pending_mirror_operations(self): |
146 | 1167 | """See `IBranch`. | 1167 | """Does this branch have pending mirror operations? |
147 | 1168 | 1168 | ||
150 | 1169 | A branch has pending writes if it has just been pushed to, if it has | 1169 | A branch has pending mirror operations if it is an imported branch |
151 | 1170 | been mirrored and not yet scanned or if it is in the middle of being | 1170 | that has just been pushed to or if it is in the middle of being |
152 | 1171 | mirrored. | 1171 | mirrored. |
153 | 1172 | """ | 1172 | """ |
154 | 1173 | new_data_pushed = ( | 1173 | new_data_pushed = ( |
155 | 1174 | self.branch_type == BranchType.IMPORTED | 1174 | self.branch_type == BranchType.IMPORTED |
156 | 1175 | and self.next_mirror_time is not None) | 1175 | and self.next_mirror_time is not None) |
157 | 1176 | # XXX 2010-04-22, MichaelHudson: This should really look for a branch | ||
158 | 1177 | # scan job. | ||
159 | 1178 | pulled_but_not_scanned = self.last_mirrored_id != self.last_scanned_id | ||
160 | 1179 | pull_in_progress = ( | 1176 | pull_in_progress = ( |
161 | 1180 | self.last_mirror_attempt is not None | 1177 | self.last_mirror_attempt is not None |
162 | 1181 | and (self.last_mirrored is None | 1178 | and (self.last_mirrored is None |
163 | 1182 | or self.last_mirror_attempt > self.last_mirrored)) | 1179 | or self.last_mirror_attempt > self.last_mirrored)) |
166 | 1183 | return ( | 1180 | return new_data_pushed or pull_in_progress |
167 | 1184 | new_data_pushed or pulled_but_not_scanned or pull_in_progress) | 1181 | |
168 | 1182 | @property | ||
169 | 1183 | def pending_writes(self): | ||
170 | 1184 | """See `IBranch`. | ||
171 | 1185 | |||
172 | 1186 | A branch has pending writes if it has pending mirror operations or | ||
173 | 1187 | if it has been mirrored and not yet scanned. Use this when you need | ||
174 | 1188 | to know if the branch is in a condition where it is possible to run | ||
175 | 1189 | other jobs on it: for example, a branch that has been unscanned | ||
176 | 1190 | cannot support jobs being run for its related merge proposals. | ||
177 | 1191 | """ | ||
178 | 1192 | pulled_but_not_scanned = self.last_mirrored_id != self.last_scanned_id | ||
179 | 1193 | return self._pending_mirror_operations or pulled_but_not_scanned | ||
180 | 1194 | |||
181 | 1195 | @property | ||
182 | 1196 | def pending_updates(self): | ||
183 | 1197 | """See `IBranch`. | ||
184 | 1198 | |||
185 | 1199 | A branch has pending updates if it has pending mirror operations or | ||
186 | 1200 | if it has a pending scan job. Use this when you need to know if | ||
187 | 1201 | there is work queued, for example when deciding whether to display | ||
188 | 1202 | in-progress UI indicators. | ||
189 | 1203 | """ | ||
190 | 1204 | from lp.code.model.branchjob import BranchJob, BranchJobType | ||
191 | 1205 | jobs = Store.of(self).find( | ||
192 | 1206 | BranchJob, | ||
193 | 1207 | BranchJob.branch == self, | ||
194 | 1208 | Job.id == BranchJob.jobID, | ||
195 | 1209 | Job._status.is_in([JobStatus.WAITING, JobStatus.RUNNING]), | ||
196 | 1210 | BranchJob.job_type == BranchJobType.SCAN_BRANCH) | ||
197 | 1211 | pending_scan_job = not jobs.is_empty() | ||
198 | 1212 | return self._pending_mirror_operations or pending_scan_job | ||
199 | 1185 | 1213 | ||
200 | 1186 | def getScannerData(self): | 1214 | def getScannerData(self): |
201 | 1187 | """See `IBranch`.""" | 1215 | """See `IBranch`.""" |
202 | 1188 | 1216 | ||
203 | === modified file 'lib/lp/code/model/gitref.py' | |||
204 | --- lib/lp/code/model/gitref.py 2017-08-22 11:33:34 +0000 | |||
205 | +++ lib/lp/code/model/gitref.py 2017-11-08 11:16:30 +0000 | |||
206 | @@ -282,9 +282,9 @@ | |||
207 | 282 | prerequisite_path=self.path, eager_load=eager_load) | 282 | prerequisite_path=self.path, eager_load=eager_load) |
208 | 283 | 283 | ||
209 | 284 | @property | 284 | @property |
211 | 285 | def pending_writes(self): | 285 | def pending_updates(self): |
212 | 286 | """See `IGitRef`.""" | 286 | """See `IGitRef`.""" |
214 | 287 | return self.repository.pending_writes | 287 | return self.repository.pending_updates |
215 | 288 | 288 | ||
216 | 289 | def _getLog(self, start, limit=None, stop=None, union_repository=None, | 289 | def _getLog(self, start, limit=None, stop=None, union_repository=None, |
217 | 290 | enable_hosting=None, enable_memcache=None, logger=None): | 290 | enable_hosting=None, enable_memcache=None, logger=None): |
218 | @@ -715,7 +715,7 @@ | |||
219 | 715 | createMergeProposal = _unimplemented | 715 | createMergeProposal = _unimplemented |
220 | 716 | getMergeProposals = _unimplemented | 716 | getMergeProposals = _unimplemented |
221 | 717 | getDependentMergeProposals = _unimplemented | 717 | getDependentMergeProposals = _unimplemented |
223 | 718 | pending_writes = False | 718 | pending_updates = False |
224 | 719 | 719 | ||
225 | 720 | def getCommits(self, *args, **kwargs): | 720 | def getCommits(self, *args, **kwargs): |
226 | 721 | """See `IGitRef`.""" | 721 | """See `IGitRef`.""" |
227 | 722 | 722 | ||
228 | === modified file 'lib/lp/code/model/gitrepository.py' | |||
229 | --- lib/lp/code/model/gitrepository.py 2017-05-04 16:02:40 +0000 | |||
230 | +++ lib/lp/code/model/gitrepository.py 2017-11-08 11:16:30 +0000 | |||
231 | @@ -949,7 +949,7 @@ | |||
232 | 949 | return self.namespace.areRepositoriesMergeable(other.namespace) | 949 | return self.namespace.areRepositoriesMergeable(other.namespace) |
233 | 950 | 950 | ||
234 | 951 | @property | 951 | @property |
236 | 952 | def pending_writes(self): | 952 | def pending_updates(self): |
237 | 953 | """See `IGitRepository`.""" | 953 | """See `IGitRepository`.""" |
238 | 954 | from lp.code.model.gitjob import ( | 954 | from lp.code.model.gitjob import ( |
239 | 955 | GitJob, | 955 | GitJob, |
240 | 956 | 956 | ||
241 | === modified file 'lib/lp/code/model/tests/test_branch.py' | |||
242 | --- lib/lp/code/model/tests/test_branch.py 2017-10-04 01:49:22 +0000 | |||
243 | +++ lib/lp/code/model/tests/test_branch.py 2017-11-08 11:16:30 +0000 | |||
244 | @@ -130,6 +130,8 @@ | |||
245 | 130 | from lp.services.database.constants import UTC_NOW | 130 | from lp.services.database.constants import UTC_NOW |
246 | 131 | from lp.services.database.interfaces import IStore | 131 | from lp.services.database.interfaces import IStore |
247 | 132 | from lp.services.features.testing import FeatureFixture | 132 | from lp.services.features.testing import FeatureFixture |
248 | 133 | from lp.services.job.interfaces.job import JobStatus | ||
249 | 134 | from lp.services.job.runner import JobRunner | ||
250 | 133 | from lp.services.job.tests import ( | 135 | from lp.services.job.tests import ( |
251 | 134 | block_on_job, | 136 | block_on_job, |
252 | 135 | monitor_celery, | 137 | monitor_celery, |
253 | @@ -155,6 +157,7 @@ | |||
254 | 155 | TestCaseWithFactory, | 157 | TestCaseWithFactory, |
255 | 156 | WebServiceTestCase, | 158 | WebServiceTestCase, |
256 | 157 | ) | 159 | ) |
257 | 160 | from lp.testing.dbuser import dbuser | ||
258 | 158 | from lp.testing.factory import LaunchpadObjectFactory | 161 | from lp.testing.factory import LaunchpadObjectFactory |
259 | 159 | from lp.testing.layers import ( | 162 | from lp.testing.layers import ( |
260 | 160 | CeleryBranchWriteJobLayer, | 163 | CeleryBranchWriteJobLayer, |
261 | @@ -2364,89 +2367,133 @@ | |||
262 | 2364 | self.assertNamespaceEqual(namespace, branch.namespace) | 2367 | self.assertNamespaceEqual(namespace, branch.namespace) |
263 | 2365 | 2368 | ||
264 | 2366 | 2369 | ||
266 | 2367 | class TestPendingWrites(TestCaseWithFactory): | 2370 | class TestPendingWritesAndUpdates(TestCaseWithFactory): |
267 | 2368 | """Are there changes to this branch not reflected in the database?""" | 2371 | """Are there changes to this branch not reflected in the database?""" |
268 | 2369 | 2372 | ||
269 | 2370 | layer = LaunchpadFunctionalLayer | 2373 | layer = LaunchpadFunctionalLayer |
270 | 2371 | 2374 | ||
271 | 2372 | def test_new_branch_no_writes(self): | 2375 | def test_new_branch_no_writes(self): |
273 | 2373 | # New branches have no pending writes. | 2376 | # New branches have no pending writes or pending updates. |
274 | 2374 | branch = self.factory.makeAnyBranch() | 2377 | branch = self.factory.makeAnyBranch() |
276 | 2375 | self.assertEqual(False, branch.pending_writes) | 2378 | self.assertFalse(branch.pending_writes) |
277 | 2379 | self.assertFalse(branch.pending_updates) | ||
278 | 2376 | 2380 | ||
279 | 2377 | def test_branchChanged_for_hosted(self): | 2381 | def test_branchChanged_for_hosted(self): |
280 | 2378 | # If branchChanged has been called with a new tip revision id, there | 2382 | # If branchChanged has been called with a new tip revision id, there |
282 | 2379 | # are pending writes. | 2383 | # are pending writes and pending updates. |
283 | 2380 | branch = self.factory.makeAnyBranch(branch_type=BranchType.HOSTED) | 2384 | branch = self.factory.makeAnyBranch(branch_type=BranchType.HOSTED) |
284 | 2381 | with person_logged_in(branch.owner): | 2385 | with person_logged_in(branch.owner): |
285 | 2382 | branch.branchChanged('', 'new-tip', None, None, None) | 2386 | branch.branchChanged('', 'new-tip', None, None, None) |
287 | 2383 | self.assertEqual(True, branch.pending_writes) | 2387 | self.assertTrue(branch.pending_writes) |
288 | 2388 | self.assertTrue(branch.pending_updates) | ||
289 | 2389 | |||
290 | 2390 | def test_unscanned_without_rescan(self): | ||
291 | 2391 | # If a branch was unscanned without requesting a rescan, then there | ||
292 | 2392 | # are pending writes but no pending updates. | ||
293 | 2393 | self.useBzrBranches(direct_database=True) | ||
294 | 2394 | branch, bzr_tree = self.create_branch_and_tree() | ||
295 | 2395 | rev_id = self.factory.getUniqueString(b'rev-id') | ||
296 | 2396 | bzr_tree.commit('Commit', committer='me@example.com', rev_id=rev_id) | ||
297 | 2397 | removeSecurityProxy(branch).branchChanged('', rev_id, None, None, None) | ||
298 | 2398 | transaction.commit() | ||
299 | 2399 | [job] = getUtility(IBranchScanJobSource).iterReady() | ||
300 | 2400 | with dbuser('branchscanner'): | ||
301 | 2401 | JobRunner([job]).runAll() | ||
302 | 2402 | self.assertFalse(branch.pending_writes) | ||
303 | 2403 | self.assertFalse(branch.pending_updates) | ||
304 | 2404 | removeSecurityProxy(branch).unscan(rescan=False) | ||
305 | 2405 | self.assertTrue(branch.pending_writes) | ||
306 | 2406 | self.assertFalse(branch.pending_updates) | ||
307 | 2407 | |||
308 | 2408 | def test_unscanned_with_rescan(self): | ||
309 | 2409 | # If a branch was unscanned and a rescan was requested, then there | ||
310 | 2410 | # are pending writes and pending updates. | ||
311 | 2411 | self.useBzrBranches(direct_database=True) | ||
312 | 2412 | branch, bzr_tree = self.create_branch_and_tree() | ||
313 | 2413 | rev_id = self.factory.getUniqueString(b'rev-id') | ||
314 | 2414 | bzr_tree.commit('Commit', committer='me@example.com', rev_id=rev_id) | ||
315 | 2415 | removeSecurityProxy(branch).branchChanged('', rev_id, None, None, None) | ||
316 | 2416 | transaction.commit() | ||
317 | 2417 | [job] = getUtility(IBranchScanJobSource).iterReady() | ||
318 | 2418 | with dbuser('branchscanner'): | ||
319 | 2419 | JobRunner([job]).runAll() | ||
320 | 2420 | self.assertFalse(branch.pending_writes) | ||
321 | 2421 | self.assertFalse(branch.pending_updates) | ||
322 | 2422 | removeSecurityProxy(branch).unscan(rescan=True) | ||
323 | 2423 | self.assertTrue(branch.pending_writes) | ||
324 | 2424 | self.assertTrue(branch.pending_updates) | ||
325 | 2384 | 2425 | ||
326 | 2385 | def test_requestMirror_for_imported(self): | 2426 | def test_requestMirror_for_imported(self): |
327 | 2386 | # If an imported branch has a requested mirror, then we've just | 2427 | # If an imported branch has a requested mirror, then we've just |
329 | 2387 | # imported new changes. Therefore, pending writes. | 2428 | # imported new changes. Therefore, pending writes and pending |
330 | 2429 | # updates. | ||
331 | 2388 | branch = self.factory.makeAnyBranch(branch_type=BranchType.IMPORTED) | 2430 | branch = self.factory.makeAnyBranch(branch_type=BranchType.IMPORTED) |
332 | 2389 | branch.requestMirror() | 2431 | branch.requestMirror() |
334 | 2390 | self.assertEqual(True, branch.pending_writes) | 2432 | self.assertTrue(branch.pending_writes) |
335 | 2433 | self.assertTrue(branch.pending_updates) | ||
336 | 2391 | 2434 | ||
337 | 2392 | def test_requestMirror_for_mirrored(self): | 2435 | def test_requestMirror_for_mirrored(self): |
341 | 2393 | # Mirrored branches *always* have a requested mirror. The fact that a | 2436 | # Mirrored branches *always* have a requested mirror. The fact that |
342 | 2394 | # mirror is requested has no bearing on whether there are pending | 2437 | # a mirror is requested has no bearing on whether there are pending |
343 | 2395 | # writes. Thus, pending_writes is False. | 2438 | # writes or pending updates. Thus, pending_writes and |
344 | 2439 | # pending_updates are both False. | ||
345 | 2396 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) | 2440 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) |
346 | 2397 | branch.requestMirror() | 2441 | branch.requestMirror() |
348 | 2398 | self.assertEqual(False, branch.pending_writes) | 2442 | self.assertFalse(branch.pending_writes) |
349 | 2443 | self.assertFalse(branch.pending_updates) | ||
350 | 2399 | 2444 | ||
351 | 2400 | def test_pulled_but_not_scanned(self): | 2445 | def test_pulled_but_not_scanned(self): |
352 | 2401 | # If a branch has been pulled (mirrored) but not scanned, then we have | 2446 | # If a branch has been pulled (mirrored) but not scanned, then we have |
353 | 2402 | # yet to load the revisions into the database. This means there are | 2447 | # yet to load the revisions into the database. This means there are |
355 | 2403 | # pending writes. | 2448 | # pending writes and pending updates. |
356 | 2404 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) | 2449 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) |
357 | 2405 | branch.startMirroring() | 2450 | branch.startMirroring() |
358 | 2406 | rev_id = self.factory.getUniqueString('rev-id') | 2451 | rev_id = self.factory.getUniqueString('rev-id') |
359 | 2407 | removeSecurityProxy(branch).branchChanged( | 2452 | removeSecurityProxy(branch).branchChanged( |
360 | 2408 | '', rev_id, None, None, None) | 2453 | '', rev_id, None, None, None) |
362 | 2409 | self.assertEqual(True, branch.pending_writes) | 2454 | self.assertTrue(branch.pending_writes) |
363 | 2455 | self.assertTrue(branch.pending_updates) | ||
364 | 2410 | 2456 | ||
365 | 2411 | def test_pulled_and_scanned(self): | 2457 | def test_pulled_and_scanned(self): |
366 | 2412 | # If a branch has been pulled and scanned, then there are no pending | 2458 | # If a branch has been pulled and scanned, then there are no pending |
368 | 2413 | # writes. | 2459 | # writes or pending updates. |
369 | 2414 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) | 2460 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) |
370 | 2415 | branch.startMirroring() | 2461 | branch.startMirroring() |
371 | 2416 | rev_id = self.factory.getUniqueString('rev-id') | 2462 | rev_id = self.factory.getUniqueString('rev-id') |
372 | 2417 | removeSecurityProxy(branch).branchChanged( | 2463 | removeSecurityProxy(branch).branchChanged( |
373 | 2418 | '', rev_id, None, None, None) | 2464 | '', rev_id, None, None, None) |
376 | 2419 | # Cheat! The actual API for marking a branch as scanned is | 2465 | # Cheat! The actual API for marking a branch as scanned is to run |
377 | 2420 | # updateScannedDetails. That requires a revision in the database | 2466 | # the BranchScanJob. That requires a revision in the database |
378 | 2421 | # though. | 2467 | # though. |
379 | 2422 | removeSecurityProxy(branch).last_scanned_id = rev_id | 2468 | removeSecurityProxy(branch).last_scanned_id = rev_id |
381 | 2423 | self.assertEqual(False, branch.pending_writes) | 2469 | [job] = getUtility(IBranchScanJobSource).iterReady() |
382 | 2470 | removeSecurityProxy(job).job._status = JobStatus.COMPLETED | ||
383 | 2471 | self.assertFalse(branch.pending_writes) | ||
384 | 2472 | self.assertFalse(branch.pending_updates) | ||
385 | 2424 | 2473 | ||
386 | 2425 | def test_first_mirror_started(self): | 2474 | def test_first_mirror_started(self): |
387 | 2426 | # If we have started mirroring the branch for the first time, then | 2475 | # If we have started mirroring the branch for the first time, then |
389 | 2427 | # there are probably pending writes. | 2476 | # there are probably pending writes and pending updates. |
390 | 2428 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) | 2477 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) |
391 | 2429 | branch.startMirroring() | 2478 | branch.startMirroring() |
393 | 2430 | self.assertEqual(True, branch.pending_writes) | 2479 | self.assertTrue(branch.pending_writes) |
394 | 2480 | self.assertTrue(branch.pending_updates) | ||
395 | 2431 | 2481 | ||
396 | 2432 | def test_following_mirror_started(self): | 2482 | def test_following_mirror_started(self): |
397 | 2433 | # If we have started mirroring the branch, then there are probably | 2483 | # If we have started mirroring the branch, then there are probably |
399 | 2434 | # pending writes. | 2484 | # pending writes and pending updates. |
400 | 2435 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) | 2485 | branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED) |
401 | 2436 | branch.startMirroring() | 2486 | branch.startMirroring() |
402 | 2437 | rev_id = self.factory.getUniqueString('rev-id') | 2487 | rev_id = self.factory.getUniqueString('rev-id') |
403 | 2438 | removeSecurityProxy(branch).branchChanged( | 2488 | removeSecurityProxy(branch).branchChanged( |
404 | 2439 | '', rev_id, None, None, None) | 2489 | '', rev_id, None, None, None) |
410 | 2440 | # Cheat! The actual API for marking a branch as scanned is | 2490 | # Cheat! We can only tell if mirroring has started if the last |
406 | 2441 | # updateScannedDetails. That requires a revision in the database | ||
407 | 2442 | # though. | ||
408 | 2443 | removeSecurityProxy(branch).last_scanned_id = rev_id | ||
409 | 2444 | # Cheat again! We can only tell if mirroring has started if the last | ||
411 | 2445 | # mirrored attempt is different from the last mirrored time. To ensure | 2491 | # mirrored attempt is different from the last mirrored time. To ensure |
412 | 2446 | # this, we start the second mirror in a new transaction. | 2492 | # this, we start the second mirror in a new transaction. |
413 | 2447 | transaction.commit() | 2493 | transaction.commit() |
414 | 2448 | branch.startMirroring() | 2494 | branch.startMirroring() |
416 | 2449 | self.assertEqual(True, branch.pending_writes) | 2495 | self.assertTrue(branch.pending_writes) |
417 | 2496 | self.assertTrue(branch.pending_updates) | ||
418 | 2450 | 2497 | ||
419 | 2451 | 2498 | ||
420 | 2452 | class TestBranchPrivacy(TestCaseWithFactory): | 2499 | class TestBranchPrivacy(TestCaseWithFactory): |
421 | 2453 | 2500 | ||
422 | === modified file 'lib/lp/code/model/tests/test_gitrepository.py' | |||
423 | --- lib/lp/code/model/tests/test_gitrepository.py 2017-10-04 01:29:35 +0000 | |||
424 | +++ lib/lp/code/model/tests/test_gitrepository.py 2017-11-08 11:16:30 +0000 | |||
425 | @@ -960,28 +960,28 @@ | |||
426 | 960 | self.assertEqual(namespace, repository.namespace) | 960 | self.assertEqual(namespace, repository.namespace) |
427 | 961 | 961 | ||
428 | 962 | 962 | ||
430 | 963 | class TestGitRepositoryPendingWrites(TestCaseWithFactory): | 963 | class TestGitRepositoryPendingUpdates(TestCaseWithFactory): |
431 | 964 | """Are there changes to this repository not reflected in the database?""" | 964 | """Are there changes to this repository not reflected in the database?""" |
432 | 965 | 965 | ||
433 | 966 | layer = LaunchpadFunctionalLayer | 966 | layer = LaunchpadFunctionalLayer |
434 | 967 | 967 | ||
437 | 968 | def test_new_repository_no_writes(self): | 968 | def test_new_repository_no_updates(self): |
438 | 969 | # New repositories have no pending writes. | 969 | # New repositories have no pending updates. |
439 | 970 | repository = self.factory.makeGitRepository() | 970 | repository = self.factory.makeGitRepository() |
441 | 971 | self.assertFalse(repository.pending_writes) | 971 | self.assertFalse(repository.pending_updates) |
442 | 972 | 972 | ||
443 | 973 | def test_notify(self): | 973 | def test_notify(self): |
444 | 974 | # If the hosting service has just sent us a change notification, | 974 | # If the hosting service has just sent us a change notification, |
446 | 975 | # then there are pending writes, but running the ref-scanning job | 975 | # then there are pending updates, but running the ref-scanning job |
447 | 976 | # clears that flag. | 976 | # clears that flag. |
448 | 977 | git_api = GitAPI(None, None) | 977 | git_api = GitAPI(None, None) |
449 | 978 | repository = self.factory.makeGitRepository() | 978 | repository = self.factory.makeGitRepository() |
450 | 979 | self.assertIsNone(git_api.notify(repository.getInternalPath())) | 979 | self.assertIsNone(git_api.notify(repository.getInternalPath())) |
452 | 980 | self.assertTrue(repository.pending_writes) | 980 | self.assertTrue(repository.pending_updates) |
453 | 981 | [job] = list(getUtility(IGitRefScanJobSource).iterReady()) | 981 | [job] = list(getUtility(IGitRefScanJobSource).iterReady()) |
454 | 982 | with dbuser("branchscanner"): | 982 | with dbuser("branchscanner"): |
455 | 983 | JobRunner([job]).runAll() | 983 | JobRunner([job]).runAll() |
457 | 984 | self.assertFalse(repository.pending_writes) | 984 | self.assertFalse(repository.pending_updates) |
458 | 985 | 985 | ||
459 | 986 | 986 | ||
460 | 987 | class TestGitRepositoryPrivacy(TestCaseWithFactory): | 987 | class TestGitRepositoryPrivacy(TestCaseWithFactory): |
461 | 988 | 988 | ||
462 | === modified file 'lib/lp/code/templates/branch-index.pt' | |||
463 | --- lib/lp/code/templates/branch-index.pt 2016-10-13 12:43:14 +0000 | |||
464 | +++ lib/lp/code/templates/branch-index.pt 2017-11-08 11:16:30 +0000 | |||
465 | @@ -130,9 +130,9 @@ | |||
466 | 130 | 130 | ||
467 | 131 | </div> | 131 | </div> |
468 | 132 | 132 | ||
470 | 133 | <div class="yui-g" tal:condition="view/pending_writes"> | 133 | <div class="yui-g" tal:condition="view/pending_updates"> |
471 | 134 | <div class="portlet"> | 134 | <div class="portlet"> |
473 | 135 | <div id="branch-pending-writes" class="pending-update"> | 135 | <div id="branch-pending-updates" class="pending-update"> |
474 | 136 | <h3>Updating branch...</h3> | 136 | <h3>Updating branch...</h3> |
475 | 137 | <p> | 137 | <p> |
476 | 138 | Launchpad is processing new changes to this branch which will be | 138 | Launchpad is processing new changes to this branch which will be |
477 | 139 | 139 | ||
478 | === modified file 'lib/lp/code/templates/gitrepository-index.pt' | |||
479 | --- lib/lp/code/templates/gitrepository-index.pt 2016-10-13 12:43:14 +0000 | |||
480 | +++ lib/lp/code/templates/gitrepository-index.pt 2017-11-08 11:16:30 +0000 | |||
481 | @@ -72,9 +72,9 @@ | |||
482 | 72 | </div> | 72 | </div> |
483 | 73 | </div> | 73 | </div> |
484 | 74 | 74 | ||
486 | 75 | <div class="yui-g" tal:condition="context/pending_writes"> | 75 | <div class="yui-g" tal:condition="context/pending_updates"> |
487 | 76 | <div class="portlet"> | 76 | <div class="portlet"> |
489 | 77 | <div id="repository-pending-writes" class="pending-update"> | 77 | <div id="repository-pending-updates" class="pending-update"> |
490 | 78 | <h3>Updating repository...</h3> | 78 | <h3>Updating repository...</h3> |
491 | 79 | <p> | 79 | <p> |
492 | 80 | Launchpad is processing new changes to this repository which will | 80 | Launchpad is processing new changes to this repository which will |
493 | 81 | 81 | ||
494 | === modified file 'lib/lp/codehosting/tests/test_branchdistro.py' | |||
495 | --- lib/lp/codehosting/tests/test_branchdistro.py 2012-02-15 17:29:54 +0000 | |||
496 | +++ lib/lp/codehosting/tests/test_branchdistro.py 2017-11-08 11:16:30 +0000 | |||
497 | @@ -273,12 +273,12 @@ | |||
498 | 273 | new_branch).getScannerData() | 273 | new_branch).getScannerData() |
499 | 274 | self.assertEqual(old_ancestry, new_ancestry) | 274 | self.assertEqual(old_ancestry, new_ancestry) |
500 | 275 | self.assertEqual(old_history, new_history) | 275 | self.assertEqual(old_history, new_history) |
501 | 276 | self.assertFalse(new_branch.pending_writes) | ||
502 | 277 | self.assertIs(None, new_branch.stacked_on) | 276 | self.assertIs(None, new_branch.stacked_on) |
503 | 278 | self.assertEqual(new_branch, db_branch.stacked_on) | 277 | self.assertEqual(new_branch, db_branch.stacked_on) |
504 | 279 | # The script doesn't have permission to create branch jobs, but just | 278 | # The script doesn't have permission to create branch jobs, but just |
505 | 280 | # to be insanely paranoid. | 279 | # to be insanely paranoid. |
506 | 281 | switch_dbuser('launchpad') | 280 | switch_dbuser('launchpad') |
507 | 281 | self.assertFalse(new_branch.pending_writes) | ||
508 | 282 | scan_jobs = list(getUtility(IBranchScanJobSource).iterReady()) | 282 | scan_jobs = list(getUtility(IBranchScanJobSource).iterReady()) |
509 | 283 | self.assertEqual(existing_scan_job_count, len(scan_jobs)) | 283 | self.assertEqual(existing_scan_job_count, len(scan_jobs)) |
510 | 284 | 284 |