Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: appengine/findit/libs/test/cache_decorator_test.py

Issue 2644543006: [Culprit-Finder] Add generator cache decorator. (Closed)
Patch Set: Rebase and fix nits. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import mock
5 import pickle 6 import pickle
7 import unittest
6 import zlib 8 import zlib
7 9
8 from testing_utils import testing
9
10 from libs import cache 10 from libs import cache
11 from libs import cache_decorator 11 from libs import cache_decorator
12 12
13 13
14 class _DummyCache(cache.Cache): 14 class _DummyCache(cache.Cache):
15 def __init__(self, cached_data): 15 def __init__(self, cached_data):
16 self.cached_data = cached_data 16 self.cached_data = cached_data
17 17
18 def Get(self, key): 18 def Get(self, key):
19 return self.cached_data.get(key) 19 return self.cached_data.get(key)
20 20
21 def Set(self, key, data, expire_time=0): 21 def Set(self, key, data, expire_time=0):
22 self.cached_data[key] = data 22 self.cached_data[key] = data
23 23
24 24
25 def _DummyKeyGenerator(func, *_): 25 def _DummyKeyGenerator(func, args, kwargs, # pylint: disable=W0613
26 return func.__name__ 26 namespace=None):
27 namespace = namespace or '%s.%s' % (func.__module__, func.__name__)
28 return namespace + '-' + func.__name__
27 29
28 30
29 class CacheDecoratorTest(testing.AppengineTestCase): 31 class CachedTest(unittest.TestCase):
32 """Tests ``Cached`` decorator."""
33
30 def testDefaultKeyGenerator(self): 34 def testDefaultKeyGenerator(self):
35 namespace = 'n'
31 expected_params = { 36 expected_params = {
32 'id1': 'fi', 37 'id1': 'fi',
33 'id2': 'pi', 38 'id2': 'pi',
34 'url': 'http://url', 39 'url': 'http://url',
35 } 40 }
36 # Hexadecimal digits of MD5 digest of "pickled_params". 41 # Hexadecimal digits of MD5 digest of "pickled_params".
37 expected_key = 'f5f173c811f7c537a80d44511903a3e0' 42 expected_key = namespace + '-f5f173c811f7c537a80d44511903a3e0'
38 43
39 def MockPickleDumps(params): 44 def MockPickleDumps(params):
40 self.assertEqual(expected_params, params) 45 self.assertEqual(expected_params, params)
41 return 'pickled_params' 46 return 'pickled_params'
42 47
43 def Func(id1, id2, url=None): # Unused parameters-pylint: disable=W0613 48 def func(id1, id2, url=None): # Unused parameters-pylint: disable=W0613
44 return 1 # pragma: no cover. 49 return 1 # pragma: no cover.
45 50
46 class CallableIdentifier(object): 51 class CallableIdentifier(object):
47 def identifier(self): 52 def identifier(self):
48 return 'fi' 53 return 'fi'
49 54
50 class PropertyIdentifier(object): 55 class PropertyIdentifier(object):
51 @property 56 @property
52 def identifier(self): 57 def identifier(self):
53 return 'pi' 58 return 'pi'
54 59
55 self.mock(pickle, 'dumps', MockPickleDumps) 60 with mock.patch('pickle.dumps', MockPickleDumps):
56 61 args = (CallableIdentifier(), PropertyIdentifier())
57 args = (CallableIdentifier(), PropertyIdentifier()) 62 kwargs = {'url': 'http://url'}
58 kwargs = {'url': 'http://url'} 63 key = cache_decorator._DefaultKeyGenerator(
59 key = cache_decorator._DefaultKeyGenerator(Func, args, kwargs) 64 func, args, kwargs, namespace=namespace)
60 self.assertEqual(expected_key, key) 65 self.assertEqual(expected_key, key)
61 66
62 def testCachedDecoratorWhenResultIsAlreadyCached(self): 67 def testCachedDecoratorWhenResultIsAlreadyCached(self):
63 dummy_cache = _DummyCache({'n-Func': 1}) 68 dummy_cache = _DummyCache({'n-func': 1})
64 69
65 @cache_decorator.Cached( 70 @cache_decorator.Cached(
66 namespace='n', key_generator=_DummyKeyGenerator, cache=dummy_cache) 71 namespace='n', key_generator=_DummyKeyGenerator, cache=dummy_cache)
67 def Func(): 72 def func():
68 return 2 # pragma: no cover. 73 return 2 # pragma: no cover.
69 74
70 self.assertEqual(1, Func()) 75 self.assertEqual(1, func())
71 self.assertEqual({'n-Func': 1}, dummy_cache.cached_data) 76 self.assertEqual({'n-func': 1}, dummy_cache.cached_data)
72 77
73 def testCachedDecoratorWhenResultIsNotCachedYet(self): 78 def testCachedDecoratorWhenResultIsNotCachedYet(self):
74 dummy_cache = _DummyCache({}) 79 dummy_cache = _DummyCache({})
75 80
76 @cache_decorator.Cached( 81 @cache_decorator.Cached(
77 namespace='n', key_generator=_DummyKeyGenerator, cache=dummy_cache) 82 namespace='n', key_generator=_DummyKeyGenerator, cache=dummy_cache)
78 def Func(): 83 def func():
79 return 2 84 return 2
80 85
81 self.assertEqual(2, Func()) 86 self.assertEqual(2, func())
82 self.assertEqual({'n-Func': 2}, dummy_cache.cached_data) 87 self.assertEqual({'n-func': 2}, dummy_cache.cached_data)
83 88
84 def testCachedDecoratorWhenResultShouldNotBeCached(self): 89 def testCachedDecoratorWhenResultShouldNotBeCached(self):
85 dummy_cache = _DummyCache({}) 90 dummy_cache = _DummyCache({})
86 91
87 results = [None, 0, [], {}, ''] 92 results = [None, 0, [], {}, '']
88 93
89 @cache_decorator.Cached( 94 @cache_decorator.Cached(
90 namespace='n', key_generator=_DummyKeyGenerator, cache=dummy_cache) 95 namespace='n', key_generator=_DummyKeyGenerator, cache=dummy_cache)
91 def Func(): 96 def func():
92 return results.pop() 97 return results.pop()
93 98
94 self.assertEqual('', Func()) 99 self.assertEqual('', func())
95 self.assertEqual({}, dummy_cache.cached_data) 100 self.assertEqual({}, dummy_cache.cached_data)
96 self.assertEqual({}, Func()) 101 self.assertEqual({}, func())
97 self.assertEqual({}, dummy_cache.cached_data) 102 self.assertEqual({}, dummy_cache.cached_data)
98 self.assertEqual([], Func()) 103 self.assertEqual([], func())
99 self.assertEqual({}, dummy_cache.cached_data) 104 self.assertEqual({}, dummy_cache.cached_data)
100 self.assertEqual(0, Func()) 105 self.assertEqual(0, func())
101 self.assertEqual({}, dummy_cache.cached_data) 106 self.assertEqual({}, dummy_cache.cached_data)
102 self.assertIsNone(Func()) 107 self.assertIsNone(func())
103 self.assertEqual({}, dummy_cache.cached_data) 108 self.assertEqual({}, dummy_cache.cached_data)
104 109
105 def testCachedDecoratorWithMethodInAClass(self): 110 def testCachedDecoratorWithMethodInAClass(self):
106 class A(object): 111 class A(object):
107 def __init__(self, url, retries): 112 def __init__(self, url, retries):
108 self.url = url 113 self.url = url
109 self.retries = retries 114 self.retries = retries
110 self.runs = 0 115 self.runs = 0
111 116
112 @property 117 @property
113 def identifier(self): 118 def identifier(self):
114 return self.url 119 return self.url
115 120
116 @cache_decorator.Cached(cache=_DummyCache({})) 121 @cache_decorator.Cached(cache=_DummyCache({}))
117 def Func(self, path): 122 def func(self, path):
118 self.runs += 1 123 self.runs += 1
119 return self.url + '/' + path 124 return self.url + '/' + path
120 125
121 a1 = A('http://test', 3) 126 a1 = A('http://test', 3)
122 self.assertEqual('http://test/p1', a1.Func('p1')) 127 self.assertEqual('http://test/p1', a1.func('p1'))
123 self.assertEqual('http://test/p1', a1.Func('p1')) 128 self.assertEqual('http://test/p1', a1.func('p1'))
124 self.assertEqual(1, a1.runs) 129 self.assertEqual(1, a1.runs)
125 130
126 a2 = A('http://test', 5) 131 a2 = A('http://test', 5)
127 self.assertEqual('http://test/p1', a2.Func('p1')) 132 self.assertEqual('http://test/p1', a2.func('p1'))
128 self.assertEqual(0, a2.runs) 133 self.assertEqual(0, a2.runs)
134
135
136 class GeneratorCachedTest(unittest.TestCase):
137 """Tests ``GeneratorCached`` decorator."""
138
139 def testWhenResultIsAlreadyCached(self):
140 value_list = [0, 1, 2]
141 key = 'n-func'
142 # Zero value won't be cached.
143 cached_keys = ['%s-%d' % (key, i) for i in xrange(len(value_list))]
144 cached_data = {key: value for key, value in zip(cached_keys, value_list)}
145 cached_data[key] = cached_keys
146
147 dummy_cache = _DummyCache(cached_data)
148
149 @cache_decorator.GeneratorCached(
150 dummy_cache, namespace='n', key_generator=_DummyKeyGenerator)
151 def func(): # pragma: no cover
152 for value in value_list:
153 yield value
154
155 for value, expected_value in zip(func(), value_list):
156 self.assertEqual(value, expected_value)
157
158 def testWhenResultIsNotCachedYet(self):
159 value_list = [0, 1, 2, 3, 4]
160 dummy_cache = _DummyCache({})
161
162 @cache_decorator.GeneratorCached(
163 dummy_cache, namespace='n', key_generator=_DummyKeyGenerator)
164 def func(): # pragma: no cover
165 for value in value_list:
166 yield value
167
168 key = _DummyKeyGenerator(func, [], {}, namespace='n')
169 cached_keys = ['%s-%d' % (key, i) for i in xrange(len(value_list))]
170 cached_data = {key: value for key, value in zip(cached_keys, value_list)}
171 cached_data[key] = cached_keys
172 for value, expected_value in zip(func(), value_list):
173 self.assertEqual(value, expected_value)
174
175 self.assertDictEqual(dummy_cache.cached_data, cached_data)
176
177 @mock.patch('libs.cache_decorator.GeneratorCached.SetCache',
178 lambda *_: False)
179 def testFailedToSetValue(self):
180 value_list = [0, 1, 2, 3, 4]
181 dummy_cache = _DummyCache({})
182
183 @cache_decorator.GeneratorCached(
184 dummy_cache, namespace='n', key_generator=_DummyKeyGenerator)
185 def func(): # pragma: no cover
186 for value in value_list:
187 yield value
188
189 key = _DummyKeyGenerator(func, [], {}, namespace='n')
190 cached_keys = ['%s-%d' % (key, i) for i in xrange(len(value_list))]
191 cached_data = {key: value for key, value in zip(cached_keys, value_list)}
192 cached_data[key] = cached_keys
193 for value, expected_value in zip(func(), value_list):
194 self.assertEqual(value, expected_value)
195
196 self.assertDictEqual(dummy_cache.cached_data, {})
OLDNEW
« no previous file with comments | « appengine/findit/libs/cache_decorator.py ('k') | appengine/findit/util_scripts/crash_queries/crash_iterator.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698