← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/soyuz-browser-tests-future-imports into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/soyuz-browser-tests-future-imports into lp:launchpad.

Commit message:
Convert lp.soyuz.browser.tests to Launchpad's preferred __future__ imports.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/soyuz-browser-tests-future-imports/+merge/337010

In the process of doing this I realised that we need to go to a bit of effort to satisfy the requirements of PEP 3333, and in the process of testing *that* I found that there were various tests that passed a principal into LaunchpadTestRequest in a way that doesn't make sense.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/soyuz-browser-tests-future-imports into lp:launchpad.
=== modified file 'lib/lp/code/browser/tests/test_branch.py'
--- lib/lp/code/browser/tests/test_branch.py	2018-01-02 16:10:26 +0000
+++ lib/lp/code/browser/tests/test_branch.py	2018-02-01 18:46:27 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2017 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for BranchView."""
@@ -1109,7 +1109,7 @@
         if user is None:
             user = removeSecurityProxy(branch).owner
         with person_logged_in(user):
-            view = create_initialized_view(branch, '+edit', user=user)
+            view = create_initialized_view(branch, '+edit', principal=user)
             self.assertContentEqual(types, view.getInformationTypesToShow())
 
     def test_public_branch(self):

=== modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py'
--- lib/lp/code/browser/tests/test_branchmergeproposal.py	2018-01-02 10:54:31 +0000
+++ lib/lp/code/browser/tests/test_branchmergeproposal.py	2018-02-01 18:46:27 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2017 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for BranchMergeProposals."""
@@ -565,8 +565,8 @@
         target_branch = self._makeTargetBranch()
         reviewer = self.factory.makePerson()
         extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
