Commit 4bac4b55 authored by maruel@chromium.org's avatar maruel@chromium.org

Create CachingRietveld to automatically cache results for presubmit checks.

Multiple presubmit checks may call the same function multiple times, so it's
worth caching the results to speed up the presubmit check run.

Convert presubmit_support, git-cl and gcl to use it.

R=dpranke@chromium.org
BUG=


Review URL: https://chromiumcodereview.appspot.com/11280143

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@169726 0039d316-1c4b-4281-b951-d872f2087c98
parent f01fad37
...@@ -363,7 +363,7 @@ class ChangeInfo(object): ...@@ -363,7 +363,7 @@ class ChangeInfo(object):
if not self._rpc_server: if not self._rpc_server:
if not self.rietveld: if not self.rietveld:
ErrorExit(CODEREVIEW_SETTINGS_FILE_NOT_FOUND) ErrorExit(CODEREVIEW_SETTINGS_FILE_NOT_FOUND)
self._rpc_server = rietveld.Rietveld(self.rietveld, None, None) self._rpc_server = rietveld.CachingRietveld(self.rietveld, None, None)
return self._rpc_server return self._rpc_server
def CloseIssue(self): def CloseIssue(self):
......
...@@ -742,8 +742,8 @@ or verify this branch is set up to track another (via the --track argument to ...@@ -742,8 +742,8 @@ or verify this branch is set up to track another (via the --track argument to
"""Returns an upload.RpcServer() to access this review's rietveld instance. """Returns an upload.RpcServer() to access this review's rietveld instance.
""" """
if not self._rpc_server: if not self._rpc_server:
self._rpc_server = rietveld.Rietveld(self.GetRietveldServer(), self._rpc_server = rietveld.CachingRietveld(
None, None) self.GetRietveldServer(), None, None)
return self._rpc_server return self._rpc_server
def _IssueSetting(self): def _IssueSetting(self):
......
...@@ -1237,7 +1237,7 @@ def Main(argv): ...@@ -1237,7 +1237,7 @@ def Main(argv):
logging.info('Found %d file(s).' % len(files)) logging.info('Found %d file(s).' % len(files))
rietveld_obj = None rietveld_obj = None
if options.rietveld_url: if options.rietveld_url:
rietveld_obj = rietveld.Rietveld( rietveld_obj = rietveld.CachingRietveld(
options.rietveld_url, options.rietveld_url,
options.rietveld_email, options.rietveld_email,
options.rietveld_password) options.rietveld_password)
......
...@@ -14,6 +14,7 @@ The following hypothesis are made: ...@@ -14,6 +14,7 @@ The following hypothesis are made:
- A patch set cannot be modified - A patch set cannot be modified
""" """
import copy
import json import json
import logging import logging
import re import re
...@@ -395,3 +396,53 @@ class Rietveld(object): ...@@ -395,3 +396,53 @@ class Rietveld(object):
# DEPRECATED. # DEPRECATED.
Send = get Send = get
class CachingRietveld(Rietveld):
"""Caches the common queries.
Not to be used in long-standing processes, like the commit queue.
"""
def __init__(self, *args, **kwargs):
super(CachingRietveld, self).__init__(*args, **kwargs)
self._cache = {}
def _lookup(self, function_name, args, update):
"""Caches the return values corresponding to the arguments.
It is important that the arguments are standardized, like None vs False.
"""
function_cache = self._cache.setdefault(function_name, {})
if args not in function_cache:
function_cache[args] = update(*args)
return copy.deepcopy(function_cache[args])
def get_description(self, issue):
return self._lookup(
'get_description',
(issue,),
super(CachingRietveld, self).get_description)
def get_issue_properties(self, issue, messages):
"""Returns the issue properties.
Because in practice the presubmit checks often ask without messages first
and then with messages, always ask with messages and strip off if not asked
for the messages.
"""
# It's a tad slower to request with the message but it's better than
# requesting the properties twice.
data = self._lookup(
'get_issue_properties',
(issue, True),
super(CachingRietveld, self).get_issue_properties)
if not messages:
# Assumes self._lookup uses deepcopy.
del data['messages']
return data
def get_patchset_properties(self, issue, patchset):
return self._lookup(
'get_patchset_properties',
(issue, patchset),
super(CachingRietveld, self).get_patchset_properties)
...@@ -69,6 +69,7 @@ class TestGitCl(TestCase): ...@@ -69,6 +69,7 @@ class TestGitCl(TestCase):
self.mock(git_cl.breakpad, 'SendStack', self._mocked_call) self.mock(git_cl.breakpad, 'SendStack', self._mocked_call)
self.mock(git_cl.presubmit_support, 'DoPresubmitChecks', PresubmitMock) self.mock(git_cl.presubmit_support, 'DoPresubmitChecks', PresubmitMock)
self.mock(git_cl.rietveld, 'Rietveld', RietveldMock) self.mock(git_cl.rietveld, 'Rietveld', RietveldMock)
self.mock(git_cl.rietveld, 'CachingRietveld', RietveldMock)
self.mock(git_cl.upload, 'RealMain', self.fail) self.mock(git_cl.upload, 'RealMain', self.fail)
self.mock(git_cl.watchlists, 'Watchlists', WatchlistsMock) self.mock(git_cl.watchlists, 'Watchlists', WatchlistsMock)
# It's important to reset settings to not have inter-tests interference. # It's important to reset settings to not have inter-tests interference.
......
...@@ -35,32 +35,29 @@ def _file( ...@@ -35,32 +35,29 @@ def _file(
} }
class RietveldTest(unittest.TestCase): class BaseFixture(unittest.TestCase):
# Override.
TESTED_CLASS = Exception
def setUp(self): def setUp(self):
super(RietveldTest, self).setUp() super(BaseFixture, self).setUp()
# Access to a protected member XX of a client class # Access to a protected member XX of a client class
# pylint: disable=W0212 # pylint: disable=W0212
self.rietveld = rietveld.Rietveld('url', 'email', 'password') self.rietveld = self.TESTED_CLASS('url', 'email', 'password')
self.rietveld._send = self._rietveld_send self.rietveld._send = self._rietveld_send
self.requests = [] self.requests = []
def tearDown(self): def tearDown(self):
self.assertEquals([], self.requests) self.assertEqual([], self.requests)
super(RietveldTest, self).tearDown() super(BaseFixture, self).tearDown()
def _rietveld_send(self, url, *args, **kwargs): def _rietveld_send(self, url, *args, **kwargs):
self.assertTrue(self.requests, url) self.assertTrue(self.requests, url)
request = self.requests.pop(0) request = self.requests.pop(0)
self.assertEquals(2, len(request)) self.assertEqual(2, len(request))
self.assertEquals(url, request[0]) self.assertEqual(url, request[0])
return request[1] return request[1]
def test_get_patch_empty(self):
self.requests = [('/api/123/456', '{}')]
patches = self.rietveld.get_patch(123, 456)
self.assertTrue(isinstance(patches, patch.PatchSet))
self.assertEquals([], patches.patches)
def _check_patch(self, def _check_patch(self,
p, p,
filename, filename,
...@@ -73,19 +70,29 @@ class RietveldTest(unittest.TestCase): ...@@ -73,19 +70,29 @@ class RietveldTest(unittest.TestCase):
patchlevel=0, patchlevel=0,
svn_properties=None): svn_properties=None):
svn_properties = svn_properties or [] svn_properties = svn_properties or []
self.assertEquals(p.filename, filename) self.assertEqual(p.filename, filename)
self.assertEquals(p.source_filename, source_filename) self.assertEqual(p.source_filename, source_filename)
self.assertEquals(p.is_binary, is_binary) self.assertEqual(p.is_binary, is_binary)
self.assertEquals(p.is_delete, is_delete) self.assertEqual(p.is_delete, is_delete)
if hasattr(p, 'is_git_diff'): if hasattr(p, 'is_git_diff'):
self.assertEquals(p.is_git_diff, is_git_diff) self.assertEqual(p.is_git_diff, is_git_diff)
self.assertEquals(p.is_new, is_new) self.assertEqual(p.is_new, is_new)
if hasattr(p, 'patchlevel'): if hasattr(p, 'patchlevel'):
self.assertEquals(p.patchlevel, patchlevel) self.assertEqual(p.patchlevel, patchlevel)
if diff: if diff:
self.assertEquals(p.get(True), diff) self.assertEqual(p.get(True), diff)
if hasattr(p, 'svn_properties'): if hasattr(p, 'svn_properties'):
self.assertEquals(p.svn_properties, svn_properties) self.assertEqual(p.svn_properties, svn_properties)
class RietveldTest(BaseFixture):
TESTED_CLASS = rietveld.Rietveld
def test_get_patch_empty(self):
self.requests = [('/api/123/456', '{}')]
patches = self.rietveld.get_patch(123, 456)
self.assertTrue(isinstance(patches, patch.PatchSet))
self.assertEqual([], patches.patches)
def test_get_patch_no_status(self): def test_get_patch_no_status(self):
self.requests = [ self.requests = [
...@@ -99,7 +106,7 @@ class RietveldTest(unittest.TestCase): ...@@ -99,7 +106,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_789.diff', RAW.DELETE), ('/download/issue123_456_789.diff', RAW.DELETE),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(1, len(patches.patches)) self.assertEqual(1, len(patches.patches))
self._check_patch( self._check_patch(
patches.patches[0], patches.patches[0],
'tools/clang_check/README.chromium', 'tools/clang_check/README.chromium',
...@@ -114,7 +121,7 @@ class RietveldTest(unittest.TestCase): ...@@ -114,7 +121,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_790.diff', RAW.NEW_NOT_NULL), ('/download/issue123_456_790.diff', RAW.NEW_NOT_NULL),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(2, len(patches.patches)) self.assertEqual(2, len(patches.patches))
self._check_patch( self._check_patch(
patches.patches[0], 'file_a', RAW.NEW_NOT_NULL, is_new=True) patches.patches[0], 'file_a', RAW.NEW_NOT_NULL, is_new=True)
self._check_patch(patches.patches[1], 'foo', RAW.NEW, is_new=True) self._check_patch(patches.patches[1], 'foo', RAW.NEW, is_new=True)
...@@ -125,7 +132,7 @@ class RietveldTest(unittest.TestCase): ...@@ -125,7 +132,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_789.diff', RAW.NEW), ('/download/issue123_456_789.diff', RAW.NEW),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(1, len(patches.patches)) self.assertEqual(1, len(patches.patches))
self._check_patch(patches.patches[0], 'foo', RAW.NEW, is_new=True) self._check_patch(patches.patches[0], 'foo', RAW.NEW, is_new=True)
def test_invalid_status(self): def test_invalid_status(self):
...@@ -136,7 +143,7 @@ class RietveldTest(unittest.TestCase): ...@@ -136,7 +143,7 @@ class RietveldTest(unittest.TestCase):
self.rietveld.get_patch(123, 456) self.rietveld.get_patch(123, 456)
self.fail() self.fail()
except patch.UnsupportedPatchFormat, e: except patch.UnsupportedPatchFormat, e:
self.assertEquals('file_a', e.filename) self.assertEqual('file_a', e.filename)
def test_add_plus_merge(self): def test_add_plus_merge(self):
# svn:mergeinfo is dropped. # svn:mergeinfo is dropped.
...@@ -149,7 +156,7 @@ class RietveldTest(unittest.TestCase): ...@@ -149,7 +156,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_789.diff', GIT.COPY), ('/download/issue123_456_789.diff', GIT.COPY),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(1, len(patches.patches)) self.assertEqual(1, len(patches.patches))
self._check_patch( self._check_patch(
patches.patches[0], patches.patches[0],
'pp', 'pp',
...@@ -167,7 +174,7 @@ class RietveldTest(unittest.TestCase): ...@@ -167,7 +174,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_789.diff', GIT.COPY), ('/download/issue123_456_789.diff', GIT.COPY),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(1, len(patches.patches)) self.assertEqual(1, len(patches.patches))
self._check_patch( self._check_patch(
patches.patches[0], patches.patches[0],
'pp', 'pp',
...@@ -184,7 +191,7 @@ class RietveldTest(unittest.TestCase): ...@@ -184,7 +191,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_789.diff', RAW.CRAP_ONLY), ('/download/issue123_456_789.diff', RAW.CRAP_ONLY),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(1, len(patches.patches)) self.assertEqual(1, len(patches.patches))
self._check_patch( self._check_patch(
patches.patches[0], patches.patches[0],
'__init__.py', '__init__.py',
...@@ -198,7 +205,7 @@ class RietveldTest(unittest.TestCase): ...@@ -198,7 +205,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_789.diff', RAW.DELETE), ('/download/issue123_456_789.diff', RAW.DELETE),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(1, len(patches.patches)) self.assertEqual(1, len(patches.patches))
self._check_patch(patches.patches[0], name, RAW.DELETE, is_delete=True) self._check_patch(patches.patches[0], name, RAW.DELETE, is_delete=True)
def test_delete_empty(self): def test_delete_empty(self):
...@@ -208,7 +215,7 @@ class RietveldTest(unittest.TestCase): ...@@ -208,7 +215,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_789.diff', GIT.DELETE_EMPTY), ('/download/issue123_456_789.diff', GIT.DELETE_EMPTY),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(1, len(patches.patches)) self.assertEqual(1, len(patches.patches))
self._check_patch( self._check_patch(
patches.patches[0], patches.patches[0],
name, name,
...@@ -225,7 +232,7 @@ class RietveldTest(unittest.TestCase): ...@@ -225,7 +232,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_789.diff', RAW.PATCH), ('/download/issue123_456_789.diff', RAW.PATCH),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(1, len(patches.patches)) self.assertEqual(1, len(patches.patches))
self._check_patch( self._check_patch(
patches.patches[0], patches.patches[0],
'chrome/file.cc', 'chrome/file.cc',
...@@ -242,7 +249,7 @@ class RietveldTest(unittest.TestCase): ...@@ -242,7 +249,7 @@ class RietveldTest(unittest.TestCase):
self.rietveld.get_patch(123, 456) self.rietveld.get_patch(123, 456)
self.fail() self.fail()
except patch.UnsupportedPatchFormat, e: except patch.UnsupportedPatchFormat, e:
self.assertEquals('file_a', e.filename) self.assertEqual('file_a', e.filename)
def test_get_patch_moved(self): def test_get_patch_moved(self):
self.requests = [ self.requests = [
...@@ -250,7 +257,7 @@ class RietveldTest(unittest.TestCase): ...@@ -250,7 +257,7 @@ class RietveldTest(unittest.TestCase):
('/download/issue123_456_789.diff', RAW.MINIMAL_RENAME), ('/download/issue123_456_789.diff', RAW.MINIMAL_RENAME),
] ]
patches = self.rietveld.get_patch(123, 456) patches = self.rietveld.get_patch(123, 456)
self.assertEquals(1, len(patches.patches)) self.assertEqual(1, len(patches.patches))
self._check_patch( self._check_patch(
patches.patches[0], patches.patches[0],
'file_b', 'file_b',
...@@ -269,14 +276,14 @@ class RietveldTest(unittest.TestCase): ...@@ -269,14 +276,14 @@ class RietveldTest(unittest.TestCase):
# svn:mergeinfo across branches: # svn:mergeinfo across branches:
# http://codereview.chromium.org/202046/diff/1/third_party/libxml/xmlcatalog_dummy.cc # http://codereview.chromium.org/202046/diff/1/third_party/libxml/xmlcatalog_dummy.cc
self.assertEquals( self.assertEqual(
[('svn:eol-style', 'LF')], [('svn:eol-style', 'LF')],
rietveld.Rietveld.parse_svn_properties( rietveld.Rietveld.parse_svn_properties(
u'\nAdded: svn:eol-style\n + LF\n', 'foo')) u'\nAdded: svn:eol-style\n + LF\n', 'foo'))
# svn:eol-style property that is lost in the diff # svn:eol-style property that is lost in the diff
# http://codereview.chromium.org/202046/diff/1/third_party/libxml/xmllint_dummy.cc # http://codereview.chromium.org/202046/diff/1/third_party/libxml/xmllint_dummy.cc
self.assertEquals( self.assertEqual(
[], [],
rietveld.Rietveld.parse_svn_properties( rietveld.Rietveld.parse_svn_properties(
u'\nAdded: svn:mergeinfo\n' u'\nAdded: svn:mergeinfo\n'
...@@ -284,13 +291,13 @@ class RietveldTest(unittest.TestCase): ...@@ -284,13 +291,13 @@ class RietveldTest(unittest.TestCase):
'libxml/xmldummy_mac.cc:r69-2775\n', 'libxml/xmldummy_mac.cc:r69-2775\n',
'foo')) 'foo'))
self.assertEquals( self.assertEqual(
[], [],
rietveld.Rietveld.parse_svn_properties(u'', 'foo')) rietveld.Rietveld.parse_svn_properties(u'', 'foo'))
# http://codereview.chromium.org/api/7834045/15001 # http://codereview.chromium.org/api/7834045/15001
self.assertEquals( self.assertEqual(
[('svn:executable', '*'), ('svn:eol-style', 'LF')], [('svn:executable', '*'), ('svn:eol-style', 'LF')],
rietveld.Rietveld.parse_svn_properties( rietveld.Rietveld.parse_svn_properties(
'\n' '\n'
...@@ -301,7 +308,7 @@ class RietveldTest(unittest.TestCase): ...@@ -301,7 +308,7 @@ class RietveldTest(unittest.TestCase):
'foo')) 'foo'))
# http://codereview.chromium.org/api/9139006/7001 # http://codereview.chromium.org/api/9139006/7001
self.assertEquals( self.assertEqual(
[('svn:mime-type', 'image/png')], [('svn:mime-type', 'image/png')],
rietveld.Rietveld.parse_svn_properties( rietveld.Rietveld.parse_svn_properties(
'\n' '\n'
...@@ -314,7 +321,7 @@ class RietveldTest(unittest.TestCase): ...@@ -314,7 +321,7 @@ class RietveldTest(unittest.TestCase):
rietveld.Rietveld.parse_svn_properties(u'\n', 'foo') rietveld.Rietveld.parse_svn_properties(u'\n', 'foo')
self.fail() self.fail()
except rietveld.patch.UnsupportedPatchFormat, e: except rietveld.patch.UnsupportedPatchFormat, e:
self.assertEquals('foo', e.filename) self.assertEqual('foo', e.filename)
# TODO(maruel): Change with no diff, only svn property change: # TODO(maruel): Change with no diff, only svn property change:
# http://codereview.chromium.org/6462019/ # http://codereview.chromium.org/6462019/
...@@ -352,7 +359,7 @@ class RietveldTest(unittest.TestCase): ...@@ -352,7 +359,7 @@ class RietveldTest(unittest.TestCase):
True, True,
True, True,
)) ))
self.assertEquals([], results) self.assertEqual([], results)
def test_results_cursor(self): def test_results_cursor(self):
# Verify cursor iteration is transparent. # Verify cursor iteration is transparent.
...@@ -379,8 +386,39 @@ class RietveldTest(unittest.TestCase): ...@@ -379,8 +386,39 @@ class RietveldTest(unittest.TestCase):
{'foo': 'prout'}, {'foo': 'prout'},
] ]
for i in self.rietveld.search(base='base'): for i in self.rietveld.search(base='base'):
self.assertEquals(expected.pop(0), i) self.assertEqual(expected.pop(0), i)
self.assertEquals([], expected) self.assertEqual([], expected)
class CachingRietveldTest(BaseFixture):
# Tests only one request is done.
TESTED_CLASS = rietveld.CachingRietveld
def test_get_description(self):
self.requests = [
('/1/description', 'Blah blah blah'),
]
expected = 'Blah blah blah'
self.assertEqual(expected, self.rietveld.get_description(1))
self.assertEqual(expected, self.rietveld.get_description(1))
def test_get_issue_properties(self):
self.requests = [
('/api/1?messages=true', rietveld.json.dumps({'messages': 'foo'})),
]
expected = {}
expected_msg = {'messages': 'foo'}
self.assertEqual(expected, self.rietveld.get_issue_properties(1, False))
self.assertEqual(expected_msg, self.rietveld.get_issue_properties(1, True))
def test_get_patchset_properties(self):
self.requests = [
('/api/1/2', '{}'),
]
expected = {}
self.assertEqual(expected, self.rietveld.get_patchset_properties(1, 2))
self.assertEqual(expected, self.rietveld.get_patchset_properties(1, 2))
if __name__ == '__main__': if __name__ == '__main__':
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment