← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/git-fixtures into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/git-fixtures into lp:launchpad.

Commit message:
Add GitHostingFixture and MemcacheFixture; convert existing tests to use them.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-fixtures/+merge/305095

Add GitHostingFixture and MemcacheFixture; convert existing tests to use them.

This is the refactoring part of https://code.launchpad.net/~cjwatson/launchpad/fix-git-update-related-bugs/+merge/304941, but without touching tests that create Git-based MPs and didn't need an IGitHostingClient-related fixture before.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-fixtures into lp:launchpad.
=== modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py'
--- lib/lp/code/browser/tests/test_branchmergeproposal.py	2016-07-29 16:13:36 +0000
+++ lib/lp/code/browser/tests/test_branchmergeproposal.py	2016-09-07 11:20:36 +0000
@@ -66,10 +66,10 @@
     IMergeProposalNeedsReviewEmailJobSource,
     IMergeProposalUpdatedEmailJobSource,
     )
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.model.diff import PreviewDiff
 from lp.code.tests.helpers import (
     add_revision_to_branch,
+    GitHostingFixture,
     make_merge_proposal_without_reviewers,
     )
 from lp.code.xmlrpc.git import GitAPI
@@ -97,8 +97,6 @@
     time_counter,
     verifyObject,
     )
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadFunctionalLayer,
@@ -117,10 +115,7 @@
 
     def setUp(self):
         super(GitHostingClientMixin, self).setUp()
-        self.hosting_client = FakeMethod()
-        self.hosting_client.getLog = FakeMethod(result=[])
-        self.useFixture(
-            ZopeUtilityFixture(self.hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture())
 
 
 class TestBranchMergeProposalContextMenu(TestCaseWithFactory):
@@ -247,8 +242,6 @@
     BrowserTestCase):
     """Tests for `BranchMergeProposalMergedView` for Git."""
 
-    layer = LaunchpadFunctionalLayer
-
     arbitrary_revisions = ("0" * 40, "1" * 40, "2" * 40)
     merged_revision_text = 'Merged Revision ID'
 
@@ -1046,8 +1039,6 @@
     BrowserTestCase):
     """Test `BranchMergeProposalRequestReviewView` for Git."""
 
-    layer = LaunchpadFunctionalLayer
-
     def makeBranchMergeProposal(self):
         return self.factory.makeBranchMergeProposalForGit()
 
@@ -1278,7 +1269,7 @@
 class TestResubmitBrowserGit(GitHostingClientMixin, BrowserTestCase):
     """Browser tests for resubmitting branch merge proposals for Git."""
 
-    layer = LaunchpadFunctionalLayer
+    layer = DatabaseFunctionalLayer
 
     def test_resubmit_text(self):
         """The text of the resubmit page is as expected."""
@@ -1488,8 +1479,7 @@
         commit_date = self.factory.getUniqueDate()
         bmp = self.factory.makeBranchMergeProposalForGit(
             date_created=review_date)
-        hosting_client = FakeMethod()
-        hosting_client.getLog = FakeMethod(result=[
+        self.useFixture(GitHostingFixture(log=[
             {
                 u'sha1': unicode(hashlib.sha1(b'0').hexdigest()),
                 u'message': u'0',
@@ -1499,8 +1489,7 @@
                     u'time': int((commit_date - epoch).total_seconds()),
                     },
                 }
-            ])
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+            ]))
 
         view = create_initialized_view(bmp, '+index')
         new_commits = view.conversation.comments[0]
@@ -1586,8 +1575,7 @@
         sha1 = unicode(hashlib.sha1(b'0').hexdigest())
         epoch = datetime.fromtimestamp(0, tz=pytz.UTC)
         commit_date = datetime(2015, 1, 1, tzinfo=pytz.UTC)
-        hosting_client = FakeMethod()
-        hosting_client.getLog = FakeMethod(result=[
+        self.useFixture(GitHostingFixture(log=[
             {
                 u'sha1': sha1,
                 u'message': u'Sample message',
@@ -1597,9 +1585,8 @@
                     u'time': int((commit_date - epoch).total_seconds()),
                     },
                 }
-            ])
+            ]))
         bmp.source_git_repository.removeRefs([bmp.source_git_path])
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
         browser = self.getUserBrowser(canonical_url(bmp, rootsite='code'))
         tag = first_tag_by_class(browser.contents, 'commit-details')
         self.assertEqual(
@@ -1607,10 +1594,9 @@
             "on 2015-01-01" % sha1, extract_text(tag))
 
 
-class TestBranchMergeProposalBrowserView(
-    GitHostingClientMixin, BrowserTestCase):
+class TestBranchMergeProposalBrowserView(BrowserTestCase):
 
-    layer = LaunchpadFunctionalLayer
+    layer = DatabaseFunctionalLayer
 
     def test_prerequisite_bzr(self):
         # A prerequisite branch is rendered in the Bazaar case.
@@ -1623,6 +1609,7 @@
 
     def test_prerequisite_git(self):
         # A prerequisite reference is rendered in the Git case.
+        self.useFixture(GitHostingFixture())
         [ref] = self.factory.makeGitRefs()
         identity = ref.identity
         bmp = self.factory.makeBranchMergeProposalForGit(prerequisite_ref=ref)
@@ -1634,9 +1621,10 @@
         # Rendering a Git-based merge proposal makes the correct calls to
         # the hosting service, including requesting cross-repository
         # information.
+        hosting_fixture = self.useFixture(GitHostingFixture())
         bmp = self.factory.makeBranchMergeProposalForGit()
         self.getMainText(bmp, '+index')