-        request = LaunchpadTestRequest(
-            method='POST', principal=owner, **extra)
+        request = LaunchpadTestRequest(method='POST', **extra)
+        request.setPrincipal(owner)
         view = self._createView(request=request)
         with person_logged_in(owner):
             result_data = view.register_action.success(self._getFormValues(
@@ -767,8 +767,8 @@
             owner=owner, information_type=InformationType.USERDATA)
         reviewer = self.factory.makePerson()
         extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
-        request = LaunchpadTestRequest(
-            method='POST', principal=owner, **extra)
+        request = LaunchpadTestRequest(method='POST', **extra)
+        request.setPrincipal(owner)
         view = self._createView(request=request)
         with person_logged_in(owner):
             branches_to_check = [self.source_branch.unique_name,
@@ -796,12 +796,13 @@
         extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
         with person_logged_in(owner):
             request = LaunchpadTestRequest(
-                method='POST', principal=owner,
+                method='POST',
                 form={
                     'field.actions.register': 'Propose Merge',
                     'field.target_branch.target_branch':
                         target_branch.unique_name},
                 **extra)
+            request.setPrincipal(owner)
             view = create_initialized_view(
                 target_branch,
                 name='+register-merge',
@@ -856,8 +857,8 @@
             owner=owner, information_type=InformationType.USERDATA)
         reviewer = self.factory.makePerson()
         extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
-        request = LaunchpadTestRequest(
-            method='POST', principal=owner, **extra)
+        request = LaunchpadTestRequest(method='POST', **extra)
+        request.setPrincipal(owner)
         view = self._createView(request=request)
         with person_logged_in(owner):
             repositories_to_check = [
@@ -887,7 +888,7 @@
         extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
         with person_logged_in(owner):
             request = LaunchpadTestRequest(
-                method='POST', principal=owner,
+                method='POST',
                 form={
                     'field.actions.register': 'Propose Merge',
                     'field.target_git_repository.target_git_repository':
@@ -895,6 +896,7 @@
                     'field.target_git_path': target_branch.path,
                     },
                 **extra)
+            request.setPrincipal(owner)
             view = create_initialized_view(
                 target_branch,
                 name='+register-merge',
@@ -916,7 +918,7 @@
         extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
         with person_logged_in(owner):
             request = LaunchpadTestRequest(
-                method='POST', principal=owner,
+                method='POST',
                 form={
                     'field.actions.register': 'Propose Merge',
                     'field.target_git_repository.target_git_repository': '',
@@ -924,6 +926,7 @@
                     'field.target_git_path': 'master',
                     },
                 **extra)
+            request.setPrincipal(owner)
             view = create_initialized_view(
                 self.source_branch,
                 name='+register-merge',
@@ -946,13 +949,14 @@
         extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
         with person_logged_in(owner):
             request = LaunchpadTestRequest(
-                method='POST', principal=owner,
+                method='POST',
                 form={
                     'field.actions.register': 'Propose Merge',
                     'field.target_git_repository.target_git_repository':
                         target_branch.repository.unique_name,
                     },
                 **extra)
+            request.setPrincipal(owner)
             view = create_initialized_view(
                 self.source_branch,
                 name='+register-merge',
@@ -979,7 +983,7 @@
         extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
         with person_logged_in(owner):
             request = LaunchpadTestRequest(
-                method='POST', principal=owner,
+                method='POST',
                 form={
                     'field.actions.register': 'Propose Merge',
                     'field.target_git_repository.target_git_repository':
@@ -989,6 +993,7 @@
                         prerequisite_branch.repository.unique_name,
                     },
                 **extra)
+            request.setPrincipal(owner)
             view = create_initialized_view(
                 self.source_branch,
                 name='+register-merge',

=== modified file 'lib/lp/code/browser/tests/test_gitlisting.py'
--- lib/lp/code/browser/tests/test_gitlisting.py	2017-10-21 18:14:14 +0000
+++ lib/lp/code/browser/tests/test_gitlisting.py	2018-02-01 18:46:27 +0000
@@ -1,4 +1,4 @@
-# Copyright 2015-2017 Canonical Ltd.  This software is licensed under the
+# Copyright 2015-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for Git listing views."""
@@ -163,7 +163,7 @@
         # But someone who can see the repo gets the normal view.
         with person_logged_in(self.owner):
             owner_view = create_initialized_view(
-                self.target, '+git', user=self.owner)
+                self.target, '+git', principal=self.owner)
             self.assertEqual(invisible_repo, owner_view.default_git_repository)
             self.assertContentEqual(
                 [invisible_repo, other_repo],
@@ -291,7 +291,7 @@
         # But someone who can see the repo gets the normal view.
         with person_logged_in(self.owner):
             owner_view = create_initialized_view(
-                self.owner_target, '+git', user=self.owner)
+                self.owner_target, '+git', principal=self.owner)
             self.assertEqual(invisible_repo, owner_view.default_git_repository)
             self.assertContentEqual(
                 [invisible_repo, other_repo],
@@ -434,7 +434,7 @@
         # But someone who can see the repo gets the full view.
         with person_logged_in(self.user):
             owner_view = create_initialized_view(
-                self.context, '+git', user=self.user)
+                self.context, '+git', principal=self.user)
             self.assertContentEqual(
                 [invisible_repo, other_repo],
                 owner_view.repo_collection.getRepositories())

=== modified file 'lib/lp/code/browser/tests/test_gitrepository.py'
--- lib/lp/code/browser/tests/test_gitrepository.py	2017-10-21 18:14:14 +0000
+++ lib/lp/code/browser/tests/test_gitrepository.py	2018-02-01 18:46:27 +0000
@@ -1,4 +1,4 @@
-# Copyright 2015-2017 Canonical Ltd.  This software is licensed under the
+# Copyright 2015-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for GitRepositoryView."""
@@ -808,7 +808,7 @@
         if user is None:
             user = removeSecurityProxy(repository).owner
         with person_logged_in(user):
-            view = create_initialized_view(repository, "+edit", user=user)
+            view = create_initialized_view(repository, "+edit", principal=user)
             self.assertContentEqual(types, view.getInformationTypesToShow())
 
     def test_public_repository(self):

=== modified file 'lib/lp/services/webapp/servers.py'
--- lib/lp/services/webapp/servers.py	2018-01-30 17:49:56 +0000
+++ lib/lp/services/webapp/servers.py	2018-02-01 18:46:27 +0000
@@ -20,6 +20,7 @@
     )
 from lazr.restful.utils import get_current_browser_request
 from lazr.uri import URI
+import six
 import transaction
 from transaction.interfaces import ISynchronizer
 from zc.zservertracelog.tracelog import Server as ZServerTracelogServer
@@ -533,6 +534,31 @@
     return decoded_qs
 
 
+def wsgi_native_string(s):
+    """Make a native string suitable for use in WSGI.
+
+    PEP 3333 requires environment variables to be native strings that
+    contain only code points representable in ISO-8859-1.  To support
+    porting to Python 3 via an intermediate stage of Unicode literals in
+    Python 2, we enforce this here.
+    """
+    # Based on twisted.python.compat.nativeString, but using a different
+    # encoding.
+    if not isinstance(s, (bytes, unicode)):
+        raise TypeError('%r is neither bytes nor unicode' % s)
+    if six.PY3:
+        if isinstance(s, bytes):
+            return s.decode('ISO-8859-1')
+        else:
+            # Ensure we're limited to ISO-8859-1.
+            s.encode('ISO-8859-1')
+    else:
+        if isinstance(s, unicode):
+            return s.encode('ISO-8859-1')
+        # Bytes objects are always decodable as ISO-8859-1.
+    return s
+
+
 class LaunchpadBrowserRequestMixin:
     """Provides methods used for both API and web browser requests."""
 
@@ -927,9 +953,19 @@
     def __init__(self, body_instream=None, environ=None, form=None,
                  skin=None, outstream=None, method='GET',
                  force_fresh_login_for_testing=False, **kw):
+        # PEP 3333 requires environment variables to be native strings that
+        # contain only code points representable in ISO-8859-1.  To support
+        # porting to Python 3 via an intermediate stage of Unicode literals
+        # in Python 2, we enforce this here.
+        native_kw = {}
+        for key, value in kw.items():
+            if value is not None:
+                value = wsgi_native_string(value)
+            native_kw[key] = value
         super(LaunchpadTestRequest, self).__init__(
             body_instream=body_instream, environ=environ, form=form,
-            skin=skin, outstream=outstream, REQUEST_METHOD=method, **kw)
+            skin=skin, outstream=outstream,
+            REQUEST_METHOD=wsgi_native_string(method), **native_kw)
         self.traversed_objects = []
         # Use an existing feature controller if one exists, otherwise use the
         # null controller.

=== modified file 'lib/lp/services/webapp/tests/test_servers.py'
--- lib/lp/services/webapp/tests/test_servers.py	2018-01-25 12:45:47 +0000
+++ lib/lp/services/webapp/tests/test_servers.py	2018-02-01 18:46:27 +0000
@@ -21,6 +21,7 @@
     IGenericEntry,
     WebServiceTestCase,
     )
+import six
 from zope.component import (
     getGlobalSiteManager,
     getUtility,
@@ -47,6 +48,7 @@
     WebServicePublication,
     WebServiceRequestPublicationFactory,
     WebServiceTestRequest,
+    wsgi_native_string,
     )
 from lp.testing import (
     EventRecorder,
@@ -353,6 +355,29 @@
             request.response.getHeader('Vary'), 'Accept')
 
 
+class TestWSGINativeString(TestCase):
+
+    def _toNative(self, s):
+        if six.PY3:
+            return s
+        else:
+            return s.encode('ISO-8859-1')
+
+    def test_not_bytes_or_unicode(self):
+        self.assertRaises(TypeError, wsgi_native_string, object())
+
+    def test_bytes_iso_8859_1(self):
+        self.assertEqual(
+            self._toNative(u'foo\xfe'), wsgi_native_string(b'foo\xfe'))
+
+    def test_unicode_iso_8859_1(self):
+        self.assertEqual(
+            self._toNative(u'foo\xfe'), wsgi_native_string(u'foo\xfe'))
+
+    def test_unicode_not_iso_8859_1(self):
+        self.assertRaises(UnicodeEncodeError, wsgi_native_string, u'foo\u2014')
+
+
 class TestBasicLaunchpadRequest(TestCase):
     """Tests for the base request class"""
 

=== modified file 'lib/lp/soyuz/browser/tests/test_archive.py'
--- lib/lp/soyuz/browser/tests/test_archive.py	2015-10-21 09:37:08 +0000
+++ lib/lp/soyuz/browser/tests/test_archive.py	2018-02-01 18:46:27 +0000
@@ -1,6 +1,8 @@
-# Copyright 2014-2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2014-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from fixtures import FakeLogger
@@ -171,6 +173,6 @@
         login_person(person)
         recorder1, recorder2 = record_two_runs(
             lambda: create_initialized_view(
-                source, '+copy-packages', user=person),
+                source, '+copy-packages', principal=person),
             create_targets, nb_objects)
         self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))

=== modified file 'lib/lp/soyuz/browser/tests/test_archive_admin_view.py'
--- lib/lp/soyuz/browser/tests/test_archive_admin_view.py	2015-09-22 20:26:36 +0000
+++ lib/lp/soyuz/browser/tests/test_archive_admin_view.py	2018-02-01 18:46:27 +0000
@@ -1,6 +1,8 @@
-# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from lp.services.webapp.servers import LaunchpadTestRequest

=== modified file 'lib/lp/soyuz/browser/tests/test_archive_packages.py'
--- lib/lp/soyuz/browser/tests/test_archive_packages.py	2018-01-02 16:10:26 +0000
+++ lib/lp/soyuz/browser/tests/test_archive_packages.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2010-2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for TestP3APackages."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 __all__ = [
     'TestP3APackages',
@@ -396,7 +398,7 @@
             soup.findAll(
                 'div', attrs={'class': 'pending-job', 'job_id': jobs[-1].id}))
         self.assertEqual(
-            [u'Showing 5 of 7'],
+            ['Showing 5 of 7'],
             soup.findAll('span', text=re.compile('Showing 5 of .')))
 
     def test_job_notifications_display_owner_is_team(self):

=== modified file 'lib/lp/soyuz/browser/tests/test_archive_webservice.py'
--- lib/lp/soyuz/browser/tests/test_archive_webservice.py	2016-10-20 11:49:45 +0000
+++ lib/lp/soyuz/browser/tests/test_archive_webservice.py	2018-02-01 18:46:27 +0000
@@ -1,6 +1,8 @@
-# Copyright 2010-2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from datetime import timedelta

=== modified file 'lib/lp/soyuz/browser/tests/test_breadcrumbs.py'
--- lib/lp/soyuz/browser/tests/test_breadcrumbs.py	2018-01-02 16:10:26 +0000
+++ lib/lp/soyuz/browser/tests/test_breadcrumbs.py	2018-02-01 18:46:27 +0000
@@ -1,6 +1,8 @@
-# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from zope.component import getUtility

=== modified file 'lib/lp/soyuz/browser/tests/test_build_views.py'
--- lib/lp/soyuz/browser/tests/test_build_views.py	2018-01-02 16:10:26 +0000
+++ lib/lp/soyuz/browser/tests/test_build_views.py	2018-02-01 18:46:27 +0000
@@ -1,6 +1,8 @@
-# Copyright 2011-2013 Canonical Ltd.  This software is licensed under the
+# Copyright 2011-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 import soupmatchers
@@ -168,7 +170,7 @@
             archive=archive, status=BuildStatus.FAILEDTOBUILD)
         with person_logged_in(self.admin):
             packageset = getUtility(IPackagesetSet).new(
-                u'rebuild', u'test', team,
+                'rebuild', 'test', team,
                 distroseries=build.distro_arch_series.distroseries)
             packageset.add((build.source_package_release.sourcepackagename,))
         # The team doesn't have permission until we grant it
