Merge lp:~stub/launchpad/pending-db-changes into lp:launchpad

Proposed by Stuart Bishop
Status: Rejected
Rejected by: Stuart Bishop
Proposed branch: lp:~stub/launchpad/pending-db-changes
Merge into: lp:launchpad
Diff against target: 1281 lines (+989/-8) (has conflicts)
16 files modified
database/schema/patch-2209-21-4.sql (+111/-0)
lib/lp/app/browser/tales.py (+14/-5)
lib/lp/app/templates/banner-macros.pt (+50/-0)
lib/lp/archivepublisher/debian_installer.py (+27/-0)
lib/lp/archivepublisher/tests/test_debian_installer.py (+150/-0)
lib/lp/archivepublisher/tests/test_dist_upgrader.py (+89/-0)
lib/lp/bugs/browser/bugtarget.py (+8/-0)
lib/lp/bugs/browser/tests/test_bugtarget_filebug.py (+93/-0)
lib/lp/bugs/javascript/filebug.js (+78/-0)
lib/lp/bugs/javascript/tests/test_filebug.html (+106/-0)
lib/lp/bugs/javascript/tests/test_filebug.js (+116/-0)
lib/lp/code/model/tests/test_branch.py (+35/-0)
lib/lp/code/model/tests/test_branchlookup.py (+6/-0)
lib/lp/code/model/tests/test_branchvisibility.py (+9/-0)
lib/lp/soyuz/tests/test_distroseriesqueue_debian_installer.py (+77/-0)
lib/lp/testing/factory.py (+20/-3)
Text conflict in lib/lp/app/browser/tales.py
Text conflict in lib/lp/app/templates/banner-macros.pt
Text conflict in lib/lp/archivepublisher/debian_installer.py
Text conflict in lib/lp/archivepublisher/tests/test_debian_installer.py
Text conflict in lib/lp/archivepublisher/tests/test_dist_upgrader.py
Text conflict in lib/lp/bugs/browser/bugtarget.py
Text conflict in lib/lp/bugs/browser/tests/test_bugtarget_filebug.py
Text conflict in lib/lp/bugs/javascript/filebug.js
Text conflict in lib/lp/bugs/javascript/tests/test_filebug.html
Text conflict in lib/lp/bugs/javascript/tests/test_filebug.js
Text conflict in lib/lp/code/model/tests/test_branch.py
Text conflict in lib/lp/code/model/tests/test_branchlookup.py
Text conflict in lib/lp/code/model/tests/test_branchvisibility.py
Text conflict in lib/lp/soyuz/tests/test_distroseriesqueue_debian_installer.py
Text conflict in lib/lp/testing/factory.py
To merge this branch: bzr merge lp:~stub/launchpad/pending-db-changes
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+108323@code.launchpad.net

Description of the change

= Summary =

Turns out pgstattuple doesn't support GIN indexes, so one of our monitoring scripts fails.

== Proposed fix ==