-        self.assertThat(self.hosting_client.getLog.calls, MatchesSetwise(
+        self.assertThat(hosting_fixture.getLog.calls, MatchesSetwise(
             # _getNewerRevisions
             MatchesListwise([
                 Equals((
@@ -1857,7 +1845,7 @@
         self.assertEqual('Eric on 2008-09-10', view.status_title)
 
 
-class TestBranchMergeProposal(GitHostingClientMixin, BrowserTestCase):
+class TestBranchMergeProposal(BrowserTestCase):
 
     layer = LaunchpadFunctionalLayer
 
@@ -1946,7 +1934,8 @@
         bmp = self.factory.makeBranchMergeProposalForGit()
         comment = self.factory.makeCodeReviewComment(
             body='x y' * 100, merge_proposal=bmp)
-        self.hosting_client.getLog.failure = TimeoutError
+        hosting_fixture = self.useFixture(GitHostingFixture())
+        hosting_fixture.getLog.failure = TimeoutError
         browser = self.getViewBrowser(comment.branch_merge_proposal)
         self.assertIn('x y' * 100, browser.contents)
 
@@ -2191,7 +2180,5 @@
     BrowserTestCase):
     """Test the BranchMergeProposal deletion view for Git."""
 
-    layer = LaunchpadFunctionalLayer
-
     def _makeBranchMergeProposal(self, **kwargs):
         return self.factory.makeBranchMergeProposalForGit(**kwargs)

=== modified file 'lib/lp/code/browser/tests/test_gitref.py'
--- lib/lp/code/browser/tests/test_gitref.py	2016-05-18 11:49:31 +0000
+++ lib/lp/code/browser/tests/test_gitref.py	2016-09-07 11:20:36 +0000
@@ -15,9 +15,9 @@
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.interfaces.gitjob import IGitRefScanJobSource
 from lp.code.interfaces.gitrepository import IGitRepositorySet
+from lp.code.tests.helpers import GitHostingFixture
 from lp.services.job.runner import JobRunner
 from lp.services.webapp.publisher import canonical_url
 from lp.testing import (
@@ -25,8 +25,6 @@
     BrowserTestCase,
     )
 from lp.testing.dbuser import dbuser
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import LaunchpadFunctionalLayer
 from lp.testing.pages import (
     extract_text,
@@ -44,10 +42,7 @@
 
     def setUp(self):
         super(TestGitRefView, self).setUp()
-        self.hosting_client = FakeMethod()
-        self.hosting_client.getLog = FakeMethod(result=[])
-        self.useFixture(
-            ZopeUtilityFixture(self.hosting_client, IGitHostingClient))
+        self.hosting_fixture = self.useFixture(GitHostingFixture())
 
     def test_rendering(self):
         repository = self.factory.makeGitRepository(
@@ -121,13 +116,13 @@
             for i in range(5)]
 
     def scanRef(self, ref, tip):
-        self.hosting_client.getRefs = FakeMethod(
-            result={
-                ref.path: {"object": {"sha1": tip["sha1"], "type": "commit"}},
-                })
-        self.hosting_client.getCommits = FakeMethod(result=[tip])
-        self.hosting_client.getProperties = FakeMethod(
-            result={"default_branch": ref.path})
+        self.hosting_fixture.getRefs.result = {
+            ref.path: {"object": {"sha1": tip["sha1"], "type": "commit"}},
+            }
+        self.hosting_fixture.getCommits.result = [tip]
+        self.hosting_fixture.getProperties.result = {
+            "default_branch": ref.path,
+            }
         job = getUtility(IGitRefScanJobSource).create(
             removeSecurityProxy(ref.repository))
         with dbuser("branchscanner"):
@@ -136,7 +131,7 @@
     def test_recent_commits(self):
         [ref] = self.factory.makeGitRefs(paths=[u"refs/heads/branch"])
         log = self.makeCommitLog()
-        self.hosting_client.getLog.result = list(reversed(log))
+        self.hosting_fixture.getLog.result = list(reversed(log))
         self.scanRef(ref, log[-1])
         view = create_initialized_view(ref, "+index")
         expected_texts = list(reversed([
@@ -156,7 +151,7 @@
     def test_recent_commits_with_merge(self):
         [ref] = self.factory.makeGitRefs(paths=[u"refs/heads/branch"])
         log = self.makeCommitLog()
-        self.hosting_client.getLog.result = list(reversed(log))
+        self.hosting_fixture.getLog.result = list(reversed(log))
         self.scanRef(ref, log[-1])
         mp = self.factory.makeBranchMergeProposalForGit(target_ref=ref)
         merged_tip = dict(log[-1])
@@ -182,7 +177,7 @@
     def test_recent_commits_with_merge_from_deleted_ref(self):
         [ref] = self.factory.makeGitRefs(paths=[u"refs/heads/branch"])
         log = self.makeCommitLog()
-        self.hosting_client.getLog.result = list(reversed(log))
+        self.hosting_fixture.getLog.result = list(reversed(log))
         self.scanRef(ref, log[-1])
         mp = self.factory.makeBranchMergeProposalForGit(target_ref=ref)
         merged_tip = dict(log[-1])
@@ -209,7 +204,7 @@
     def test_all_commits_link(self):
         [ref] = self.factory.makeGitRefs(paths=[u"refs/heads/branch"])
         log = self.makeCommitLog()
-        self.hosting_client.getLog.result = list(reversed(log))
+        self.hosting_fixture.getLog.result = list(reversed(log))
         self.scanRef(ref, log[-1])
         view = create_initialized_view(ref, "+index")
         recent_commits_tag = soupmatchers.Tag(

=== modified file 'lib/lp/code/browser/tests/test_gitrepository.py'
--- lib/lp/code/browser/tests/test_gitrepository.py	2016-05-19 11:52:16 +0000
+++ lib/lp/code/browser/tests/test_gitrepository.py	2016-09-07 11:20:36 +0000
@@ -22,8 +22,8 @@
 from lp.app.enums import InformationType
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.interfaces.services import IService
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.interfaces.revision import IRevisionSet
+from lp.code.tests.helpers import GitHostingFixture
 from lp.registry.enums import BranchSharingPolicy
 from lp.registry.interfaces.accesspolicy import IAccessPolicySource
 from lp.registry.interfaces.person import PersonVisibility
@@ -39,8 +39,6 @@
     record_two_runs,
     TestCaseWithFactory,
     )
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import DatabaseFunctionalLayer
 from lp.testing.matchers import (
     Contains,
@@ -648,9 +646,7 @@
     def test_change_default_branch(self):
         # An authorised user can change the default branch to one that
         # exists.  They may omit "refs/heads/".
-        hosting_client = FakeMethod()
-        hosting_client.setProperties = FakeMethod()
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        hosting_fixture = self.useFixture(GitHostingFixture())
         person = self.factory.makePerson()
         repository = self.factory.makeGitRepository(owner=person)
         master, new = self.factory.makeGitRefs(
@@ -665,7 +661,7 @@
             self.assertEqual(
                 [((repository.getInternalPath(),),
                  {u"default_branch": u"refs/heads/new"})],
-                hosting_client.setProperties.calls)
+                hosting_fixture.setProperties.calls)
             self.assertEqual(u"refs/heads/new", repository.default_branch)
 
     def test_change_default_branch_nonexistent(self):
@@ -762,10 +758,9 @@
     layer = DatabaseFunctionalLayer
 
     def test_render(self):
-        hosting_client = FakeMethod()
         diff = u"A fake diff\n"
-        hosting_client.getDiff = FakeMethod(result={"patch": diff})
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        hosting_fixture = self.useFixture(GitHostingFixture(
+            diff={"patch": diff}))
         person = self.factory.makePerson()
         repository = self.factory.makeGitRepository(owner=person)
         browser = self.getUserBrowser(
@@ -773,22 +768,20 @@
         with person_logged_in(person):
             self.assertEqual(
                 [((repository.getInternalPath(), "0123456^", "0123456"), {})],
-                hosting_client.getDiff.calls)
+                hosting_fixture.getDiff.calls)
         self.assertEqual(
             'text/x-patch;charset=UTF-8', browser.headers["Content-Type"])
         self.assertEqual(str(len(diff)), browser.headers["Content-Length"])
         self.assertEqual(
             "attachment; filename=0123456^_0123456.diff",
             browser.headers["Content-Disposition"])
-        self.assertEqual("A fake diff\n", browser.contents)
+        self.assertEqual(diff, browser.contents)
 
     def test_security(self):
         # A user who can see a private repository can fetch diffs from it,
         # but other users cannot.
-        hosting_client = FakeMethod()
         diff = u"A fake diff\n"
-        hosting_client.getDiff = FakeMethod(result={"patch": diff})
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture(diff={"patch": diff}))
         person = self.factory.makePerson()
         project = self.factory.makeProduct(
             owner=person, information_type=InformationType.PROPRIETARY)
@@ -799,7 +792,7 @@
             repository_url = canonical_url(repository)
         browser = self.getUserBrowser(
             repository_url + "/+diff/0123456/0123456^", user=person)
-        self.assertEqual("A fake diff\n", browser.contents)
+        self.assertEqual(diff, browser.contents)
         self.useFixture(FakeLogger())
         self.assertRaises(
             Unauthorized, self.getUserBrowser,

=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2016-08-12 12:56:41 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2016-09-07 11:20:36 +0000
@@ -35,13 +35,15 @@
 from lp.code.browser.sourcepackagerecipebuild import (
     SourcePackageRecipeBuildView,
     )
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.interfaces.sourcepackagerecipe import (
     GIT_RECIPES_FEATURE_FLAG,
     MINIMAL_RECIPE_TEXT_BZR,
     MINIMAL_RECIPE_TEXT_GIT,
     )
-from lp.code.tests.helpers import recipe_parser_newest_version
+from lp.code.tests.helpers import (
+    GitHostingFixture,
+    recipe_parser_newest_version,
+    )
 from lp.registry.interfaces.person import TeamMembershipPolicy
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
@@ -64,8 +66,6 @@
     time_counter,
     )
 from lp.testing.deprecated import LaunchpadFormHarness
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadFunctionalLayer,
@@ -857,9 +857,7 @@
 
     def setUp(self):
         super(TestSourcePackageRecipeAddViewGit, self).setUp()
-        hosting_client = FakeMethod()
-        hosting_client.getLog = FakeMethod(result=[])
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture())
 
     def makeBranchAndPackage(self):
         product = self.factory.makeProduct(

=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
--- lib/lp/code/model/tests/test_branchmergeproposal.py	2016-08-24 13:51:36 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposal.py	2016-09-07 11:20:36 +0000
@@ -60,7 +60,6 @@
     IBranchMergeProposalGetter,
     notify_modified,
     )
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.model.branchmergeproposal import (
     BranchMergeProposalGetter,
     is_valid_transition,
@@ -72,6 +71,7 @@
     )
 from lp.code.tests.helpers import (
     add_revision_to_branch,
+    GitHostingFixture,
     make_merge_proposal_without_reviewers,
     )
 from lp.registry.enums import TeamMembershipPolicy
@@ -80,6 +80,7 @@
 from lp.services.config import config
 from lp.services.database.constants import UTC_NOW
 from lp.services.features.testing import FeatureFixture
+from lp.services.memcache.testing import MemcacheFixture
 from lp.services.webapp import canonical_url
 from lp.services.xref.interfaces import IXRefSet
 from lp.testing import (
@@ -95,8 +96,6 @@
     )
 from lp.testing.dbuser import dbuser
 from lp.testing.factory import LaunchpadObjectFactory
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadFunctionalLayer,
@@ -1460,12 +1459,9 @@
 
     def setUp(self):
         super(TestBranchMergeProposalBugsGit, self).setUp()
-        # Disable GitRef._getLog's use of memcache; we don't need it here,
-        # it requires more time-consuming test setup, and it makes it harder
-        # to repeatedly run updateRelatedBugsFromSource with different log
-        # responses.
-        self.useFixture(FeatureFixture(
-            {u"code.git.log.disable_memcache": u"on"}))
+        self.hosting_fixture = self.useFixture(GitHostingFixture(
+            disable_memcache=False))
+        self.memcache_fixture = self.useFixture(MemcacheFixture())
 
     def _makeBranchMergeProposal(self):
         return self.factory.makeBranchMergeProposalForGit()
@@ -1475,8 +1471,7 @@
         # parses commit messages.
         bugs = [self.factory.makeBug() for _ in range(3)]
         bmp = self._makeBranchMergeProposal()
-        hosting_client = FakeMethod()
-        hosting_client.getLog = FakeMethod(result=[
+        self.hosting_fixture.getLog.result = [
             {
                 u"sha1": bmp.source_git_commit_sha1,
                 u"message": u"Commit 1\n\nLP: #%d" % bugs[0].id,
@@ -1495,8 +1490,7 @@
                 # Non-existent bug ID will not be returned.
                 u"message": u"Non-existent bug; LP: #%d" % (bugs[2].id + 100),
                 },
-            ])
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+            ]
         related_bugs = bmp._fetchRelatedBugIDsFromSource()
         path = "%s:%s" % (
             bmp.target_git_repository.getInternalPath(),
@@ -1505,22 +1499,18 @@
             [((path, bmp.source_git_commit_sha1),
               {"limit": 10, "stop": bmp.target_git_commit_sha1,
                "logger": None})],
-            hosting_client.getLog.calls)
+            self.hosting_fixture.getLog.calls)
         self.assertContentEqual([bugs[0].id, bugs[2].id], related_bugs)
 
     def _setUpLog(self, bugs):
         """Set up a fake log response referring to the given bugs."""
-        if getattr(self, "hosting_client", None) is None:
-            self.hosting_client = FakeMethod()
-            self.hosting_client.getLog = FakeMethod()
-            self.useFixture(
-                ZopeUtilityFixture(self.hosting_client, IGitHostingClient))
-        self.hosting_client.getLog = FakeMethod(result=[
+        self.hosting_fixture.getLog.result = [
             {
                 u"sha1": unicode(hashlib.sha1(str(i)).hexdigest()),
                 u"message": u"LP: #%d" % bug.id,
                 }
-            for i, bug in enumerate(bugs)])
+            for i, bug in enumerate(bugs)]
+        self.memcache_fixture.clear()
 
     def test_updateRelatedBugsFromSource_no_links(self):
         # updateRelatedBugsFromSource does nothing if there are no related

=== modified file 'lib/lp/code/model/tests/test_branchmergeproposaljobs.py'
--- lib/lp/code/model/tests/test_branchmergeproposaljobs.py	2016-09-06 15:34:38 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposaljobs.py	2016-09-07 11:20:36 +0000
@@ -76,7 +76,6 @@
     verifyObject,
     )
 from lp.testing.dbuser import dbuser
-from lp.testing.fakemethod import FakeMethod
 from lp.testing.layers import (
     CeleryBzrsyncdJobLayer,
     CeleryJobLayer,
@@ -258,7 +257,6 @@
 
     def test_run_git(self):
         bmp, _, _, patch = self.createExampleGitMerge()
-        self.hosting_client.getLog = FakeMethod(result=[])
         job = UpdatePreviewDiffJob.create(bmp)
         with dbuser("merge-proposal-jobs"):
             JobRunner([job]).runAll()
@@ -268,12 +266,12 @@
         # The merge proposal has its related bugs updated.
         bug = self.factory.makeBug()
         bmp, _, _, patch = self.createExampleGitMerge()
-        self.hosting_client.getLog = FakeMethod(result=[
+        self.hosting_fixture.getLog.result = [
             {
                 u"sha1": unicode(hashlib.sha1("tip").hexdigest()),
                 u"message": u"Fix upside-down messages\n\nLP: #%d" % bug.id,
                 },
-            ])
+            ]
         job = UpdatePreviewDiffJob.create(bmp)
         with dbuser("merge-proposal-jobs"):
             JobRunner([job]).runAll()
@@ -383,7 +381,6 @@
         self.useFixture(FeatureFixture(
             {BRANCH_MERGE_PROPOSAL_WEBHOOKS_FEATURE_FLAG: "on"}))
         bmp = self.createExampleGitMerge()[0]
-        self.hosting_client.getLog = FakeMethod(result=[])
         hook = self.factory.makeWebhook(
             target=bmp.target_git_repository,
             event_types=["merge-proposal:0.1"])

=== modified file 'lib/lp/code/model/tests/test_diff.py'
--- lib/lp/code/model/tests/test_diff.py	2016-09-06 15:34:38 +0000
+++ lib/lp/code/model/tests/test_diff.py	2016-09-07 11:20:36 +0000
@@ -17,7 +17,6 @@
     RemoveLine,
     )
 import transaction
-from zope.interface import implementer
 from zope.security.proxy import removeSecurityProxy
 
 from lp.app.errors import NotFoundError
@@ -26,12 +25,12 @@
     IIncrementalDiff,
     IPreviewDiff,
     )
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.model.diff import (
     Diff,
     PreviewDiff,
     )
 from lp.code.model.directbranchcommit import DirectBranchCommit
+from lp.code.tests.helpers import GitHostingFixture
 from lp.services.librarian.interfaces.client import (
     LIBRARIAN_SERVER_DEFAULT_TIMEOUT,
     )
@@ -52,7 +51,6 @@
     verifyObject,
     )
 from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import (
     LaunchpadFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -99,12 +97,6 @@
     return bmp, source_rev_id, target_rev_id
 
 
-@implementer(IGitHostingClient)
-class FakeGitHostingClient:
-
-    pass
-
-
 class DiffTestCase(TestCaseWithFactory):
 
     def createExampleBzrMerge(self):
@@ -147,13 +139,6 @@
         return (source_bzr, source_rev_id, target_bzr, prerequisite_bzr,
                 prerequisite)
 
-    def installFakeGitMergeDiff(self, result=None, failure=None):
-        self.hosting_client = FakeGitHostingClient()
-        self.hosting_client.getMergeDiff = FakeMethod(
-            result=result, failure=failure)
-        self.useFixture(
-            ZopeUtilityFixture(self.hosting_client, IGitHostingClient))
-
     def createExampleGitMerge(self):
         """Create an example Git-based merge scenario.
 
@@ -178,10 +163,10 @@
              a
             +b
             """) % (target_sha1[:7], source_sha1[:7])
-        self.installFakeGitMergeDiff(result={
+        self.hosting_fixture = self.useFixture(GitHostingFixture(merge_diff={
             "patch": patch,
             "conflicts": ["foo"],
-            })
+            }))
         return bmp, source_sha1, target_sha1, patch
 
 

=== modified file 'lib/lp/code/model/tests/test_gitjob.py'
--- lib/lp/code/model/tests/test_gitjob.py	2016-06-25 08:07:19 +0000
+++ lib/lp/code/model/tests/test_gitjob.py	2016-09-07 11:20:36 +0000
@@ -19,14 +19,12 @@
     MatchesSetwise,
     MatchesStructure,
     )
-from zope.interface import implementer
 from zope.security.proxy import removeSecurityProxy
 
 from lp.code.enums import GitObjectType
 from lp.code.interfaces.branchmergeproposal import (
     BRANCH_MERGE_PROPOSAL_WEBHOOKS_FEATURE_FLAG,
     )
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.interfaces.gitjob import (
     IGitJob,
     IGitRefScanJob,
@@ -39,6 +37,7 @@
     GitRefScanJob,
     ReclaimGitRepositorySpaceJob,
     )
+from lp.code.tests.helpers import GitHostingFixture
 from lp.services.config import config
 from lp.services.database.constants import UTC_NOW
 from lp.services.features.testing import FeatureFixture
@@ -49,32 +48,12 @@
     time_counter,
     )
 from lp.testing.dbuser import dbuser
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
-    LaunchpadZopelessLayer,
+    ZopelessDatabaseLayer,
     )
 
 
-@implementer(IGitHostingClient)
-class FakeGitHostingClient:
-
-    def __init__(self, refs, commits, default_branch=u"refs/heads/master"):
-        self._refs = refs
-        self._commits = commits
-        self._default_branch = default_branch
-
-    def getRefs(self, paths):
-        return self._refs
-
-    def getCommits(self, path, commit_oids, logger=None):
-        return self._commits
-
-    def getProperties(self, path):
-        return {u"default_branch": self._default_branch}
-
-
 class TestGitJob(TestCaseWithFactory):
     """Tests for `GitJob`."""
 
@@ -90,7 +69,7 @@
 class TestGitJobDerived(TestCaseWithFactory):
     """Tests for `GitJobDerived`."""
 
-    layer = LaunchpadZopelessLayer
+    layer = ZopelessDatabaseLayer
 
     def test_getOopsMailController(self):
         """By default, no mail is sent about failed BranchJobs."""
@@ -103,7 +82,7 @@
 class TestGitRefScanJob(TestCaseWithFactory):
     """Tests for `GitRefScanJob`."""
 
-    layer = LaunchpadZopelessLayer
+    layer = ZopelessDatabaseLayer
 
     @staticmethod
     def makeFakeRefs(paths):
@@ -165,10 +144,9 @@
         author = repository.owner
         author_date_start = datetime(2015, 1, 1, tzinfo=pytz.UTC)
         author_date_gen = time_counter(author_date_start, timedelta(days=1))
-        hosting_client = FakeGitHostingClient(
-            self.makeFakeRefs(paths),
-            self.makeFakeCommits(author, author_date_gen, paths))
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture(
+            refs=self.makeFakeRefs(paths),
+            commits=self.makeFakeCommits(author, author_date_gen, paths)))
         with dbuser("branchscanner"):
             JobRunner([job]).runAll()
         self.assertRefsMatch(repository.refs, repository, paths)
@@ -177,8 +155,7 @@
     def test_logs_bad_ref_info(self):
         repository = self.factory.makeGitRepository()
         job = GitRefScanJob.create(repository)
-        hosting_client = FakeGitHostingClient({u"refs/heads/master": {}}, [])
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture(refs={u"refs/heads/master": {}}))
         expected_message = (
             'Unconvertible ref refs/heads/master {}: '
             'ref info does not contain "object" key')
@@ -197,8 +174,7 @@
             target=repository, event_types=['git:push:0.1'])
         job = GitRefScanJob.create(repository)
         paths = (u'refs/heads/master', u'refs/tags/2.0')