@@ -427,7 +429,7 @@
             distroseries.distribution, name="+builds",
             form={
                 'build_state': 'built',
-                'build_text': u'foo',
+                'build_text': 'foo',
                 'start': 75,
                 'memo': '["2012-01-01T01:01:01", 0]'})
         view.setupBuildList()

=== modified file 'lib/lp/soyuz/browser/tests/test_distributionsourcepackagerelease.py'
--- lib/lp/soyuz/browser/tests/test_distributionsourcepackagerelease.py	2018-01-02 10:54:31 +0000
+++ lib/lp/soyuz/browser/tests/test_distributionsourcepackagerelease.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for TestSourcePackageReleaseFiles."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 __all__ = [
     'TestDistributionSourcePackageReleaseFiles',

=== modified file 'lib/lp/soyuz/browser/tests/test_distroarchseries_view.py'
--- lib/lp/soyuz/browser/tests/test_distroarchseries_view.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/browser/tests/test_distroarchseries_view.py	2018-02-01 18:46:27 +0000
@@ -1,6 +1,8 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from lp.services.webapp.servers import LaunchpadTestRequest

=== modified file 'lib/lp/soyuz/browser/tests/test_distroarchseries_webservice.py'
--- lib/lp/soyuz/browser/tests/test_distroarchseries_webservice.py	2013-11-24 23:42:37 +0000
+++ lib/lp/soyuz/browser/tests/test_distroarchseries_webservice.py	2018-02-01 18:46:27 +0000
@@ -1,6 +1,8 @@
-# Copyright 2010-2013 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 import hashlib

=== modified file 'lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py'
--- lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py	2018-01-02 10:54:31 +0000
+++ lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for DistroSourcePackageRelease pages."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from zope.security.proxy import removeSecurityProxy

=== modified file 'lib/lp/soyuz/browser/tests/test_livefs.py'
--- lib/lp/soyuz/browser/tests/test_livefs.py	2015-11-26 15:46:38 +0000
+++ lib/lp/soyuz/browser/tests/test_livefs.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2014-2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2014-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Test live filesystem views."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from datetime import (
@@ -67,7 +69,7 @@
 
     def setUp(self):
         super(TestLiveFSNavigation, self).setUp()
-        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
 
     def test_canonical_url(self):
         owner = self.factory.makePerson(name="person")
@@ -77,7 +79,7 @@
             distribution=distribution, name="unstable")
         livefs = self.factory.makeLiveFS(
             registrant=owner, owner=owner, distroseries=distroseries,
-            name=u"livefs")
+            name="livefs")
         self.assertEqual(
             "http://launchpad.dev/~person/+livefs/distro/unstable/livefs";,
             canonical_url(livefs))
@@ -109,7 +111,7 @@
 
     def setUp(self):
         super(TestLiveFSAddView, self).setUp()
-        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
         self.useFixture(FakeLogger())
         self.person = self.factory.makePerson(
             name="test-person", displayname="Test Person")
@@ -188,7 +190,7 @@
 
     def setUp(self):
         super(TestLiveFSAdminView, self).setUp()
-        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
         self.useFixture(FakeLogger())
         self.person = self.factory.makePerson(
             name="test-person", displayname="Test Person")
@@ -243,7 +245,7 @@
 
     def setUp(self):
         super(TestLiveFSEditView, self).setUp()
-        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
         self.useFixture(FakeLogger())
         self.person = self.factory.makePerson(
             name="test-person", displayname="Test Person")
@@ -290,7 +292,7 @@
             view.initialize()
             view.request_action.success({
                 "owner": livefs.owner,
-                "name": u"changed",
+                "name": "changed",
                 "distro_series": livefs.distro_series,
                 "metadata": "{}",
                 })
@@ -303,10 +305,10 @@
             displayname="Grumpy")
         livefs = self.factory.makeLiveFS(
             registrant=self.person, owner=self.person,
-            distroseries=distroseries, name=u"one")
+            distroseries=distroseries, name="one")
         self.factory.makeLiveFS(
             registrant=self.person, owner=self.person,
-            distroseries=distroseries, name=u"two")
+            distroseries=distroseries, name="two")
         browser = self.getViewBrowser(livefs, user=self.person)
         browser.getLink("Edit live filesystem").click()
         browser.getControl("Name").value = "two"
@@ -323,7 +325,7 @@
 
     def setUp(self):
         super(TestLiveFSDeleteView, self).setUp()
-        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
         self.person = self.factory.makePerson(
             name="test-person", displayname="Test Person")
 
@@ -373,7 +375,7 @@
 
     def setUp(self):
         super(TestLiveFSView, self).setUp()
-        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
         self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         self.distroseries = self.factory.makeDistroSeries(
             distribution=self.ubuntu, name="shiny", displayname="Shiny")
@@ -388,7 +390,7 @@
     def makeLiveFS(self):
         return self.factory.makeLiveFS(
             registrant=self.person, owner=self.person,
-            distroseries=self.distroseries, name=u"livefs-name",
+            distroseries=self.distroseries, name="livefs-name",
             metadata={"project": "ubuntu-test"})
 
     def makeBuild(self, livefs=None, archive=None, date_created=None,

=== modified file 'lib/lp/soyuz/browser/tests/test_livefsbuild.py'
--- lib/lp/soyuz/browser/tests/test_livefsbuild.py	2015-10-21 09:37:08 +0000
+++ lib/lp/soyuz/browser/tests/test_livefsbuild.py	2018-02-01 18:46:27 +0000
@@ -1,12 +1,15 @@
-# Copyright 2014 Canonical Ltd.  This software is licensed under the
+# Copyright 2014-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Test live filesystem build views."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from fixtures import FakeLogger
 from mechanize import LinkNotFoundError
+import soupmatchers
 from storm.locals import Store
 from testtools.matchers import StartsWith
 import transaction
@@ -46,7 +49,7 @@
 
     def setUp(self):
         super(TestCanonicalUrlForLiveFSBuild, self).setUp()
-        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
 
     def test_canonical_url(self):
         owner = self.factory.makePerson(name="person")
@@ -56,7 +59,7 @@
             distribution=distribution, name="unstable")
         livefs = self.factory.makeLiveFS(
             registrant=owner, owner=owner, distroseries=distroseries,
-            name=u"livefs")
+            name="livefs")
         build = self.factory.makeLiveFSBuild(requester=owner, livefs=livefs)
         self.assertThat(
             canonical_url(build),
@@ -71,7 +74,7 @@
 
     def setUp(self):
         super(TestLiveFSBuildView, self).setUp()
-        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
 
     def test_files(self):
         # LiveFSBuildView.files returns all the associated files.
@@ -114,7 +117,7 @@
 
     def setUp(self):
         super(TestLiveFSBuildOperations, self).setUp()
-        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
         self.useFixture(FakeLogger())
         self.build = self.factory.makeLiveFSBuild()
         self.build_url = canonical_url(self.build)
@@ -204,9 +207,10 @@
         browser = self.getViewBrowser(
             self.build, "+rescore", user=self.buildd_admin)
         self.assertEqual(self.build_url, browser.url)
-        self.assertIn(
-            "Cannot rescore this build because it is not queued.",
-            browser.contents)
+        self.assertThat(browser.contents, soupmatchers.HTMLContains(
+            soupmatchers.Tag(
+                "notification", "div", attrs={"class": "warning message"},
+                text="Cannot rescore this build because it is not queued.")))
 
     def test_builder_history(self):
         Store.of(self.build).flush()

=== modified file 'lib/lp/soyuz/browser/tests/test_package_copying_mixin.py'
--- lib/lp/soyuz/browser/tests/test_package_copying_mixin.py	2012-11-12 14:54:36 +0000
+++ lib/lp/soyuz/browser/tests/test_package_copying_mixin.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2011-2012 Canonical Ltd.  This software is licensed under the
+# Copyright 2011-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for `PackageCopyingMixin`."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from zope.component import getUtility

=== modified file 'lib/lp/soyuz/browser/tests/test_personal_archive_subscription.py'
--- lib/lp/soyuz/browser/tests/test_personal_archive_subscription.py	2012-04-18 18:51:11 +0000
+++ lib/lp/soyuz/browser/tests/test_personal_archive_subscription.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# Copyright 2012-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for the PersonalArchiveSubscription components and view."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from lp.app.interfaces.launchpad import IPrivacy

=== modified file 'lib/lp/soyuz/browser/tests/test_publishing.py'
--- lib/lp/soyuz/browser/tests/test_publishing.py	2015-04-20 15:59:52 +0000
+++ lib/lp/soyuz/browser/tests/test_publishing.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2011-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for source package publication listing."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 import soupmatchers

=== modified file 'lib/lp/soyuz/browser/tests/test_publishing_webservice.py'
--- lib/lp/soyuz/browser/tests/test_publishing_webservice.py	2015-12-02 13:19:42 +0000
+++ lib/lp/soyuz/browser/tests/test_publishing_webservice.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2011-2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2011-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Test webservice methods related to the publisher."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 from functools import partial
 
 from lp.services.librarian.browser import ProxiedLibraryFileAlias

=== modified file 'lib/lp/soyuz/browser/tests/test_queue.py'
--- lib/lp/soyuz/browser/tests/test_queue.py	2018-01-02 16:10:26 +0000
+++ lib/lp/soyuz/browser/tests/test_queue.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2010-2017 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for QueueItemsView."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from lxml import html
@@ -482,7 +484,7 @@
         distroseries = complete_upload.distroseries
         complete_upload.package_sets = [
             self.factory.makePackageset(distroseries=distroseries, name=name)
-            for name in [u'ccc', u'aaa', u'bbb']]
+            for name in ['ccc', 'aaa', 'bbb']]
         self.assertEqual("aaa bbb ccc", complete_upload.display_package_sets)
 
     def test_display_component_returns_source_upload_component_name(self):

