test__base.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015, 2016 OpenMarket Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from tests import unittest
  16. from twisted.internet import defer
  17. from synapse.util.async import ObservableDeferred
  18. from synapse.util.caches.descriptors import Cache, cached
  19. class CacheTestCase(unittest.TestCase):
  20. def setUp(self):
  21. self.cache = Cache("test")
  22. def test_empty(self):
  23. failed = False
  24. try:
  25. self.cache.get("foo")
  26. except KeyError:
  27. failed = True
  28. self.assertTrue(failed)
  29. def test_hit(self):
  30. self.cache.prefill("foo", 123)
  31. self.assertEquals(self.cache.get("foo"), 123)
  32. def test_invalidate(self):
  33. self.cache.prefill(("foo",), 123)
  34. self.cache.invalidate(("foo",))
  35. failed = False
  36. try:
  37. self.cache.get(("foo",))
  38. except KeyError:
  39. failed = True
  40. self.assertTrue(failed)
  41. def test_eviction(self):
  42. cache = Cache("test", max_entries=2)
  43. cache.prefill(1, "one")
  44. cache.prefill(2, "two")
  45. cache.prefill(3, "three") # 1 will be evicted
  46. failed = False
  47. try:
  48. cache.get(1)
  49. except KeyError:
  50. failed = True
  51. self.assertTrue(failed)
  52. cache.get(2)
  53. cache.get(3)
  54. def test_eviction_lru(self):
  55. cache = Cache("test", max_entries=2, lru=True)
  56. cache.prefill(1, "one")
  57. cache.prefill(2, "two")
  58. # Now access 1 again, thus causing 2 to be least-recently used
  59. cache.get(1)
  60. cache.prefill(3, "three")
  61. failed = False
  62. try:
  63. cache.get(2)
  64. except KeyError:
  65. failed = True
  66. self.assertTrue(failed)
  67. cache.get(1)
  68. cache.get(3)
  69. class CacheDecoratorTestCase(unittest.TestCase):
  70. @defer.inlineCallbacks
  71. def test_passthrough(self):
  72. class A(object):
  73. @cached()
  74. def func(self, key):
  75. return key
  76. a = A()
  77. self.assertEquals((yield a.func("foo")), "foo")
  78. self.assertEquals((yield a.func("bar")), "bar")
  79. @defer.inlineCallbacks
  80. def test_hit(self):
  81. callcount = [0]
  82. class A(object):
  83. @cached()
  84. def func(self, key):
  85. callcount[0] += 1
  86. return key
  87. a = A()
  88. yield a.func("foo")
  89. self.assertEquals(callcount[0], 1)
  90. self.assertEquals((yield a.func("foo")), "foo")
  91. self.assertEquals(callcount[0], 1)
  92. @defer.inlineCallbacks
  93. def test_invalidate(self):
  94. callcount = [0]
  95. class A(object):
  96. @cached()
  97. def func(self, key):
  98. callcount[0] += 1
  99. return key
  100. a = A()
  101. yield a.func("foo")
  102. self.assertEquals(callcount[0], 1)
  103. a.func.invalidate(("foo",))
  104. yield a.func("foo")
  105. self.assertEquals(callcount[0], 2)
  106. def test_invalidate_missing(self):
  107. class A(object):
  108. @cached()
  109. def func(self, key):
  110. return key
  111. A().func.invalidate(("what",))
  112. @defer.inlineCallbacks
  113. def test_max_entries(self):
  114. callcount = [0]
  115. class A(object):
  116. @cached(max_entries=10)
  117. def func(self, key):
  118. callcount[0] += 1
  119. return key
  120. a = A()
  121. for k in range(0, 12):
  122. yield a.func(k)
  123. self.assertEquals(callcount[0], 12)
  124. # There must have been at least 2 evictions, meaning if we calculate
  125. # all 12 values again, we must get called at least 2 more times
  126. for k in range(0, 12):
  127. yield a.func(k)
  128. self.assertTrue(
  129. callcount[0] >= 14,
  130. msg="Expected callcount >= 14, got %d" % (callcount[0])
  131. )
  132. def test_prefill(self):
  133. callcount = [0]
  134. d = defer.succeed(123)
  135. class A(object):
  136. @cached()
  137. def func(self, key):
  138. callcount[0] += 1
  139. return d
  140. a = A()
  141. a.func.prefill(("foo",), ObservableDeferred(d))
  142. self.assertEquals(a.func("foo").result, d.result)
  143. self.assertEquals(callcount[0], 0)