-        hosting_client = FakeGitHostingClient(self.makeFakeRefs(paths), [])
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture(refs=self.makeFakeRefs(paths)))
         with dbuser('branchscanner'):
             JobRunner([job]).runAll()
         delivery = hook.deliveries.one()
@@ -244,11 +220,8 @@
                 'type': 'commit',
                 }},
             }
-        hosting_client = FakeGitHostingClient(new_refs, [])
-        hosting_client.getLog = FakeMethod(result=[])
-        hosting_client.detectMerges = FakeMethod(
-            result={source.commit_sha1: u'0' * 40})
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        new_merges = {source.commit_sha1: u'0' * 40}
+        self.useFixture(GitHostingFixture(refs=new_refs, merges=new_merges))
         job = GitRefScanJob.create(repository)
         with dbuser('branchscanner'):
             JobRunner([job]).runAll()
@@ -307,7 +280,7 @@
 class TestReclaimGitRepositorySpaceJob(TestCaseWithFactory):
     """Tests for `ReclaimGitRepositorySpaceJob`."""
 
-    layer = LaunchpadZopelessLayer
+    layer = ZopelessDatabaseLayer
 
     def test_provides_interface(self):
         # `ReclaimGitRepositorySpaceJob` objects provide
@@ -352,17 +325,15 @@
     def test_run(self):
         # Running a job to reclaim space sends a request to the hosting
         # service.
-        hosting_client = FakeGitHostingClient({}, [])
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        hosting_fixture = self.useFixture(GitHostingFixture())
         name = "/~owner/+git/gone"
         path = "1"
         job = ReclaimGitRepositorySpaceJob.create(name, path)
         self.makeJobReady(job)
         [job] = list(ReclaimGitRepositorySpaceJob.iterReady())
         with dbuser("branchscanner"):
-            hosting_client.delete = FakeMethod()
             JobRunner([job]).runAll()
-        self.assertEqual([(path,)], hosting_client.delete.extract_args())
+        self.assertEqual([(path,)], hosting_fixture.delete.extract_args())
 
 
 # XXX cjwatson 2015-03-12: We should test that the jobs work via Celery too,

=== modified file 'lib/lp/code/model/tests/test_gitref.py'
--- lib/lp/code/model/tests/test_gitref.py	2016-09-06 15:34:38 +0000
+++ lib/lp/code/model/tests/test_gitref.py	2016-09-07 11:20:36 +0000
@@ -27,7 +27,7 @@
 from lp.app.interfaces.informationtype import IInformationType
 from lp.app.interfaces.launchpad import IPrivacy
 from lp.code.errors import InvalidBranchMergeProposal
-from lp.code.interfaces.githosting import IGitHostingClient
+from lp.code.tests.helpers import GitHostingFixture
 from lp.services.features.testing import FeatureFixture
 from lp.services.memcache.interfaces import IMemcacheClient
 from lp.services.webapp.interfaces import OAuthPermission