Skip GIN indexes. Leave a better fix (such as replacing pgstattuple output with guesses) is better left to lp:pgdbr work which will replace this stuff and make it non-LP specific.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'database/schema/patch-2209-21-4.sql'
2--- database/schema/patch-2209-21-4.sql 1970-01-01 00:00:00 +0000
3+++ database/schema/patch-2209-21-4.sql 2012-06-01 11:55:34 +0000
4@@ -0,0 +1,111 @@
5+CREATE OR REPLACE FUNCTION update_database_disk_utilization() RETURNS void
6+ LANGUAGE sql SECURITY DEFINER
7+ SET search_path TO public
8+ AS $$
9+ INSERT INTO DatabaseDiskUtilization
10+ SELECT
11+ CURRENT_TIMESTAMP AT TIME ZONE 'UTC',
12+ namespace, name,
13+ sub_namespace, sub_name,
14+ kind,
15+ (namespace || '.' || name || COALESCE(
16+ '/' || sub_namespace || '.' || sub_name, '')) AS sort,
17+ (stat).table_len,
18+ (stat).tuple_count,
19+ (stat).tuple_len,
20+ (stat).tuple_percent,
21+ (stat).dead_tuple_count,
22+ (stat).dead_tuple_len,
23+ (stat).dead_tuple_percent,
24+ (stat).free_space,
25+ (stat).free_percent
26+ FROM (
27+ -- Tables
28+ SELECT
29+ pg_namespace.nspname AS namespace,
30+ pg_class.relname AS name,
31+ NULL AS sub_namespace,
32+ NULL AS sub_name,
33+ pg_class.relkind AS kind,
34+ pgstattuple(pg_class.oid) AS stat
35+ FROM pg_class, pg_namespace
36+ WHERE
37+ pg_class.relnamespace = pg_namespace.oid
38+ AND pg_class.relkind = 'r'
39+ AND pg_table_is_visible(pg_class.oid)
40+
41+ UNION ALL
42+
43+ -- Indexes
44+ SELECT
45+ pg_namespace_table.nspname AS namespace,
46+ pg_class_table.relname AS name,
47+ pg_namespace_index.nspname AS sub_namespace,
48+ pg_class_index.relname AS sub_name,
49+ pg_class_index.relkind AS kind,
50+ pgstattuple(pg_class_index.oid) AS stat
51+ FROM
52+ pg_namespace AS pg_namespace_table,
53+ pg_namespace AS pg_namespace_index,
54+ pg_class AS pg_class_table,
55+ pg_class AS pg_class_index,
56+ pg_index,
57+ pg_am
58+ WHERE
59+ pg_class_index.relkind = 'i'
60+ AND pg_am.amname <> 'gin' -- pgstattuple doesn't support GIN
61+ AND pg_table_is_visible(pg_class_table.oid)
62+ AND pg_class_index.relnamespace = pg_namespace_index.oid
63+ AND pg_class_table.relnamespace = pg_namespace_table.oid
64+ AND pg_class_index.relam = pg_am.oid
65+ AND pg_index.indexrelid = pg_class_index.oid
66+ AND pg_index.indrelid = pg_class_table.oid
67+
68+ UNION ALL
69+
70+ -- TOAST tables
71+ SELECT
72+ pg_namespace_table.nspname AS namespace,
73+ pg_class_table.relname AS name,
74+ pg_namespace_toast.nspname AS sub_namespace,
75+ pg_class_toast.relname AS sub_name,
76+ pg_class_toast.relkind AS kind,
77+ pgstattuple(pg_class_toast.oid) AS stat
78+ FROM
79+ pg_namespace AS pg_namespace_table,
80+ pg_namespace AS pg_namespace_toast,
81+ pg_class AS pg_class_table,
82+ pg_class AS pg_class_toast
83+ WHERE
84+ pg_class_toast.relnamespace = pg_namespace_toast.oid
85+ AND pg_table_is_visible(pg_class_table.oid)
86+ AND pg_class_table.relnamespace = pg_namespace_table.oid
87+ AND pg_class_toast.oid = pg_class_table.reltoastrelid
88+
89+ UNION ALL
90+
91+ -- TOAST indexes
92+ SELECT
93+ pg_namespace_table.nspname AS namespace,
94+ pg_class_table.relname AS name,
95+ pg_namespace_index.nspname AS sub_namespace,
96+ pg_class_index.relname AS sub_name,
97+ pg_class_index.relkind AS kind,
98+ pgstattuple(pg_class_index.oid) AS stat
99+ FROM
100+ pg_namespace AS pg_namespace_table,
101+ pg_namespace AS pg_namespace_index,
102+ pg_class AS pg_class_table,
103+ pg_class AS pg_class_index,
104+ pg_class AS pg_class_toast
105+ WHERE
106+ pg_class_table.relnamespace = pg_namespace_table.oid
107+ AND pg_table_is_visible(pg_class_table.oid)
108+ AND pg_class_index.relnamespace = pg_namespace_index.oid
109+ AND pg_class_table.reltoastrelid = pg_class_toast.oid
110+ AND pg_class_index.oid = pg_class_toast.reltoastidxid
111+ ) AS whatever;
112+$$;
113+
114+INSERT INTO LaunchpadDatabaseRevision VALUES (2209, 21, 4);
115+
116
117=== modified file 'lib/lp/app/browser/tales.py'
118--- lib/lp/app/browser/tales.py 2012-05-31 02:20:41 +0000
119+++ lib/lp/app/browser/tales.py 2012-06-01 11:55:34 +0000
120@@ -666,11 +666,20 @@
121 css_classes.add('private')
122 css_classes.add('global-notification-visible')
123 else:
124- css_classes.add('public')
125- beta = getattr(view, 'beta_features', [])
126- if beta != []:
127- css_classes.add('global-notification-visible')
128- return ' '.join(list(css_classes))
129+<<<<<<< TREE
130+ css_classes.add('public')
131+ beta = getattr(view, 'beta_features', [])
132+ if beta != []:
133+ css_classes.add('global-notification-visible')
134+ return ' '.join(list(css_classes))
135+=======
136+ css_classes.add('public')
137+ beta = getattr(view, 'beta_features', [])
138+ if beta != []:
139+ css_classes.add('global-notification-visible')
140+ return ' '.join(list(css_classes))
141+
142+>>>>>>> MERGE-SOURCE
143
144 def _getSaneBreadcrumbDetail(self, breadcrumb):
145 text = breadcrumb.detail
146
147=== modified file 'lib/lp/app/browser/tests/test_launchpad.py'
148=== modified file 'lib/lp/app/doc/tales.txt'
149=== modified file 'lib/lp/app/templates/banner-macros.pt'
150--- lib/lp/app/templates/banner-macros.pt 2012-05-30 13:59:24 +0000
151+++ lib/lp/app/templates/banner-macros.pt 2012-06-01 11:55:34 +0000
152@@ -1,3 +1,4 @@
153+<<<<<<< TREE
154 <macros
155 xmlns="http://www.w3.org/1999/xhtml"
156 xmlns:tal="http://xml.zope.org/namespaces/tal"
157@@ -54,3 +55,52 @@
158 </metal:beta>
159
160 </macros>
161+=======
162+<macros
163+ xmlns="http://www.w3.org/1999/xhtml"
164+ xmlns:tal="http://xml.zope.org/namespaces/tal"
165+ xmlns:metal="http://xml.zope.org/namespaces/metal"
166+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
167+ i18n:domain="launchpad"
168+ tal:omit-tag=""
169+>
170+
171+<metal:privacy define-macro="privacy-banner">
172+ <tal:show-banner condition="view/private">
173+ <div class="yui3-widget yui3-banner yui3-privacybanner">
174+ <div class="yui3-privacybanner-content">
175+ <div class="global-notification">
176+ <span class="sprite notification-private"></span>
177+ <span class="banner-text">The information on this page is private.</span>
178+ </div>
179+ </div>
180+ </div>
181+ </tal:show-banner>
182+</metal:privacy>
183+
184+<metal:beta define-macro="beta-banner">
185+ <tal:show-banner condition="view/beta_features">
186+ <div class="yui3-widget yui3-banner yui3-betabanner">
187+ <div class="yui3-betabanner-content">
188+ <div class="global-notification">
189+ <span class="beta-warning">BETA!</span>
190+ <span class="banner-text">
191+ Some parts of this page are in beta:&nbsp;
192+ <span class="beta-feature">
193+ <tal:features
194+ repeat="feature view/beta_features">
195+ <tal:feature replace="feature/title" />
196+ <tal:link condition="feature/url">
197+ (<a tal:attributes="href feature/url" class="info-link">read more</a>)
198+ </tal:link>
199+ </tal:features>
200+ </span>
201+ </span>
202+ </div>
203+ </div>
204+ </div>
205+ </tal:show-banner>
206+</metal:beta>
207+
208+</macros>
209+>>>>>>> MERGE-SOURCE
210
211=== modified file 'lib/lp/archivepublisher/debian_installer.py'
212--- lib/lp/archivepublisher/debian_installer.py 2012-05-30 10:25:43 +0000
213+++ lib/lp/archivepublisher/debian_installer.py 2012-06-01 11:55:34 +0000
214@@ -16,7 +16,22 @@
215 import os
216 import shutil
217
218+<<<<<<< TREE
219 from lp.archivepublisher.customupload import CustomUpload
220+=======
221+from lp.archivepublisher.customupload import (
222+ CustomUpload,
223+ CustomUploadError,
224+ )
225+
226+
227+class DebianInstallerAlreadyExists(CustomUploadError):
228+ """A build for this type, architecture, and version already exists."""
229+ def __init__(self, arch, version):
230+ message = ('installer build %s for architecture %s already exists' %
231+ (arch, version))
232+ CustomUploadError.__init__(self, message)
233+>>>>>>> MERGE-SOURCE
234
235
236 class DebianInstallerUpload(CustomUpload):
237@@ -42,13 +57,21 @@
238
239 def setTargetDirectory(self, archive_root, tarfile_path, distroseries):
240 tarfile_base = os.path.basename(tarfile_path)
241+<<<<<<< TREE
242 _, self.version, self.arch = tarfile_base.split("_")
243 self.arch = self.arch.split(".")[0]
244
245+=======
246+ components = tarfile_base.split('_')
247+ self.version = components[1]
248+ self.arch = components[2].split('.')[0]
249+
250+>>>>>>> MERGE-SOURCE
251 self.targetdir = os.path.join(
252 archive_root, 'dists', distroseries, 'main',
253 'installer-%s' % self.arch)
254
255+<<<<<<< TREE
256 @classmethod
257 def getSeriesKey(cls, tarfile_path):
258 try:
259@@ -56,6 +79,10 @@
260 return arch.split(".")[0]
261 except ValueError:
262 return None
263+=======
264+ if os.path.exists(os.path.join(self.targetdir, self.version)):
265+ raise DebianInstallerAlreadyExists(self.arch, self.version)
266+>>>>>>> MERGE-SOURCE
267
268 def extract(self):
269 CustomUpload.extract(self)
270
271=== modified file 'lib/lp/archivepublisher/tests/test_debian_installer.py'
272--- lib/lp/archivepublisher/tests/test_debian_installer.py 2012-05-30 10:25:43 +0000
273+++ lib/lp/archivepublisher/tests/test_debian_installer.py 2012-06-01 11:55:34 +0000
274@@ -1,3 +1,4 @@
275+<<<<<<< TREE
276 # Copyright 2012 Canonical Ltd. This software is licensed under the
277 # GNU Affero General Public License version 3 (see the file LICENSE).
278
279@@ -166,3 +167,152 @@
280 "package_1.0.tar.gz"))
281 self.assertIsNone(DebianInstallerUpload.getSeriesKey(
282 "one_two_three_four_5.tar.gz"))
283+=======
284+# Copyright 2012 Canonical Ltd. This software is licensed under the
285+# GNU Affero General Public License version 3 (see the file LICENSE).
286+
287+"""Test debian-installer custom uploads.
288+
289+See also lp.soyuz.tests.test_distroseriesqueue_debian_installer for
290+high-level tests of debian-installer upload and queue manipulation.
291+"""
292+
293+import os
294+
295+from lp.archivepublisher.customupload import CustomUploadBadUmask
296+from lp.archivepublisher.debian_installer import (
297+ DebianInstallerAlreadyExists,
298+ process_debian_installer,
299+ )
300+from lp.services.tarfile_helpers import LaunchpadWriteTarFile
301+from lp.testing import TestCase
302+
303+
304+class TestDebianInstaller(TestCase):
305+
306+ def setUp(self):
307+ super(TestDebianInstaller, self).setUp()
308+ self.temp_dir = self.makeTemporaryDirectory()
309+ self.suite = "distroseries"
310+ # CustomUpload.installFiles requires a umask of 022.
311+ old_umask = os.umask(022)
312+ self.addCleanup(os.umask, old_umask)
313+
314+ def openArchive(self):
315+ self.version = "20070214ubuntu1"
316+ self.arch = "i386"
317+ self.path = os.path.join(
318+ self.temp_dir,
319+ "debian-installer-images_%s_%s.tar.gz" % (self.version, self.arch))
320+ self.buffer = open(self.path, "wb")
321+ self.archive = LaunchpadWriteTarFile(self.buffer)
322+
323+ def addFile(self, path, contents):
324+ self.archive.add_file(
325+ "installer-%s/%s/%s" % (self.arch, self.version, path), contents)
326+
327+ def addSymlink(self, path, target):
328+ self.archive.add_symlink(
329+ "installer-%s/%s/%s" % (self.arch, self.version, path), target)
330+
331+ def process(self):
332+ self.archive.close()
333+ self.buffer.close()
334+ process_debian_installer(self.temp_dir, self.path, self.suite)
335+
336+ def getInstallerPath(self, versioned_filename=None):
337+ installer_path = os.path.join(
338+ self.temp_dir, "dists", self.suite, "main",
339+ "installer-%s" % self.arch)
340+ if versioned_filename is not None:
341+ installer_path = os.path.join(
342+ installer_path, self.version, versioned_filename)
343+ return installer_path
344+
345+ def test_basic(self):
346+ # Processing a simple correct tar file succeeds.
347+ self.openArchive()
348+ self.addFile("hello", "world")
349+ self.process()
350+
351+ def test_already_exists(self):
352+ # If the target directory already exists, processing fails.
353+ self.openArchive()
354+ os.makedirs(self.getInstallerPath("."))
355+ self.assertRaises(DebianInstallerAlreadyExists, self.process)
356+
357+ def test_bad_umask(self):
358+ # The umask must be 022 to avoid incorrect permissions.
359+ self.openArchive()
360+ self.addFile("dir/file", "foo")
361+ os.umask(002) # cleanup already handled by setUp
362+ self.assertRaises(CustomUploadBadUmask, self.process)
363+
364+ def test_current_symlink(self):
365+ # A "current" symlink is created to the last version.
366+ self.openArchive()
367+ self.addFile("hello", "world")
368+ self.process()
369+ installer_path = self.getInstallerPath()
370+ self.assertContentEqual(
371+ [self.version, "current"], os.listdir(installer_path))
372+ self.assertEqual(
373+ self.version, os.readlink(os.path.join(installer_path, "current")))
374+
375+ def test_correct_file(self):
376+ # Files in the tarball are extracted correctly.
377+ self.openArchive()
378+ directory = ("images/netboot/ubuntu-installer/i386/"
379+ "pxelinux.cfg.serial-9600")
380+ filename = os.path.join(directory, "default")
381+ long_filename = os.path.join(
382+ directory, "very_very_very_very_very_very_long_filename")
383+ self.addFile(filename, "hey")
384+ self.addFile(long_filename, "long")
385+ self.process()
386+ with open(self.getInstallerPath(filename)) as f:
387+ self.assertEqual("hey", f.read())
388+ with open(self.getInstallerPath(long_filename)) as f:
389+ self.assertEqual("long", f.read())
390+
391+ def test_correct_symlink(self):
392+ # Symbolic links in the tarball are extracted correctly.
393+ self.openArchive()
394+ foo_path = "images/netboot/foo"
395+ foo_target = "ubuntu-installer/i386/pxelinux.cfg.serial-9600/default"
396+ link_to_dir_path = "images/netboot/link_to_dir"
397+ link_to_dir_target = "ubuntu-installer/i386/pxelinux.cfg.serial-9600"
398+ self.addSymlink(foo_path, foo_target)
399+ self.addSymlink(link_to_dir_path, link_to_dir_target)
400+ self.process()
401+ self.assertEqual(
402+ foo_target, os.readlink(self.getInstallerPath(foo_path)))
403+ self.assertEqual(
404+ link_to_dir_target,
405+ os.path.normpath(os.readlink(
406+ self.getInstallerPath(link_to_dir_path))))
407+
408+ def test_top_level_permissions(self):
409+ # Top-level directories are set to mode 0755 (see bug 107068).
410+ self.openArchive()
411+ self.addFile("hello", "world")
412+ self.process()
413+ installer_path = self.getInstallerPath()
414+ self.assertEqual(0755, os.stat(installer_path).st_mode & 0777)
415+ self.assertEqual(
416+ 0755,
417+ os.stat(os.path.join(installer_path, os.pardir)).st_mode & 0777)
418+
419+ def test_extracted_permissions(self):
420+ # Extracted files and directories are set to 0644/0755.
421+ self.openArchive()
422+ directory = ("images/netboot/ubuntu-installer/i386/"
423+ "pxelinux.cfg.serial-9600")
424+ filename = os.path.join(directory, "default")
425+ self.addFile(filename, "hey")
426+ self.process()
427+ self.assertEqual(
428+ 0644, os.stat(self.getInstallerPath(filename)).st_mode & 0777)
429+ self.assertEqual(
430+ 0755, os.stat(self.getInstallerPath(directory)).st_mode & 0777)
431+>>>>>>> MERGE-SOURCE
432
433=== modified file 'lib/lp/archivepublisher/tests/test_dist_upgrader.py'
434--- lib/lp/archivepublisher/tests/test_dist_upgrader.py 2012-05-30 10:25:43 +0000
435+++ lib/lp/archivepublisher/tests/test_dist_upgrader.py 2012-06-01 11:55:34 +0000
436@@ -1,3 +1,4 @@
437+<<<<<<< TREE
438 # Copyright 2012 Canonical Ltd. This software is licensed under the
439 # GNU Affero General Public License version 3 (see the file LICENSE).
440
441@@ -104,3 +105,91 @@
442 "package_1.0.tar.gz"))
443 self.assertIsNone(DistUpgraderUpload.getSeriesKey(
444 "one_two_three_four_5.tar.gz"))
445+=======
446+# Copyright 2012 Canonical Ltd. This software is licensed under the
447+# GNU Affero General Public License version 3 (see the file LICENSE).
448+
449+"""Test dist-upgrader custom uploads.
450+
451+See also lp.soyuz.tests.test_distroseriesqueue_dist_upgrader for high-level
452+tests of dist-upgrader upload and queue manipulation.
453+"""
454+
455+import os
456+
457+from lp.archivepublisher.customupload import CustomUploadBadUmask
458+from lp.archivepublisher.dist_upgrader import (
459+ DistUpgraderAlreadyExists,
460+ DistUpgraderBadVersion,
461+ process_dist_upgrader,
462+ )
463+from lp.services.tarfile_helpers import LaunchpadWriteTarFile
464+from lp.testing import TestCase
465+
466+
467+class TestDistUpgrader(TestCase):
468+
469+ def setUp(self):
470+ super(TestDistUpgrader, self).setUp()
471+ self.temp_dir = self.makeTemporaryDirectory()
472+ self.suite = "distroseries"
473+ # CustomUpload.installFiles requires a umask of 022.
474+ old_umask = os.umask(022)
475+ self.addCleanup(os.umask, old_umask)
476+
477+ def openArchive(self, version):
478+ self.path = os.path.join(
479+ self.temp_dir, "dist-upgrader_%s_all.tar.gz" % version)
480+ self.buffer = open(self.path, "wb")
481+ self.archive = LaunchpadWriteTarFile(self.buffer)
482+
483+ def process(self):
484+ self.archive.close()
485+ self.buffer.close()
486+ process_dist_upgrader(self.temp_dir, self.path, self.suite)
487+
488+ def getUpgraderPath(self):
489+ return os.path.join(
490+ self.temp_dir, "dists", self.suite, "main", "dist-upgrader-all")
491+
492+ def test_basic(self):
493+ # Processing a simple correct tar file works.
494+ self.openArchive("20060302.0120")
495+ self.archive.add_file("20060302.0120/hello", "world")
496+ self.process()
497+
498+ def test_already_exists(self):
499+ # If the target directory already exists, processing fails.
500+ self.openArchive("20060302.0120")
501+ self.archive.add_file("20060302.0120/hello", "world")
502+ os.makedirs(os.path.join(self.getUpgraderPath(), "20060302.0120"))
503+ self.assertRaises(DistUpgraderAlreadyExists, self.process)
504+
505+ def test_bad_umask(self):
506+ # The umask must be 022 to avoid incorrect permissions.
507+ self.openArchive("20060302.0120")
508+ self.archive.add_file("20060302.0120/file", "foo")
509+ os.umask(002) # cleanup already handled by setUp
510+ self.assertRaises(CustomUploadBadUmask, self.process)
511+
512+ def test_current_symlink(self):
513+ # A "current" symlink is created to the last version.
514+ self.openArchive("20060302.0120")
515+ self.archive.add_file("20060302.0120/hello", "world")
516+ self.process()
517+ upgrader_path = self.getUpgraderPath()
518+ self.assertContentEqual(
519+ ["20060302.0120", "current"], os.listdir(upgrader_path))
520+ self.assertEqual(
521+ "20060302.0120",
522+ os.readlink(os.path.join(upgrader_path, "current")))
523+ self.assertContentEqual(
524+ ["hello"],
525+ os.listdir(os.path.join(upgrader_path, "20060302.0120")))
526+
527+ def test_bad_version(self):
528+ # Bad versions in the tarball are refused.
529+ self.openArchive("20070219.1234")
530+ self.archive.add_file("foobar/foobar/dapper.tar.gz", "")
531+ self.assertRaises(DistUpgraderBadVersion, self.process)
532+>>>>>>> MERGE-SOURCE
533
534=== modified file 'lib/lp/bugs/browser/bugtarget.py'
535--- lib/lp/bugs/browser/bugtarget.py 2012-05-30 00:51:34 +0000
536+++ lib/lp/bugs/browser/bugtarget.py 2012-06-01 11:55:34 +0000
537@@ -388,6 +388,7 @@
538 if self.redirect_ubuntu_filebug:
539 pass
540 LaunchpadFormView.initialize(self)
541+<<<<<<< TREE
542 cache = IJSONRequestCache(self.request)
543 cache.objects['enable_bugfiling_duplicate_search'] = (
544 IProjectGroup.providedBy(self.context)
545@@ -408,6 +409,13 @@
546 excluded_items=[BugTaskImportance.UNKNOWN])
547 cache.objects['bugtask_importance_data'] = bugtask_importance_data
548 if (self.extra_data_token is not None and
549+=======
550+ cache = IJSONRequestCache(self.request)
551+ cache.objects['enable_bugfiling_duplicate_search'] = (
552+ IProjectGroup.providedBy(self.context)
553+ or self.context.enable_bugfiling_duplicate_search)
554+ if (self.extra_data_token is not None and
555+>>>>>>> MERGE-SOURCE
556 not self.extra_data_to_process):
557 # self.extra_data has been initialized in publishTraverse().
558 if self.extra_data.initial_summary:
559
560=== modified file 'lib/lp/bugs/browser/tests/test_bugtarget_filebug.py'
561--- lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2012-05-30 00:51:34 +0000
562+++ lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2012-06-01 11:55:34 +0000
563@@ -26,11 +26,18 @@
564 BugTaskStatus,
565 )
566 from lp.bugs.publisher import BugsLayer
567+<<<<<<< TREE
568 from lp.registry.enums import (
569 InformationType,
570 PRIVATE_INFORMATION_TYPES,
571 PUBLIC_INFORMATION_TYPES,
572 )
573+=======
574+from lp.registry.enums import (
575+ InformationType,
576+ PRIVATE_INFORMATION_TYPES,
577+ )
578+>>>>>>> MERGE-SOURCE
579 from lp.services.features.testing import FeatureFixture
580 from lp.services.webapp.servers import LaunchpadTestRequest
581 from lp.testing import (
582@@ -477,6 +484,7 @@
583 notification.message
584 for notification in view.request.response.notifications])
585 self.assertIn("Thank you for your bug report.", msg)
586+<<<<<<< TREE
587
588
589 class TestFileBugGuidelinesRequestCache(TestCaseWithFactory):
590@@ -609,3 +617,88 @@
591 login_person(user)
592 view = create_initialized_view(project, '+filebug', principal=user)
593 self._assert_cache_values(view, True)
594+=======
595+
596+
597+class TestFileBugGuidelinesRequestCache(TestCaseWithFactory):
598+ # Tests to ensure the request cache contains the expected values for
599+ # file bug guidelines views.
600+
601+ layer = DatabaseFunctionalLayer
602+
603+ def _assert_cache_values(self, view, private_bugs, duplicate_search):
604+ cache = IJSONRequestCache(view.request).objects
605+ self.assertContentEqual(cache['private_types'], [
606+ type.name for type in PRIVATE_INFORMATION_TYPES])
607+ self.assertEqual(cache['bug_private_by_default'], private_bugs)
608+ self.assertEqual(
609+ cache['enable_bugfiling_duplicate_search'], duplicate_search)
610+
611+ def test_product(self):
612+ project = self.factory.makeProduct(official_malone=True)
613+ user = self.factory.makePerson()
614+ login_person(user)
615+ view = create_initialized_view(project,
616+ '+filebug-reporting-guidelines', principal=user)
617+ self._assert_cache_values(view, False, True)
618+
619+ def test_product_default_private(self):
620+ product = self.factory.makeProduct(official_malone=True)
621+ removeSecurityProxy(product).private_bugs = True
622+ user = self.factory.makePerson()
623+ login_person(user)
624+ view = create_initialized_view(product,
625+ '+filebug-reporting-guidelines', principal=user)
626+ self._assert_cache_values(view, True, True)
627+
628+ def test_product_no_duplicate_search(self):
629+ product = self.factory.makeProduct(official_malone=True)
630+ removeSecurityProxy(product).enable_bugfiling_duplicate_search = False
631+ user = self.factory.makePerson()
632+ login_person(user)
633+ view = create_initialized_view(product,
634+ '+filebug-reporting-guidelines', principal=user)
635+ self._assert_cache_values(view, False, False)
636+
637+ def test_project_group(self):
638+ project = self.factory.makeProject()
639+ user = self.factory.makePerson()
640+ login_person(user)
641+ view = create_initialized_view(project,
642+ '+filebug-reporting-guidelines', principal=user)
643+ self._assert_cache_values(view, False, True)
644+
645+
646+class TestFileBugRequestCache(TestCaseWithFactory):
647+ # Tests to ensure the request cache contains the expected values for
648+ # file bug views.
649+
650+ layer = DatabaseFunctionalLayer
651+
652+ def _assert_cache_values(self, view, duplicate_search):
653+ cache = IJSONRequestCache(view.request).objects
654+ self.assertEqual(
655+ cache['enable_bugfiling_duplicate_search'], duplicate_search)
656+
657+ def test_product(self):
658+ project = self.factory.makeProduct(official_malone=True)
659+ user = self.factory.makePerson()
660+ login_person(user)
661+ view = create_initialized_view(project, '+filebug', principal=user)
662+ self._assert_cache_values(view, True)
663+
664+ def test_product_no_duplicate_search(self):
665+ product = self.factory.makeProduct(official_malone=True)
666+ removeSecurityProxy(product).enable_bugfiling_duplicate_search = False
667+ user = self.factory.makePerson()
668+ login_person(user)
669+ view = create_initialized_view(product, '+filebug', principal=user)
670+ self._assert_cache_values(view, False)
671+
672+ def test_project_group(self):
673+ project = self.factory.makeProject()
674+ user = self.factory.makePerson()
675+ login_person(user)
676+ view = create_initialized_view(project, '+filebug', principal=user)
677+ self._assert_cache_values(view, True)
678+>>>>>>> MERGE-SOURCE
679
680=== modified file 'lib/lp/bugs/browser/tests/test_bugtask.py'
681=== modified file 'lib/lp/bugs/javascript/filebug.js'
682--- lib/lp/bugs/javascript/filebug.js 2012-05-28 12:34:29 +0000
683+++ lib/lp/bugs/javascript/filebug.js 2012-06-01 11:55:34 +0000
684@@ -1,3 +1,4 @@
685+<<<<<<< TREE
686 /* Copyright 2012 Canonical Ltd. This software is licensed under the
687 * GNU Affero General Public License version 3 (see the file LICENSE).
688 *
689@@ -81,3 +82,80 @@
690 "base", "node", "event", "node-event-delegate", "lazr.choiceedit",
691 "lp.app.banner.privacy", "lp.app.choice",
692 "lp.bugs.filebug_dupefinder"]});
693+=======
694+/* Copyright 2012 Canonical Ltd. This software is licensed under the
695+ * GNU Affero General Public License version 3 (see the file LICENSE).
696+ *
697+ * Provide functionality for the file bug pages.
698+ *
699+ * @module bugs
700+ * @submodule filebug
701+ */
702+YUI.add('lp.bugs.filebug', function(Y) {
703+
704+var namespace = Y.namespace('lp.bugs.filebug');
705+
706+// For tests.
707+var skip_animation;
708+
709+var setup_filebug = function(skip_anim) {
710+ skip_animation = skip_anim;
711+ if (LP.cache.enable_bugfiling_duplicate_search) {
712+ Y.lp.bugs.filebug_dupefinder.setup_dupe_finder();
713+ Y.lp.bugs.filebug_dupefinder.setup_dupes();
714+ }
715+ var search_button = Y.one(Y.DOM.byId('field.actions.projectgroupsearch'));
716+ if (Y.Lang.isValue(search_button )) {
717+ search_button.set('value', 'Check again');
718+ }
719+ if (LP.cache.show_information_type_in_ui) {
720+ setup_information_type();
721+ } else {
722+ setup_security_related();
723+ }
724+ var filebug_privacy_text = "This report will be private. " +
725+ "You can disclose it later.";
726+ update_privacy_banner(
727+ LP.cache.bug_private_by_default, filebug_privacy_text);
728+};
729+
730+var update_privacy_banner = function(show, banner_text) {
731+ var banner = Y.lp.app.banner.privacy.getPrivacyBanner(
732+ banner_text, skip_animation);
733+ if (show) {
734+ banner.show();
735+ } else {
736+ banner.hide();
737+ }
738+};
739+
740+var setup_information_type = function() {
741+ var itypes_table = Y.one('.radio-button-widget');
742+ itypes_table.delegate('click', function() {
743+ var private_type = (Y.Array.indexOf(
744+ LP.cache.private_types, this.get('value')) >= 0);
745+ update_privacy_banner(private_type);
746+ }, "input[name='field.information_type']");
747+};
748+
749+var setup_security_related = function() {
750+ var sec = Y.one('[id="field.security_related"]');
751+ if (!Y.Lang.isValue(sec)) {
752+ return;
753+ }
754+ var notification_text = "This report will be private " +
755+ "because it is a security " +
756+ "vulnerability. You can " +
757+ "disclose it later.";
758+ sec.on('change', function() {
759+ var checked = sec.get('checked');
760+ update_privacy_banner(checked, notification_text);
761+ });
762+};
763+
764+namespace.setup_filebug = setup_filebug;
765+
766+}, "0.1", {"requires": [
767+ "base", "node", "event", "node-event-delegate",
768+ "lp.app.banner.privacy", "lp.bugs.filebug_dupefinder"]});
769+>>>>>>> MERGE-SOURCE
770
771=== modified file 'lib/lp/bugs/javascript/tests/test_filebug.html'
772--- lib/lp/bugs/javascript/tests/test_filebug.html 2012-05-28 12:34:29 +0000
773+++ lib/lp/bugs/javascript/tests/test_filebug.html 2012-06-01 11:55:34 +0000
774@@ -1,3 +1,4 @@
775+<<<<<<< TREE
776 <!DOCTYPE html>
777 <!--
778 Copyright 2012 Canonical Ltd. This software is licensed under the
779@@ -117,3 +118,108 @@
780 </script>
781 </body>
782 </html>
783+=======
784+<!DOCTYPE html>
785+<!--
786+Copyright 2012 Canonical Ltd. This software is licensed under the
787+GNU Affero General Public License version 3 (see the file LICENSE).
788+-->
789+
790+<html>
791+ <head>
792+ <title>File Bug View Tests</title>
793+
794+ <!-- YUI and test setup -->
795+ <script type="text/javascript"
796+ src="../../../../../build/js/yui/yui/yui.js">
797+ </script>
798+ <link rel="stylesheet"
799+ href="../../../../../build/js/yui/console/assets/console-core.css" />
800+ <link rel="stylesheet"
801+ href="../../../../../build/js/yui/console/assets/skins/sam/console.css" />
802+ <link rel="stylesheet"
803+ href="../../../../../build/js/yui/test/assets/skins/sam/test.css" />
804+
805+ <script type="text/javascript"
806+ src="../../../../../build/js/lp/app/testing/testrunner.js"></script>
807+
808+ <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
809+
810+ <!-- Dependencies -->
811+ <script type="text/javascript"
812+ src="../../../../../build/js/lp/app/mustache.js"></script>
813+ <script type="text/javascript"
814+ src="../../../../../build/js/lp/app/expander.js"></script>
815+ <script type="text/javascript"
816+ src="../../../../../build/js/lp/app/formoverlay/formoverlay.js"></script>
817+ <script type="text/javascript"
818+ src="../../../../../build/js/lp/app/formwidgets/formwidgets.js"></script>
819+ <script type="text/javascript"
820+ src="../../../../../build/js/lp/app/formwidgets/resizing_textarea.js"></script>
821+ <script type="text/javascript"
822+ src="../../../../../build/js/lp/app/overlay/overlay.js"></script>
823+ <script type="text/javascript"
824+ src="../../../../../build/js/lp/app/anim/anim.js"></script>
825+ <script type="text/javascript"
826+ src="../../../../../build/js/lp/bugs/filebug_dupefinder.js"></script>
827+ <script type="text/javascript"
828+ src="../../../../../build/js/lp/app/effects/effects.js"></script>
829+ <script type="text/javascript"
830+ src="../../../../../build/js/lp/app/extras/extras.js"></script>
831+ <script type="text/javascript"
832+ src="../../../../../build/js/lp/app/banners/banner.js"></script>
833+ <script type="text/javascript"
834+ src="../../../../../build/js/lp/app/banners/privacy.js"></script>
835+
836+ <!-- The module under test. -->
837+ <script type="text/javascript" src="../filebug.js"></script>
838+
839+ <!-- The test suite -->
840+ <script type="text/javascript" src="test_filebug.js"></script>
841+
842+ </head>
843+ <body class="yui3-skin-sam">
844+ <ul id="suites">
845+ <li>lp.bugs.filebug.test</li>
846+ </ul>
847+ <div class='login-logout'></div>
848+ <div id="fixture"></div>
849+ <script type="text/x-template" id="privacy-banner-template">
850+ <table class="radio-button-widget">
851+ <tbody>
852+ <tr>
853+ <td><input type="radio" value="PUBLIC" name="field.information_type"
854+ id="field.information_type.0" checked="checked" class="radioType">
855+ </td>
856+ <td><label for="field.information_type.0">Public</label></td>
857+ </tr>
858+ <tr>
859+ <td><input type="radio" value="UNEMBARGOEDSECURITY" name="field.information_type"
860+ id="field.information_type.1"class="radioType">
861+ </td>
862+ <td><label for="field.information_type.1">Unembargoed Security</label></td>
863+ </tr>
864+ <tr>
865+ <td><input type="radio" value="EMBARGOEDSECURITY" name="field.information_type"
866+ id="field.information_type.2"class="radioType">
867+ </td>
868+ <td><label for="field.information_type.2">Embargoed Security</label></td>
869+ </tr>
870+ <tr>
871+ <td><input type="radio" value="USERDATA" name="field.information_type"
872+ id="field.information_type.3"class="radioType">
873+ </td>
874+ <td><label for="field.information_type.3">User Data</label></td>
875+ </tr>
876+ <tr>
877+ <td><input type="radio" value="PROPRIETARY" name="field.information_type"
878+ id="field.information_type.4"class="radioType">
879+ </td>
880+ <td><label for="field.information_type.4">Proprietary</label></td>
881+ </tr>
882+ </tbody>
883+ </table>
884+ </script>
885+ </body>
886+</html>
887+>>>>>>> MERGE-SOURCE
888
889=== modified file 'lib/lp/bugs/javascript/tests/test_filebug.js'
890--- lib/lp/bugs/javascript/tests/test_filebug.js 2012-05-30 21:36:01 +0000
891+++ lib/lp/bugs/javascript/tests/test_filebug.js 2012-06-01 11:55:34 +0000
892@@ -1,3 +1,4 @@
893+<<<<<<< TREE
894 /* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
895
896 YUI.add('lp.bugs.filebug.test', function (Y) {
897@@ -166,3 +167,118 @@
898 'lp.app.banner.privacy', 'lp.app.choice',
899 'lp.bugs.filebug_dupefinder', 'lp.bugs.filebug'
900 ]});
901+=======
902+/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
903+
904+YUI.add('lp.bugs.filebug.test', function (Y) {
905+
906+ var tests = Y.namespace('lp.bugs.filebug.test');
907+ tests.suite = new Y.Test.Suite(
908+ 'lp.bugs.filebug Tests');
909+
910+ tests.suite.add(new Y.Test.Case({
911+ name: 'lp.bugs.filebug_tests',
912+
913+ setUp: function () {
914+ window.LP = {
915+ links: {},
916+ cache: {
917+ private_types: ['EMBARGOEDSECURITY', 'USERDATA']
918+ }
919+ };
920+ this.fixture = Y.one('#fixture');
921+ var banner = Y.Node.create(
922+ Y.one('#privacy-banner-template').getContent());
923+ this.fixture.appendChild(banner);
924+ },
925+
926+ tearDown: function () {
927+ if (this.fixture !== null) {
928+ this.fixture.empty(true);
929+ }
930+ delete this.fixture;
931+ delete window.LP;
932+ },
933+
934+ test_library_exists: function () {
935+ Y.Assert.isObject(Y.lp.bugs.filebug,
936+ "Could not locate the " +
937+ "lp.bugs.filebug module");
938+ },
939+
940+ // Filing a public bug does not show the privacy banner.
941+ test_setup_filebug_public: function () {
942+ Y.lp.bugs.filebug.setup_filebug(true);
943+ var banner_hidden = Y.one('.yui3-privacybanner-hidden');
944+ Y.Assert.isNotNull(banner_hidden);
945+ },
946+
947+ // Filing a bug for a project with private bugs shows the privacy
948+ // banner.
949+ test_setup_filebug_private: function () {
950+ window.LP.cache.bug_private_by_default = true;
951+ Y.lp.bugs.filebug.setup_filebug(true);
952+ var banner_hidden = Y.one('.yui3-privacybanner-hidden');
953+ Y.Assert.isNull(banner_hidden);
954+ var banner_text = Y.one('.banner-text').get('text');
955+ Y.Assert.areEqual(
956+ 'This report will be private. ' +
957+ 'You can disclose it later.', banner_text);
958+ },
959+
960+ // Selecting a private info type turns on the privacy banner.
961+ test_select_private_info_type: function () {
962+ window.LP.cache.show_information_type_in_ui = true;
963+ Y.lp.bugs.filebug.setup_filebug(true);
964+ var banner_hidden = Y.one('.yui3-privacybanner-hidden');
965+ Y.Assert.isNotNull(banner_hidden);
966+ Y.one('[id=field.information_type.2]').simulate('click');
967+ banner_hidden = Y.one('.yui3-privacybanner-hidden');
968+ Y.Assert.isNull(banner_hidden);
969+ var banner_text = Y.one('.banner-text').get('text');
970+ Y.Assert.areEqual(
971+ 'This report will be private. ' +
972+ 'You can disclose it later.', banner_text);
973+ },
974+
975+ // Selecting a public info type turns off the privacy banner.
976+ test_select_public_info_type: function () {
977+ window.LP.cache.show_information_type_in_ui = true;
978+ window.LP.cache.bug_private_by_default = true;
979+ Y.lp.bugs.filebug.setup_filebug(true);
980+ var banner_hidden = Y.one('.yui3-privacybanner-hidden');
981+ Y.Assert.isNull(banner_hidden);
982+ Y.one('[id=field.information_type.0]').simulate('click');
983+ banner_hidden = Y.one('.yui3-privacybanner-hidden');
984+ Y.Assert.isNotNull(banner_hidden);
985+ },
986+
987+ // The dupe finder functionality is setup.
988+ test_dupe_finder_setup: function () {
989+ window.LP.cache.enable_bugfiling_duplicate_search = true;
990+ var orig_setup_dupe_finder =
991+ Y.lp.bugs.filebug_dupefinder.setup_dupe_finder;
992+ var orig_setup_dupes =
993+ Y.lp.bugs.filebug_dupefinder.setup_dupes;
994+ var setup_dupe_finder_called = false;
995+ var setup_dupes_called = false;
996+ Y.lp.bugs.filebug_dupefinder.setup_dupe_finder = function() {
997+ setup_dupe_finder_called = true;
998+ };
999+ Y.lp.bugs.filebug_dupefinder.setup_dupes = function() {
1000+ setup_dupes_called = true;
1001+ };
1002+ Y.lp.bugs.filebug.setup_filebug(true);
1003+ Y.Assert.isTrue(setup_dupe_finder_called);
1004+ Y.Assert.isTrue(setup_dupes_called);
1005+ Y.lp.bugs.filebug_dupefinder.setup_dupes = orig_setup_dupes;
1006+ Y.lp.bugs.filebug_dupefinder.setup_dupe_finder
1007+ = orig_setup_dupe_finder;
1008+ }
1009+ }));
1010+
1011+}, '0.1', {'requires': ['test', 'console', 'event', 'node-event-simulate',
1012+ 'lp.app.banner.privacy', 'lp.bugs.filebug_dupefinder',
1013+ 'lp.bugs.filebug'
1014+ ]});
1015+>>>>>>> MERGE-SOURCE
1016
1017=== modified file 'lib/lp/code/browser/tests/test_branch.py'
1018=== modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py'
1019=== modified file 'lib/lp/code/model/tests/test_branch.py'
1020--- lib/lp/code/model/tests/test_branch.py 2012-05-31 03:54:13 +0000
1021+++ lib/lp/code/model/tests/test_branch.py 2012-06-01 11:55:34 +0000
1022@@ -2337,9 +2337,15 @@
1023
1024 def test_public_stacked_on_private_is_private(self):
1025 # A public branch stacked on a private branch is private.
1026+<<<<<<< TREE
1027 stacked_on = self.factory.makeBranch(
1028 information_type=InformationType.USERDATA)
1029 branch = self.factory.makeBranch(stacked_on=stacked_on)
1030+=======
1031+ stacked_on = self.factory.makeBranch(private=True)
1032+ branch = self.factory.makeBranch(
1033+ stacked_on=stacked_on, private=False)
1034+>>>>>>> MERGE-SOURCE
1035 self.assertTrue(branch.private)
1036 self.assertEqual(
1037 stacked_on.information_type, branch.information_type)
1038@@ -2347,10 +2353,16 @@
1039 self.assertTrue(branch.explicitly_private)
1040
1041 def test_private_stacked_on_public_is_private(self):
1042+<<<<<<< TREE
1043 # A private branch stacked on a public branch is private.
1044 stacked_on = self.factory.makeBranch()
1045 branch = self.factory.makeBranch(
1046 stacked_on=stacked_on, information_type=InformationType.USERDATA)
1047+=======
1048+ # A private branch stacked on a public branch is private.
1049+ stacked_on = self.factory.makeBranch(private=False)
1050+ branch = self.factory.makeBranch(stacked_on=stacked_on, private=True)
1051+>>>>>>> MERGE-SOURCE
1052 self.assertTrue(branch.private)
1053 self.assertNotEqual(
1054 stacked_on.information_type, branch.information_type)
1055@@ -2449,6 +2461,7 @@
1056 branch.setPrivate,
1057 False, branch.owner)
1058
1059+<<<<<<< TREE
1060 def test_cannot_transition_with_private_stacked_on(self):
1061 # If a public branch is stacked on a private branch, it can not
1062 # change its information_type to public.
1063@@ -2471,6 +2484,28 @@
1064 self.assertEqual(
1065 InformationType.UNEMBARGOEDSECURITY, branch.information_type)
1066
1067+=======
1068+ def test_cannot_transition_with_private_stacked_on(self):
1069+ # If a public branch is stacked on a private branch, it can not
1070+ # change its information_type to public.
1071+ stacked_on = self.factory.makeBranch(private=True)
1072+ branch = self.factory.makeBranch(stacked_on=stacked_on)
1073+ self.assertRaises(
1074+ BranchCannotChangeInformationType,
1075+ branch.transitionToInformationType, InformationType.PUBLIC,
1076+ branch.owner)
1077+
1078+ def test_can_transition_with_public_stacked_on(self):
1079+ # If a private branch is stacked on a public branch, it can change
1080+ # its information_type.
1081+ stacked_on = self.factory.makeBranch()
1082+ branch = self.factory.makeBranch(stacked_on=stacked_on, private=True)
1083+ branch.transitionToInformationType(
1084+ InformationType.UNEMBARGOEDSECURITY, branch.owner)
1085+ self.assertEqual(
1086+ InformationType.UNEMBARGOEDSECURITY, branch.information_type)
1087+
1088+>>>>>>> MERGE-SOURCE
1089
1090 class TestBranchCommitsForDays(TestCaseWithFactory):
1091 """Tests for `Branch.commitsForDays`."""
1092
1093=== modified file 'lib/lp/code/model/tests/test_branchlookup.py'
1094--- lib/lp/code/model/tests/test_branchlookup.py 2012-05-31 03:54:13 +0000
1095+++ lib/lp/code/model/tests/test_branchlookup.py 2012-06-01 11:55:34 +0000
1096@@ -148,9 +148,15 @@
1097 # (this is for anonymous access)
1098 owner = self.factory.makePerson()
1099 private_branch = self.factory.makeAnyBranch(
1100+<<<<<<< TREE
1101 owner=owner, information_type=InformationType.USERDATA)
1102 branch = self.factory.makeAnyBranch(
1103 stacked_on=private_branch, owner=owner)
1104+=======
1105+ owner=owner, private=True)
1106+ branch = self.factory.makeAnyBranch(
1107+ stacked_on=private_branch, owner=owner)
1108+>>>>>>> MERGE-SOURCE
1109 with person_logged_in(owner):
1110 path = branch_id_alias(branch)
1111 result = self.branch_set.getIdAndTrailingPath(path)
1112
1113=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
1114=== modified file 'lib/lp/code/model/tests/test_branchvisibility.py'
1115--- lib/lp/code/model/tests/test_branchvisibility.py 2012-05-31 03:54:13 +0000
1116+++ lib/lp/code/model/tests/test_branchvisibility.py 2012-06-01 11:55:34 +0000
1117@@ -138,17 +138,26 @@
1118 private_owner = self.factory.makePerson()
1119 test_branches = []
1120 for x in range(5):
1121+<<<<<<< TREE
1122 # We want the first 3 public and the last 3 private.
1123 information_type = InformationType.PUBLIC
1124 if x > 2:
1125 information_type = InformationType.USERDATA
1126 branch = self.factory.makeBranch(
1127 information_type=information_type)
1128+=======
1129+ # We want the first 3 public and the last 3 private.
1130+ branch = self.factory.makeBranch(private=x > 2)
1131+>>>>>>> MERGE-SOURCE
1132 test_branches.append(branch)
1133 test_branches.append(
1134+<<<<<<< TREE
1135 self.factory.makeBranch(
1136 owner=private_owner,
1137 information_type=InformationType.USERDATA))
1138+=======
1139+ self.factory.makeBranch(private=True, owner=private_owner))
1140+>>>>>>> MERGE-SOURCE
1141
1142 # Anonymous users see just the public branches.
1143 branch_info = [(branch, branch.private)
1144
1145=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
1146=== modified file 'lib/lp/code/xmlrpc/tests/test_branch.py'
1147=== modified file 'lib/lp/registry/templates/person-upcomingwork.pt'
1148=== modified file 'lib/lp/registry/tests/test_productjob.py'
1149=== modified file 'lib/lp/soyuz/model/queue.py'
1150=== modified file 'lib/lp/soyuz/tests/test_distroseriesqueue_debian_installer.py'
1151--- lib/lp/soyuz/tests/test_distroseriesqueue_debian_installer.py 2012-05-28 12:50:34 +0000
1152+++ lib/lp/soyuz/tests/test_distroseriesqueue_debian_installer.py 2012-06-01 11:55:34 +0000
1153@@ -1,3 +1,4 @@
1154+<<<<<<< TREE
1155 # Copyright 2012 Canonical Ltd. This software is licensed under the
1156 # GNU Affero General Public License version 3 (see the file LICENSE).
1157
1158@@ -72,3 +73,79 @@
1159 CustomUploadAlreadyExists,
1160 upload.queue_root.customfiles[0].publish, self.logger)
1161 self.assertEqual("ACCEPTED", upload.queue_root.status.name)
1162+=======
1163+# Copyright 2012 Canonical Ltd. This software is licensed under the
1164+# GNU Affero General Public License version 3 (see the file LICENSE).
1165+
1166+"""Test upload and queue manipulation of debian-installer custom uploads.
1167+
1168+See also lp.archivepublisher.tests.test_debian_installer for detailed tests
1169+of debian-installer custom upload extraction.
1170+"""
1171+
1172+import os
1173+
1174+import transaction
1175+
1176+from lp.archivepublisher.debian_installer import DebianInstallerAlreadyExists
1177+from lp.archiveuploader.nascentupload import NascentUpload
1178+from lp.archiveuploader.tests import (
1179+ datadir,
1180+ getPolicy,
1181+ )
1182+from lp.services.log.logger import DevNullLogger
1183+from lp.services.mail import stub
1184+from lp.soyuz.tests.test_publishing import TestNativePublishingBase
1185+from lp.testing.gpgkeys import import_public_test_keys
1186+
1187+
1188+class TestDistroSeriesQueueDebianInstaller(TestNativePublishingBase):
1189+
1190+ def setUp(self):
1191+ super(TestDistroSeriesQueueDebianInstaller, self).setUp()
1192+ import_public_test_keys()
1193+ # CustomUpload.installFiles requires a umask of 022.
1194+ old_umask = os.umask(022)
1195+ self.addCleanup(os.umask, old_umask)
1196+ self.anything_policy = getPolicy(
1197+ name="anything", distro="ubuntutest", distroseries=None)
1198+ self.logger = DevNullLogger()
1199+
1200+ def uploadTestData(self):
1201+ upload = NascentUpload.from_changesfile_path(
1202+ datadir(
1203+ "debian-installer/"
1204+ "debian-installer_20070214ubuntu1_i386.changes"),
1205+ self.anything_policy, self.logger)
1206+ upload.process()
1207+ self.assertFalse(upload.is_rejected)
1208+ self.assertTrue(upload.do_accept())
1209+ self.assertFalse(upload.rejection_message)
1210+ return upload
1211+
1212+ def test_accepts_correct_upload(self):
1213+ upload = self.uploadTestData()
1214+ self.assertEqual(1, upload.queue_root.customfiles.count())
1215+
1216+ def test_generates_mail(self):
1217+ # Two e-mail messages were generated (acceptance and announcement).
1218+ self.anything_policy.setDistroSeriesAndPocket("hoary-test")
1219+ self.anything_policy.distroseries.changeslist = "announce@example.com"
1220+ self.uploadTestData()
1221+ self.assertEqual(2, len(stub.test_emails))
1222+
1223+ def test_bad_upload_remains_in_accepted(self):
1224+ # Bad debian-installer uploads remain in accepted. Simulate an
1225+ # on-disk conflict to force an error.
1226+ upload = self.uploadTestData()
1227+ # Make sure that we can use the librarian files.
1228+ transaction.commit()
1229+ os.makedirs(os.path.join(
1230+ self.config.distroroot, "ubuntutest", "dists", "hoary-test",
1231+ "main", "installer-i386", "20070214ubuntu1"))
1232+ self.assertFalse(upload.queue_root.realiseUpload(self.logger))
1233+ self.assertRaises(
1234+ DebianInstallerAlreadyExists,
1235+ upload.queue_root.customfiles[0].publish, self.logger)
1236+ self.assertEqual("ACCEPTED", upload.queue_root.status.name)
1237+>>>>>>> MERGE-SOURCE
1238
1239=== modified file 'lib/lp/soyuz/tests/test_packageupload.py'
1240=== modified file 'lib/lp/testing/factory.py'
1241--- lib/lp/testing/factory.py 2012-06-01 02:33:27 +0000
1242+++ lib/lp/testing/factory.py 2012-06-01 11:55:34 +0000
1243@@ -1073,8 +1073,13 @@
1244
1245 def makeBranch(self, branch_type=None, owner=None,
1246 name=None, product=_DEFAULT, url=_DEFAULT, registrant=None,
1247+<<<<<<< TREE
1248 information_type=None, stacked_on=None,
1249 sourcepackage=None, reviewer=None, **optional_branch_args):
1250+=======
1251+ private=None, information_type=None, stacked_on=None,
1252+ sourcepackage=None, reviewer=None, **optional_branch_args):
1253+>>>>>>> MERGE-SOURCE
1254 """Create and return a new, arbitrary Branch of the given type.
1255
1256 Any parameters for `IBranchNamespace.createBranch` can be specified to
1257@@ -1120,9 +1125,21 @@
1258 branch = namespace.createBranch(
1259 branch_type=branch_type, name=name, registrant=registrant,
1260 url=url, **optional_branch_args)
1261- if information_type is not None:
1262- removeSecurityProxy(branch).transitionToInformationType(
1263- information_type, registrant, verify_policy=False)
1264+<<<<<<< TREE
1265+ if information_type is not None:
1266+ removeSecurityProxy(branch).transitionToInformationType(
1267+ information_type, registrant, verify_policy=False)
1268+=======
1269+ assert information_type is None or private is None, (
1270+ "Can not specify both information_type and private")
1271+ if private is not None:
1272+ information_type = (
1273+ InformationType.USERDATA if private else
1274+ InformationType.PUBLIC)
1275+ if information_type is not None:
1276+ removeSecurityProxy(branch).transitionToInformationType(
1277+ information_type, registrant, verify_policy=False)
1278+>>>>>>> MERGE-SOURCE
1279 if stacked_on is not None:
1280 removeSecurityProxy(branch).branchChanged(
1281 removeSecurityProxy(stacked_on).unique_name, 'rev1', None,