=== modified file 'lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py'
--- lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py	2014-11-09 01:07:27 +0000
+++ lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for TestSourcePackageReleaseFiles."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 __all__ = [
     'TestSourcePackageReleaseView',

=== modified file 'lib/lp/soyuz/browser/tests/test_sourceslistentries.py'
--- lib/lp/soyuz/browser/tests/test_sourceslistentries.py	2018-01-02 10:54:31 +0000
+++ lib/lp/soyuz/browser/tests/test_sourceslistentries.py	2018-02-01 18:46:27 +0000
@@ -1,8 +1,10 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for SourceListEntriesView."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 __all__ = [
     'TestDefaultSelectedSeries',
@@ -51,7 +53,7 @@
                                 'Firefox/3.0.10'))
         view.initialize()
 
-        self.assertEqual(u'getsy', view.default_series_name)
+        self.assertEqual('getsy', view.default_series_name)
 
         # Ubuntu version 9.04 in the user-agent should display as feasty
         view = SourcesListEntriesView(
@@ -63,7 +65,7 @@
                                 'Firefox/3.0.10'))
         view.initialize()
 
-        self.assertEqual(u'feasty', view.default_series_name)
+        self.assertEqual('feasty', view.default_series_name)
 
     def testDefaultWithoutUserAgent(self):
         # If there is no user-agent setting, then we force the user

=== modified file 'lib/lp/soyuz/browser/tests/test_views.py'
--- lib/lp/soyuz/browser/tests/test_views.py	2011-12-28 17:03:06 +0000
+++ lib/lp/soyuz/browser/tests/test_views.py	2018-02-01 18:46:27 +0000
@@ -1,9 +1,9 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-"""
-Run the view tests.
-"""
+"""Run the view tests."""
+
+from __future__ import absolute_import, print_function, unicode_literals
 
 import logging
 import os


Follow ups