@@ -39,8 +39,6 @@
     TestCaseWithFactory,
     verifyObject,
     )
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadFunctionalLayer,
@@ -142,10 +140,7 @@
                 u"tree": unicode(hashlib.sha1("").hexdigest()),
                 },
             ]
-        self.hosting_client = FakeMethod()
-        self.hosting_client.getLog = FakeMethod(result=self.log)
-        self.useFixture(
-            ZopeUtilityFixture(self.hosting_client, IGitHostingClient))
+        self.hosting_fixture = self.useFixture(GitHostingFixture(log=self.log))
 
     def test_basic(self):
         commits = self.ref.getCommits(self.sha1_tip)
@@ -153,7 +148,7 @@
         self.assertEqual(
             [((path, self.sha1_tip),
               {"limit": None, "stop": None, "logger": None})],
-            self.hosting_client.getLog.calls)
+            self.hosting_fixture.getLog.calls)
         self.assertThat(commits, MatchesListwise([
             ContainsDict({
                 "sha1": Equals(self.sha1_tip),
@@ -189,7 +184,7 @@
                 "commit_message": Is(None),
                 }),
             ]))
-        self.assertEqual([], self.hosting_client.getLog.calls)
+        self.assertEqual([], self.hosting_fixture.getLog.calls)
         path = self.ref.repository.getInternalPath()
         key = u"git.launchpad.dev:git-log:%s:%s" % (path, self.sha1_tip)
         self.assertIsNone(getUtility(IMemcacheClient).get(key.encode("UTF-8")))
@@ -210,7 +205,7 @@
         self.assertEqual(
             [((path, self.sha1_tip),
               {"limit": 10, "stop": self.sha1_root, "logger": None})],
-            self.hosting_client.getLog.calls)
+            self.hosting_fixture.getLog.calls)
         key = u"git.launchpad.dev:git-log:%s:%s:limit=10:stop=%s" % (
             path, self.sha1_tip, self.sha1_root)
         self.assertEqual(
@@ -228,7 +223,7 @@
         self.assertEqual(
             [((path, self.sha1_tip),
               {"limit": None, "stop": self.sha1_root, "logger": None})],
-            self.hosting_client.getLog.calls)
+            self.hosting_fixture.getLog.calls)
         key = u"git.launchpad.dev:git-log:%s:%s:stop=%s" % (
             path, self.sha1_tip, self.sha1_root)
         self.assertEqual(

=== modified file 'lib/lp/code/model/tests/test_gitrepository.py'
--- lib/lp/code/model/tests/test_gitrepository.py	2016-09-06 15:34:38 +0000
+++ lib/lp/code/model/tests/test_gitrepository.py	2016-09-07 11:20:36 +0000
@@ -56,7 +56,6 @@
     BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,
     )
 from lp.code.interfaces.defaultgit import ICanHasDefaultGitRepository
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.interfaces.gitjob import (
     IGitRefScanJobSource,
     IGitRepositoryModifiedMailJobSource,
@@ -91,6 +90,7 @@
     DeletionOperation,
     GitRepository,
     )
+from lp.code.tests.helpers import GitHostingFixture
 from lp.code.xmlrpc.git import GitAPI
 from lp.registry.enums import (
     BranchSharingPolicy,
@@ -130,12 +130,9 @@
     verifyObject,
     )
 from lp.testing.dbuser import dbuser
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadFunctionalLayer,
-    LaunchpadZopelessLayer,
     ZopelessDatabaseLayer,
     )
 from lp.testing.mail_helpers import pop_notifications
@@ -514,7 +511,7 @@
     """Test determination and application of repository deletion
     consequences."""
 
-    layer = LaunchpadZopelessLayer
+    layer = ZopelessDatabaseLayer
 
     def setUp(self):
         super(TestGitRepositoryDeletionConsequences, self).setUp(
@@ -1141,8 +1138,7 @@
         self.assertRefsMatch(repository.refs, repository, paths)
         master_sha1 = repository.getRefByPath(u"refs/heads/master").commit_sha1
         foo_sha1 = repository.getRefByPath(u"refs/heads/foo").commit_sha1
-        hosting_client = FakeMethod()
-        hosting_client.getRefs = FakeMethod(result={
+        self.useFixture(GitHostingFixture(refs={
             u"refs/heads/master": {
                 u"object": {
                     u"sha1": u"1111111111111111111111111111111111111111",
@@ -1161,8 +1157,7 @@
                     u"type": u"commit",
                     },
                 },
-            })
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+            }))
         refs_to_upsert, refs_to_remove = repository.planRefChanges("dummy")
 
         expected_upsert = {
@@ -1195,16 +1190,14 @@
                 },
             }
         repository.createOrUpdateRefs(refs_info)
