test_func.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import pytest
  2. import multiprocessing
  3. import random
  4. import threading
  5. import time
  6. import library.python.func as func
  7. def test_map0():
  8. assert None is func.map0(lambda x: x + 1, None)
  9. assert 3 == func.map0(lambda x: x + 1, 2)
  10. assert None is func.map0(len, None)
  11. assert 2 == func.map0(len, [1, 2])
  12. def test_single():
  13. assert 1 == func.single([1])
  14. with pytest.raises(Exception):
  15. assert 1 == func.single([])
  16. with pytest.raises(Exception):
  17. assert 1 == func.single([1, 2])
  18. def test_memoize():
  19. class Counter(object):
  20. @staticmethod
  21. def inc():
  22. Counter._qty = getattr(Counter, '_qty', 0) + 1
  23. return Counter._qty
  24. @func.memoize()
  25. def t1(a):
  26. return a, Counter.inc()
  27. @func.memoize()
  28. def t2(a):
  29. return a, Counter.inc()
  30. @func.memoize()
  31. def t3(a):
  32. return a, Counter.inc()
  33. @func.memoize()
  34. def t4(a):
  35. return a, Counter.inc()
  36. @func.memoize()
  37. def t5(a, b, c):
  38. return a + b + c, Counter.inc()
  39. @func.memoize()
  40. def t6():
  41. return Counter.inc()
  42. @func.memoize(limit=2)
  43. def t7(a, _b):
  44. return a, Counter.inc()
  45. assert (1, 1) == t1(1)
  46. assert (1, 1) == t1(1)
  47. assert (2, 2) == t1(2)
  48. assert (2, 2) == t1(2)
  49. assert (1, 3) == t2(1)
  50. assert (1, 3) == t2(1)
  51. assert (2, 4) == t2(2)
  52. assert (2, 4) == t2(2)
  53. assert (1, 5) == t3(1)
  54. assert (1, 5) == t3(1)
  55. assert (2, 6) == t3(2)
  56. assert (2, 6) == t3(2)
  57. assert (1, 7) == t4(1)
  58. assert (1, 7) == t4(1)
  59. assert (2, 8) == t4(2)
  60. assert (2, 8) == t4(2)
  61. assert (6, 9) == t5(1, 2, 3)
  62. assert (6, 9) == t5(1, 2, 3)
  63. assert (7, 10) == t5(1, 2, 4)
  64. assert (7, 10) == t5(1, 2, 4)
  65. assert 11 == t6()
  66. assert 11 == t6()
  67. assert (1, 12) == t7(1, None)
  68. assert (2, 13) == t7(2, None)
  69. assert (1, 12) == t7(1, None)
  70. assert (2, 13) == t7(2, None)
  71. # removed result for (1, None)
  72. assert (3, 14) == t7(3, None)
  73. assert (1, 15) == t7(1, None)
  74. class ClassWithMemoizedMethod(object):
  75. def __init__(self):
  76. self.a = 0
  77. @func.memoize(True)
  78. def t(self, i):
  79. self.a += i
  80. return i
  81. obj = ClassWithMemoizedMethod()
  82. assert 10 == obj.t(10)
  83. assert 10 == obj.a
  84. assert 10 == obj.t(10)
  85. assert 10 == obj.a
  86. assert 20 == obj.t(20)
  87. assert 30 == obj.a
  88. assert 20 == obj.t(20)
  89. assert 30 == obj.a
  90. def test_first():
  91. assert func.first([0, [], (), None, False, {}, 0.0, '1', 0]) == '1'
  92. assert func.first([]) is None
  93. assert func.first([0]) is None
  94. def test_split():
  95. assert func.split([1, 1], lambda x: x) == ([1, 1], [])
  96. assert func.split([0, 0], lambda x: x) == ([], [0, 0])
  97. assert func.split([], lambda x: x) == ([], [])
  98. assert func.split([1, 0, 1], lambda x: x) == ([1, 1], [0])
  99. def test_flatten_dict():
  100. assert func.flatten_dict({"a": 1, "b": 2}) == {"a": 1, "b": 2}
  101. assert func.flatten_dict({"a": 1}) == {"a": 1}
  102. assert func.flatten_dict({}) == {}
  103. assert func.flatten_dict({"a": 1, "b": {"c": {"d": 2}}}) == {"a": 1, "b.c.d": 2}
  104. assert func.flatten_dict({"a": 1, "b": {"c": {"d": 2}}}, separator="/") == {"a": 1, "b/c/d": 2}
  105. def test_memoize_thread_local():
  106. class Counter(object):
  107. def __init__(self, s):
  108. self.val = s
  109. def inc(self):
  110. self.val += 1
  111. return self.val
  112. @func.memoize(thread_local=True)
  113. def get_counter(start):
  114. return Counter(start)
  115. def th_inc():
  116. assert get_counter(0).inc() == 1
  117. assert get_counter(0).inc() == 2
  118. assert get_counter(10).inc() == 11
  119. assert get_counter(10).inc() == 12
  120. th_inc()
  121. th = threading.Thread(target=th_inc)
  122. th.start()
  123. th.join()
  124. def test_memoize_not_thread_safe():
  125. class Counter(object):
  126. def __init__(self, s):
  127. self.val = s
  128. def inc(self):
  129. self.val += 1
  130. return self.val
  131. @func.memoize(thread_safe=False)
  132. def io_job(n):
  133. time.sleep(0.1)
  134. return Counter(n)
  135. def worker(n):
  136. assert io_job(n).inc() == n + 1
  137. assert io_job(n).inc() == n + 2
  138. assert io_job(n*10).inc() == n*10 + 1
  139. assert io_job(n*10).inc() == n*10 + 2
  140. assert io_job(n).inc() == n + 3
  141. threads = []
  142. for i in range(5):
  143. threads.append(threading.Thread(target=worker, args=(i+1,)))
  144. st = time.time()
  145. for thread in threads:
  146. thread.start()
  147. for thread in threads:
  148. thread.join()
  149. elapsed_time = time.time() - st
  150. assert elapsed_time < 0.5
  151. def test_memoize_not_thread_safe_concurrent():
  152. class Counter(object):
  153. def __init__(self, s):
  154. self.val = s
  155. def inc(self):
  156. self.val += 1
  157. return self.val
  158. @func.memoize(thread_safe=False)
  159. def io_job(n):
  160. time.sleep(0.1)
  161. return Counter(n)
  162. def worker():
  163. io_job(100).inc()
  164. th1 = threading.Thread(target=worker)
  165. th2 = threading.Thread(target=worker)
  166. th3 = threading.Thread(target=worker)
  167. th1.start()
  168. time.sleep(0.05)
  169. th2.start()
  170. th1.join()
  171. assert io_job(100).inc() == 100 + 2
  172. th3.start()
  173. # th3 instantly got counter from memory
  174. assert io_job(100).inc() == 100 + 4
  175. th2.join()
  176. # th2 shoud increase th1 counter
  177. assert io_job(100).inc() == 100 + 6
  178. def test_memoize_not_thread_safe_stress():
  179. @func.memoize(thread_safe=False)
  180. def job():
  181. for _ in range(1000):
  182. hash = random.getrandbits(128)
  183. return hash
  184. def worker(n):
  185. hash = job()
  186. results[n] = hash
  187. num_threads = min(multiprocessing.cpu_count()*4, 64)
  188. threads = []
  189. results = [None for _ in range(num_threads)]
  190. for i in range(num_threads):
  191. thread = threading.Thread(target=worker, args=(i,))
  192. threads.append(thread)
  193. for thread in threads:
  194. thread.start()
  195. for thread in threads:
  196. thread.join()
  197. assert len(set(results)) == 1
  198. def test_memoize_thread_safe():
  199. class Counter(object):
  200. def __init__(self, s):
  201. self.val = s
  202. def inc(self):
  203. self.val += 1
  204. return self.val
  205. @func.memoize(thread_safe=True)
  206. def io_job(n):
  207. time.sleep(0.05)
  208. return Counter(n)
  209. def worker(n):
  210. assert io_job(n).inc() == n + 1
  211. assert io_job(n).inc() == n + 2
  212. assert io_job(n*10).inc() == n*10 + 1
  213. assert io_job(n*10).inc() == n*10 + 2
  214. threads = []
  215. for i in range(5):
  216. threads.append(threading.Thread(target=worker, args=(i+1,)))
  217. st = time.time()
  218. for thread in threads:
  219. thread.start()
  220. for thread in threads:
  221. thread.join()
  222. elapsed_time = time.time() - st
  223. assert elapsed_time >= 0.5
  224. if __name__ == '__main__':
  225. pytest.main([__file__])