blob: e668bce29ede8dceac28df91df6bff14bd3bfd12 [file] [log] [blame]
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import mock
import pickle
import unittest
import zlib
from libs import cache
from libs import cache_decorator
class _DummyCache(cache.Cache):
def __init__(self, cached_data):
self.cached_data = cached_data
def Get(self, key):
return self.cached_data.get(key)
def Set(self, key, data, expire_time=0):
self.cached_data[key] = data
def _DummyKeyGenerator(func, _args, _kwargs, namespace=None):
namespace = namespace or '%s.%s' % (func.__module__, func.__name__)
return namespace + '-' + func.__name__
class CachedTest(unittest.TestCase):
"""Tests ``Cached`` decorator."""
def testDefaultKeyGenerator(self):
namespace = 'n'
expected_params = {
'id1': 'fi',
'id2': 'pi',
'url': 'http://url',
}
# Hexadecimal digits of MD5 digest of "pickled_params".
expected_key = namespace + '-f5f173c811f7c537a80d44511903a3e0'
def MockPickleDumps(params):
self.assertEqual(expected_params, params)
return 'pickled_params'
def func(id1, id2, url=None): # Unused parameters-pylint: disable=W0613
return 1 # pragma: no cover.
class CallableIdentifier(object):
def identifier(self):
return 'fi'
class PropertyIdentifier(object):
@property
def identifier(self):
return 'pi'
with mock.patch('pickle.dumps', MockPickleDumps):
args = (CallableIdentifier(), PropertyIdentifier())
kwargs = {'url': 'http://url'}
key = cache_decorator._DefaultKeyGenerator(
func, args, kwargs, namespace=namespace)
self.assertEqual(expected_key, key)
def testCachedDecoratorWhenResultIsAlreadyCached(self):
dummy_cache = _DummyCache({'n-func': 1})
@cache_decorator.Cached(
namespace='n', key_generator=_DummyKeyGenerator, cache=dummy_cache)
def func():
return 2 # pragma: no cover.
self.assertEqual(1, func())
self.assertEqual({'n-func': 1}, dummy_cache.cached_data)
def testCachedDecoratorWhenResultIsNotCachedYet(self):
dummy_cache = _DummyCache({})
@cache_decorator.Cached(
namespace='n', key_generator=_DummyKeyGenerator, cache=dummy_cache)
def func():
return 2
self.assertEqual(2, func())
self.assertEqual({'n-func': 2}, dummy_cache.cached_data)
def testCachedDecoratorWhenResultShouldNotBeCached(self):
dummy_cache = _DummyCache({})
results = ['invalid', None, 0, [], {}, '']
def ResultValidator(result):
return result != 'invalid'
@cache_decorator.Cached(
namespace='n',
key_generator=_DummyKeyGenerator,
cache=dummy_cache,
result_validator=ResultValidator)
def func():
return results.pop()
self.assertEqual('', func())
self.assertEqual({}, dummy_cache.cached_data)
self.assertEqual({}, func())
self.assertEqual({}, dummy_cache.cached_data)
self.assertEqual([], func())
self.assertEqual({}, dummy_cache.cached_data)
self.assertEqual(0, func())
self.assertEqual({}, dummy_cache.cached_data)
self.assertIsNone(func())
self.assertEqual({}, dummy_cache.cached_data)
self.assertEqual('invalid', func())
self.assertEqual({}, dummy_cache.cached_data)
def testCachedDecoratorWithMethodInAClass(self):
class A(object):
def __init__(self, url, retries):
self.url = url
self.retries = retries
self.runs = 0
@property
def identifier(self):
return self.url
@cache_decorator.Cached(cache=_DummyCache({}))
def func(self, path):
self.runs += 1
return self.url + '/' + path
a1 = A('http://test', 3)
self.assertEqual('http://test/p1', a1.func('p1'))
self.assertEqual('http://test/p1', a1.func('p1'))
self.assertEqual(1, a1.runs)
a2 = A('http://test', 5)
self.assertEqual('http://test/p1', a2.func('p1'))
self.assertEqual(0, a2.runs)
class GeneratorCachedTest(unittest.TestCase):
"""Tests ``GeneratorCached`` decorator."""
def testWhenResultIsAlreadyCached(self):
value_list = [0, 1, 2]
key = 'n-func'
# Zero value won't be cached.
cached_keys = ['%s-%d' % (key, i) for i in xrange(len(value_list))]
cached_data = {key: value for key, value in zip(cached_keys, value_list)}
cached_data[key] = cached_keys
dummy_cache = _DummyCache(cached_data)
@cache_decorator.GeneratorCached(
dummy_cache, namespace='n', key_generator=_DummyKeyGenerator)
def func(): # pragma: no cover
for value in value_list:
yield value
for value, expected_value in zip(func(), value_list):
self.assertEqual(value, expected_value)
def testWhenResultIsNotCachedYet(self):
value_list = [0, 1, 2, 3, 4]
dummy_cache = _DummyCache({})
@cache_decorator.GeneratorCached(
dummy_cache, namespace='n', key_generator=_DummyKeyGenerator)
def func(): # pragma: no cover
for value in value_list:
yield value
key = _DummyKeyGenerator(func, [], {}, namespace='n')
cached_keys = ['%s-%d' % (key, i) for i in xrange(len(value_list))]
cached_data = {key: value for key, value in zip(cached_keys, value_list)}
cached_data[key] = cached_keys
for value, expected_value in zip(func(), value_list):
self.assertEqual(value, expected_value)
self.assertDictEqual(dummy_cache.cached_data, cached_data)
@mock.patch('libs.cache_decorator.GeneratorCached.SetCache', lambda *_: False)
def testFailedToSetValue(self):
value_list = [0, 1, 2, 3, 4]
dummy_cache = _DummyCache({})
@cache_decorator.GeneratorCached(
dummy_cache, namespace='n', key_generator=_DummyKeyGenerator)
def func(): # pragma: no cover
for value in value_list:
yield value
key = _DummyKeyGenerator(func, [], {}, namespace='n')
cached_keys = ['%s-%d' % (key, i) for i in xrange(len(value_list))]
cached_data = {key: value for key, value in zip(cached_keys, value_list)}
cached_data[key] = cached_keys
for value, expected_value in zip(func(), value_list):
self.assertEqual(value, expected_value)
self.assertDictEqual(dummy_cache.cached_data, {})