-        hosting_client = FakeMethod()
-        hosting_client.getRefs = FakeMethod(result={
+        self.useFixture(GitHostingFixture(refs={
             u"refs/heads/blob": {
                 u"object": {
                     u"sha1": blob_sha1,
                     u"type": u"blob",
                     },
                 },
-            })
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+            }))
         self.assertEqual(({}, set()), repository.planRefChanges("dummy"))
 
     def test_fetchRefCommits(self):
@@ -1218,8 +1211,7 @@
         epoch = datetime.fromtimestamp(0, tz=pytz.UTC)
         author_date = datetime(2015, 1, 1, tzinfo=pytz.UTC)
         committer_date = datetime(2015, 1, 2, tzinfo=pytz.UTC)
-        hosting_client = FakeMethod()
-        hosting_client.getCommits = FakeMethod(result=[
+        hosting_fixture = self.useFixture(GitHostingFixture(commits=[
             {
                 u"sha1": master_sha1,
                 u"message": u"tip of master",
@@ -1235,8 +1227,7 @@
                     },
                 u"parents": [],
                 u"tree": unicode(hashlib.sha1("").hexdigest()),
-                }])
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+                }]))
         refs = {
             u"refs/heads/master": {
                 u"sha1": master_sha1,
@@ -1250,7 +1241,7 @@
         GitRepository.fetchRefCommits("dummy", refs)
 
         expected_oids = [master_sha1, foo_sha1]
-        [(_, observed_oids)] = hosting_client.getCommits.extract_args()
+        [(_, observed_oids)] = hosting_fixture.getCommits.extract_args()
         self.assertContentEqual(expected_oids, observed_oids)
         expected_author_addr = u"%s <%s>" % (author.displayname, author_email)
         [expected_author] = getUtility(IRevisionSet).acquireRevisionAuthors(
@@ -1320,9 +1311,7 @@
         self.assertThat(repository.refs, MatchesSetwise(*matchers))
 
     def test_set_default_branch(self):
-        hosting_client = FakeMethod()
-        hosting_client.setProperties = FakeMethod()
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        hosting_fixture = self.useFixture(GitHostingFixture())
         repository = self.factory.makeGitRepository()
         self.factory.makeGitRefs(
             repository=repository,
@@ -1333,20 +1322,18 @@
         self.assertEqual(
             [((repository.getInternalPath(),),
              {u"default_branch": u"refs/heads/new"})],
-            hosting_client.setProperties.calls)
+            hosting_fixture.setProperties.calls)
         self.assertEqual(u"refs/heads/new", repository.default_branch)
 
     def test_set_default_branch_unchanged(self):
-        hosting_client = FakeMethod()
-        hosting_client.setProperties = FakeMethod()
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        hosting_fixture = self.useFixture(GitHostingFixture())
         repository = self.factory.makeGitRepository()
         self.factory.makeGitRefs(
             repository=repository, paths=[u"refs/heads/master"])
         removeSecurityProxy(repository)._default_branch = u"refs/heads/master"
         with person_logged_in(repository.owner):
             repository.default_branch = u"master"
-        self.assertEqual([], hosting_client.setProperties.calls)
+        self.assertEqual([], hosting_fixture.setProperties.calls)
         self.assertEqual(u"refs/heads/master", repository.default_branch)
 
 
@@ -1979,7 +1966,7 @@
 
 class TestGitRepositoryDetectMerges(TestCaseWithFactory):
 
-    layer = LaunchpadZopelessLayer
+    layer = ZopelessDatabaseLayer
 
     def test_markProposalMerged(self):
         # A merge proposal that is merged is marked as such.
@@ -2026,10 +2013,8 @@
             target_ref=target_1, source_ref=source_2)
         bmp3 = self.factory.makeBranchMergeProposalForGit(
             target_ref=target_2, source_ref=source_1)
-        hosting_client = FakeMethod()
-        hosting_client.detectMerges = FakeMethod(
-            result={source_1.commit_sha1: u"0" * 40})
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        hosting_fixture = self.useFixture(GitHostingFixture(
+            merges={source_1.commit_sha1: u"0" * 40}))
         refs_info = {
             u"refs/heads/target-1": {
                 u"sha1": u"0" * 40,
@@ -2051,7 +2036,7 @@
              set([source_1.commit_sha1])),
             ]
         self.assertContentEqual(
-            expected_args, hosting_client.detectMerges.extract_args())
+            expected_args, hosting_fixture.detectMerges.extract_args())
         self.assertEqual(BranchMergeProposalStatus.MERGED, bmp1.queue_status)
         self.assertEqual(u"0" * 40, bmp1.merged_revision_id)
         self.assertEqual(
@@ -2076,17 +2061,13 @@
 
     def test_getBlob_with_default_rev(self):
         repository = self.factory.makeGitRepository()
-        hosting_client = FakeMethod()
-        hosting_client.getBlob = FakeMethod(result='Some text')
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture(blob='Some text'))
         ret = repository.getBlob('src/README.txt')
         self.assertEqual('Some text', ret)
 
     def test_getBlob_with_rev(self):
         repository = self.factory.makeGitRepository()
-        hosting_client = FakeMethod()
-        hosting_client.getBlob = FakeMethod(result='Some text')
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture(blob='Some text'))
         ret = repository.getBlob('src/README.txt', 'some-rev')
         self.assertEqual('Some text', ret)
 

=== modified file 'lib/lp/code/stories/branches/xx-code-review-comments.txt'
--- lib/lp/code/stories/branches/xx-code-review-comments.txt	2016-05-13 17:59:35 +0000
+++ lib/lp/code/stories/branches/xx-code-review-comments.txt	2016-09-07 11:20:36 +0000
@@ -191,19 +191,16 @@
 The same thing works for Git.  Note that the hosting client returns newest
 log entries first.
 
-    >>> from lp.code.interfaces.githosting import IGitHostingClient
-    >>> from lp.testing.fakemethod import FakeMethod
-    >>> from lp.testing.fixture import ZopeUtilityFixture
+    >>> from lp.code.tests.helpers import GitHostingFixture
 
     >>> login('admin@xxxxxxxxxxxxx')
     >>> bmp = factory.makeBranchMergeProposalForGit()
     >>> bmp.requestReview(review_date)
     >>> epoch = datetime.fromtimestamp(0, tz=pytz.UTC)
     >>> commit_date = review_date + timedelta(days=1)
-    >>> hosting_client = FakeMethod()
-    >>> hosting_client.getLog = FakeMethod(result=[])
+    >>> hosting_fixture = GitHostingFixture()
     >>> for i in range(2):
-    ...     hosting_client.getLog.result.insert(0, {
+    ...     hosting_fixture.getLog.result.insert(0, {
     ...         u'sha1': unicode(i * 2) * 40,
     ...         u'message': u'Testing commits in conversation',
     ...         u'author': {
@@ -212,7 +209,7 @@
     ...             u'time': int((commit_date - epoch).total_seconds()),
     ...             },
     ...         })
-    ...     hosting_client.getLog.result.insert(0, {
+    ...     hosting_fixture.getLog.result.insert(0, {
     ...         u'sha1': unicode(i * 2 + 1) * 40,
     ...         u'message': u'and it works!',
     ...         u'author': {
@@ -225,7 +222,7 @@
     >>> url = canonical_url(bmp)
     >>> logout()
 
-    >>> with ZopeUtilityFixture(hosting_client, IGitHostingClient):
+    >>> with hosting_fixture:
     ...     browser.open(url)
     >>> print_tag_with_id(browser.contents, 'conversation')
     ~.../+git/...:... updated on 2009-09-12 ...

=== modified file 'lib/lp/code/stories/sourcepackagerecipes/xx-recipe-listings.txt'
--- lib/lp/code/stories/sourcepackagerecipes/xx-recipe-listings.txt	2016-05-13 21:12:59 +0000
+++ lib/lp/code/stories/sourcepackagerecipes/xx-recipe-listings.txt	2016-09-07 11:20:36 +0000
@@ -125,13 +125,9 @@
 If we start from one of the branches instead, then only two recipes are
 listed.
 
-    >>> from lp.code.interfaces.githosting import IGitHostingClient
-    >>> from lp.testing.fakemethod import FakeMethod
-    >>> from lp.testing.fixture import ZopeUtilityFixture
+    >>> from lp.code.tests.helpers import GitHostingFixture
 
-    >>> hosting_client = FakeMethod()
-    >>> hosting_client.getLog = FakeMethod(result=[])
-    >>> with ZopeUtilityFixture(hosting_client, IGitHostingClient):
+    >>> with GitHostingFixture():
     ...     nopriv_browser.open(ref1_url)
     >>> nopriv_browser.getLink('2 recipes').click()
     >>> print nopriv_browser.url

=== modified file 'lib/lp/code/tests/helpers.py'
--- lib/lp/code/tests/helpers.py	2015-09-28 17:38:45 +0000
+++ lib/lp/code/tests/helpers.py	2016-09-07 11:20:36 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2016 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Helper functions for code testing live here."""
@@ -23,6 +23,7 @@
 from itertools import count
 
 from bzrlib.plugins.builder.recipe import RecipeParser
+import fixtures
 import transaction
 from zope.component import getUtility
 from zope.security.proxy import (
@@ -34,6 +35,7 @@
 from lp.code.interfaces.branchmergeproposal import (
     IBranchMergeProposalJobSource,
     )
+from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
 from lp.code.interfaces.revision import IRevisionSet
 from lp.code.model.seriessourcepackagebranch import (
@@ -42,10 +44,13 @@
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.database.sqlbase import cursor
+from lp.services.memcache.testing import MemcacheFixture
 from lp.testing import (
     run_with_login,
     time_counter,
     )
+from lp.testing.fakemethod import FakeMethod
+from lp.testing.fixture import ZopeUtilityFixture
 
 
 def mark_all_merge_proposal_jobs_done():
@@ -291,3 +296,38 @@
     c.execute('delete from codeimportjob')
     c.execute('delete from codeimport')
     c.execute('delete from branch')
+
+
+class GitHostingFixture(fixtures.Fixture):
+    """A fixture that temporarily registers a fake Git hosting client."""
+
+    def __init__(self, default_branch=u"refs/heads/master",
+                 refs=None, commits=None, log=None, diff=None, merge_diff=None,
+                 merges=None, blob=None, disable_memcache=True):
+        self.create = FakeMethod()
+        self.getProperties = FakeMethod(
+            result={u"default_branch": default_branch})
+        self.setProperties = FakeMethod()
+        self.getRefs = FakeMethod(result=({} if refs is None else refs))
+        self.getCommits = FakeMethod(
+            result=([] if commits is None else commits))
+        self.getLog = FakeMethod(result=([] if log is None else log))
+        self.getDiff = FakeMethod(result=({} if diff is None else diff))
+        self.getMergeDiff = FakeMethod(
+            result={} if merge_diff is None else merge_diff)
+        self.detectMerges = FakeMethod(
+            result=({} if merges is None else merges))
+        self.getBlob = FakeMethod(result=blob)
+        self.delete = FakeMethod()
+        self.disable_memcache = disable_memcache
+
+    def setUp(self):
+        super(GitHostingFixture, self).setUp()
+        self.useFixture(ZopeUtilityFixture(self, IGitHostingClient))
+        if self.disable_memcache:
+            # Most tests that involve GitRef._getLog don't want to cache the
+            # result: doing so requires more time-consuming test setup and
+            # makes it awkward to repeat the same call with different log
+            # responses.  For convenience, we make it easy to disable that
+            # here.
+            self.useFixture(MemcacheFixture())

=== modified file 'lib/lp/code/xmlrpc/tests/test_git.py'
--- lib/lp/code/xmlrpc/tests/test_git.py	2015-07-10 04:51:21 +0000
+++ lib/lp/code/xmlrpc/tests/test_git.py	2016-09-07 11:20:36 +0000
@@ -1,4 +1,4 @@
-# Copyright 2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2015-2016 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for the internal Git API."""
@@ -6,7 +6,6 @@
 __metaclass__ = type
 
 from zope.component import getUtility
-from zope.interface import implementer
 from zope.security.proxy import removeSecurityProxy
 
 from lp.app.enums import InformationType
@@ -16,12 +15,12 @@
     LAUNCHPAD_SERVICES,
     )
 from lp.code.interfaces.gitcollection import IAllGitRepositories
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.interfaces.gitjob import IGitRefScanJobSource
 from lp.code.interfaces.gitrepository import (
     GIT_REPOSITORY_NAME_VALIDATION_ERROR_MESSAGE,
     IGitRepositorySet,
     )
+from lp.code.tests.helpers import GitHostingFixture
 from lp.code.xmlrpc.git import GitAPI
 from lp.registry.enums import TeamMembershipPolicy
 from lp.services.webapp.escaping import html_escape
@@ -32,7 +31,6 @@
     person_logged_in,
     TestCaseWithFactory,
     )
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import (
     AppServerLayer,
     LaunchpadFunctionalLayer,
@@ -40,34 +38,13 @@
 from lp.xmlrpc import faults
 
 
-@implementer(IGitHostingClient)
-class FakeGitHostingClient:
-    """A GitHostingClient lookalike that just logs calls."""
-
-    def __init__(self):
-        self.calls = []
-
-    def create(self, path, clone_from=None):
-        self.calls.append(("create", path, clone_from))
-
-
-@implementer(IGitHostingClient)
-class BrokenGitHostingClient:
-    """A GitHostingClient lookalike that pretends the remote end is down."""
-
-    def create(self, path, clone_from=None):
-        raise GitRepositoryCreationFault("nothing here")
-
-
 class TestGitAPIMixin:
     """Helper methods for `IGitAPI` tests, and security-relevant tests."""
 
     def setUp(self):
         super(TestGitAPIMixin, self).setUp()
         self.git_api = GitAPI(None, None)
-        self.hosting_client = FakeGitHostingClient()
-        self.useFixture(
-            ZopeUtilityFixture(self.hosting_client, IGitHostingClient))
+        self.hosting_fixture = self.useFixture(GitHostingFixture())
         self.repository_set = getUtility(IGitRepositorySet)
 
     def assertGitRepositoryNotFound(self, requester, path, permission="read",
@@ -174,15 +151,16 @@
              "trailing": "", "private": private},
             translation)
         self.assertEqual(
-            ("create", repository.getInternalPath()),
-            self.hosting_client.calls[0][0:2])
+            (repository.getInternalPath(),),
+            self.hosting_fixture.create.extract_args()[0])
         return repository
 
     def assertCreatesFromClone(self, requester, path, cloned_from,
                                can_authenticate=False):
         self.assertCreates(requester, path, can_authenticate)
         self.assertEqual(
-            cloned_from.getInternalPath(), self.hosting_client.calls[0][2])
+            {"clone_from": cloned_from.getInternalPath()},
+            self.hosting_fixture.create.extract_kwargs()[0])
 
     def test_translatePath_private_repository(self):
         requester = self.factory.makePerson()
@@ -610,8 +588,8 @@
     def test_translatePath_create_broken_hosting_service(self):
         # If the hosting service is down, trying to create a repository
         # fails and doesn't leave junk around in the Launchpad database.
-        hosting_client = BrokenGitHostingClient()
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.hosting_fixture.create.failure = GitRepositoryCreationFault(
+            "nothing here")
         requester = self.factory.makePerson()
         initial_count = getUtility(IAllGitRepositories).count()
         oops_id = self.assertOopsOccurred(

=== added file 'lib/lp/services/memcache/testing.py'
--- lib/lp/services/memcache/testing.py	1970-01-01 00:00:00 +0000
+++ lib/lp/services/memcache/testing.py	2016-09-07 11:20:36 +0000
@@ -0,0 +1,37 @@
+# Copyright 2016 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+__metaclass__ = type
+__all__ = [
+    'MemcacheFixture',
+    ]
+
+import fixtures
+
+from lp.services.memcache.interfaces import IMemcacheClient
+from lp.testing.fixture import ZopeUtilityFixture
+
+
+class MemcacheFixture(fixtures.Fixture):
+    """A trivial in-process memcache fixture."""
+
+    def __init__(self):
+        self._cache = {}
+
+    def setUp(self):
+        super(MemcacheFixture, self).setUp()
+        self.useFixture(ZopeUtilityFixture(self, IMemcacheClient))
+
+    def get(self, key):
+        return self._cache.get(key)
+
+    def set(self, key, val):
+        self._cache[key] = val
+        return 1
+
+    def delete(self, key):
+        self._cache.pop(key, None)
+        return 1
+
+    def clear(self):
+        self._cache = {}

=== modified file 'lib/lp/snappy/browser/tests/test_hassnaps.py'
--- lib/lp/snappy/browser/tests/test_hassnaps.py	2016-07-18 13:48:42 +0000
+++ lib/lp/snappy/browser/tests/test_hassnaps.py	2016-09-07 11:20:36 +0000
@@ -12,16 +12,11 @@
     )
 
 from lp.code.interfaces.branch import IBranch
-from lp.code.interfaces.githosting import IGitHostingClient
 from lp.code.interfaces.gitrepository import IGitRepository
+from lp.code.tests.helpers import GitHostingFixture
 from lp.services.webapp import canonical_url
 from lp.testing import TestCaseWithFactory
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
-from lp.testing.layers import (
-    DatabaseFunctionalLayer,
-    LaunchpadFunctionalLayer,
-    )
+from lp.testing.layers import DatabaseFunctionalLayer
 from lp.testing.views import create_initialized_view
 
 
@@ -96,27 +91,24 @@
 
 class TestHasSnapsMenu(WithScenarios, TestCaseWithFactory):
 
-    needs_hosting_client = False
+    layer = DatabaseFunctionalLayer
+
+    needs_hosting_fixture = False
 
     scenarios = [
         ("Branch", {
-            "layer": DatabaseFunctionalLayer,
             "context_factory": make_branch,
             }),
         ("GitRef", {
-            "layer": LaunchpadFunctionalLayer,
             "context_factory": make_git_ref,
-            "needs_hosting_client": True,
+            "needs_hosting_fixture": True,
             }),
         ]
 
     def setUp(self):
         super(TestHasSnapsMenu, self).setUp()
-        if self.needs_hosting_client:
-            hosting_client = FakeMethod()
-            hosting_client.getLog = FakeMethod(result=[])
-            self.useFixture(
-                ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        if self.needs_hosting_fixture:
+            self.useFixture(GitHostingFixture())
 
     def makeSnap(self, context):
         if IBranch.providedBy(context):

=== modified file 'lib/lp/snappy/browser/tests/test_snap.py'
--- lib/lp/snappy/browser/tests/test_snap.py	2016-08-12 12:56:41 +0000
+++ lib/lp/snappy/browser/tests/test_snap.py	2016-09-07 11:20:36 +0000
@@ -41,7 +41,7 @@
 from lp.buildmaster.enums import BuildStatus
 from lp.buildmaster.interfaces.processor import IProcessorSet
 from lp.code.errors import GitRepositoryScanFault
-from lp.code.interfaces.githosting import IGitHostingClient
+from lp.code.tests.helpers import GitHostingFixture
 from lp.registry.enums import PersonVisibility
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
@@ -251,9 +251,7 @@
             MatchesTagText(content, "store_upload"))
 
     def test_create_new_snap_git(self):
-        hosting_client = FakeMethod()
-        hosting_client.getBlob = FakeMethod(result="")
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture(blob=""))
         [git_ref] = self.factory.makeGitRefs()
         source_display = git_ref.display_name
         browser = self.getViewBrowser(

=== modified file 'lib/lp/snappy/browser/tests/test_snaplisting.py'
--- lib/lp/snappy/browser/tests/test_snaplisting.py	2016-06-30 17:25:25 +0000
+++ lib/lp/snappy/browser/tests/test_snaplisting.py	2016-09-07 11:20:36 +0000
@@ -8,7 +8,7 @@
 import soupmatchers
 from testtools.matchers import Not
 
-from lp.code.interfaces.githosting import IGitHostingClient
+from lp.code.tests.helpers import GitHostingFixture
 from lp.services.database.constants import (
     ONE_DAY_AGO,
     UTC_NOW,
@@ -21,8 +21,6 @@
     person_logged_in,
     record_two_runs,
     )
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.fixture import ZopeUtilityFixture
 from lp.testing.layers import LaunchpadFunctionalLayer
 from lp.testing.matchers import HasQueryCount
 
@@ -57,9 +55,7 @@
         self.assertSnapsLink(repository, "2 snap packages", git_ref=ref)
 
     def test_git_ref_links_to_snaps(self):
-        hosting_client = FakeMethod()
-        hosting_client.getLog = FakeMethod(result=[])
-        self.useFixture(ZopeUtilityFixture(hosting_client, IGitHostingClient))
+        self.useFixture(GitHostingFixture())
         [ref] = self.factory.makeGitRefs()
         self.assertSnapsLink(ref, "2 snap packages", git_ref=ref)
 


Follow ups