PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/product/HBTreeFolder2/tests/testHBTreeFolder2.py

https://gitlab.com/kirr/erp5
Python | 352 lines | 305 code | 22 blank | 25 comment | 15 complexity | 8f0645cc264361370bcfb0834c4ff78b MD5 | raw file
  1. ##############################################################################
  2. #
  3. # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
  4. # All Rights Reserved.
  5. #
  6. # This software is subject to the provisions of the Zope Public License,
  7. # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
  8. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  9. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  10. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  11. # FOR A PARTICULAR PURPOSE
  12. #
  13. ##############################################################################
  14. import random
  15. import unittest
  16. from Testing import ZopeTestCase
  17. from Products.HBTreeFolder2.HBTreeFolder2 \
  18. import HBTreeFolder2, ExhaustedUniqueIdsError
  19. from OFS.ObjectManager import BadRequestException
  20. from OFS.Folder import Folder
  21. from Acquisition import aq_base
  22. import timeit
  23. from textwrap import dedent
  24. from unittest import expectedFailure
  25. from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
  26. from Products.PythonScripts.PythonScript import PythonScript
  27. class HBTreeFolder2Tests(ERP5TypeTestCase):
  28. def getBase(self, ob):
  29. # This is overridden in subclasses.
  30. return aq_base(ob)
  31. def setUp(self):
  32. self.f = HBTreeFolder2('root')
  33. ff = HBTreeFolder2('item')
  34. self.f._setOb(ff.id, ff)
  35. self.ff = self.f._getOb(ff.id)
  36. def testAdded(self):
  37. self.assertEqual(self.ff.id, 'item')
  38. def testCount(self):
  39. self.assertEqual(self.f.objectCount(), 1)
  40. self.assertEqual(self.ff.objectCount(), 0)
  41. self.assertEqual(len(self.f), 1)
  42. self.assertEqual(len(self.ff), 0)
  43. def testObjectIds(self):
  44. self.assertEqual(list(self.f.objectIds()), ['item'])
  45. self.assertEqual(list(self.f.keys()), ['item'])
  46. self.assertEqual(list(self.ff.objectIds()), [])
  47. f3 = HBTreeFolder2('item3')
  48. self.f._setOb(f3.id, f3)
  49. lst = list(self.f.objectIds())
  50. lst.sort()
  51. self.assertEqual(lst, ['item', 'item3'])
  52. def testObjectValues(self):
  53. values = self.f.objectValues()
  54. self.assertEqual(len(values), 1)
  55. self.assertEqual(values[0].id, 'item')
  56. # Make sure the object is wrapped.
  57. self.assert_(values[0] is not self.getBase(values[0]))
  58. def testObjectItems(self):
  59. items = self.f.objectItems()
  60. self.assertEqual(len(items), 1)
  61. id, val = items[0]
  62. self.assertEqual(id, 'item')
  63. self.assertEqual(val.id, 'item')
  64. # Make sure the object is wrapped.
  65. self.assert_(val is not self.getBase(val))
  66. def testHasKey(self):
  67. self.assert_(self.f.hasObject('item')) # Old spelling
  68. self.assert_(self.f.has_key('item')) # New spelling
  69. def testDelete(self):
  70. self.f._delOb('item')
  71. self.assertEqual(list(self.f.objectIds()), [])
  72. self.assertEqual(self.f.objectCount(), 0)
  73. def testObjectIds_d(self):
  74. self.assertEqual(self.f.objectIds_d(), {'item': 1})
  75. def testCheckId(self):
  76. self.assertEqual(self.f._checkId('xyz'), None)
  77. self.assertRaises(BadRequestException, self.f._checkId, 'item')
  78. self.assertRaises(BadRequestException, self.f._checkId, 'REQUEST')
  79. def testSetObject(self):
  80. f2 = HBTreeFolder2('item2')
  81. self.f._setObject(f2.id, f2)
  82. self.assert_(self.f.has_key('item2'))
  83. self.assertEqual(self.f.objectCount(), 2)
  84. def testWrapped(self):
  85. # Verify that the folder returns wrapped versions of objects.
  86. base = self.getBase(self.f._getOb('item'))
  87. self.assert_(self.f._getOb('item') is not base)
  88. self.assert_(self.f['item'] is not base)
  89. self.assert_(self.f.get('item') is not base)
  90. self.assert_(self.getBase(self.f._getOb('item')) is base)
  91. def testGenerateId(self):
  92. ids = {}
  93. for n in range(10):
  94. ids[self.f.generateId()] = 1
  95. self.assertEqual(len(ids), 10) # All unique
  96. for id in ids.keys():
  97. self.f._checkId(id) # Must all be valid
  98. def testGenerateIdDenialOfServicePrevention(self):
  99. for n in range(10):
  100. item = Folder()
  101. item.id = 'item%d' % n
  102. self.f._setOb(item.id, item)
  103. self.f.generateId('item', rand_ceiling=20) # Shouldn't be a problem
  104. self.assertRaises(ExhaustedUniqueIdsError,
  105. self.f.generateId, 'item', rand_ceiling=9)
  106. def testReplace(self):
  107. old_f = Folder()
  108. old_f.id = 'item'
  109. inner_f = HBTreeFolder2('inner')
  110. old_f._setObject(inner_f.id, inner_f)
  111. self.ff._populateFromFolder(old_f)
  112. self.assertEqual(self.ff.objectCount(), 1)
  113. self.assert_(self.ff.has_key('inner'))
  114. self.assertEqual(self.getBase(self.ff._getOb('inner')), inner_f)
  115. def testObjectListing(self):
  116. f2 = HBTreeFolder2('somefolder')
  117. self.f._setObject(f2.id, f2)
  118. # Hack in an absolute_url() method that works without context.
  119. self.f.absolute_url = str
  120. info = self.f.getBatchObjectListing()
  121. self.assertEqual(info['b_start'], 1)
  122. self.assertEqual(info['b_end'], 2)
  123. self.assertEqual(info['prev_batch_url'], '')
  124. self.assertEqual(info['next_batch_url'], '')
  125. self.assert_(info['formatted_list'].find('</select>') > 0)
  126. self.assert_(info['formatted_list'].find('item') > 0)
  127. self.assert_(info['formatted_list'].find('somefolder') > 0)
  128. # Ensure batching is working.
  129. info = self.f.getBatchObjectListing({'b_count': 1})
  130. self.assertEqual(info['b_start'], 1)
  131. self.assertEqual(info['b_end'], 1)
  132. self.assertEqual(info['prev_batch_url'], '')
  133. self.assert_(info['next_batch_url'] != '')
  134. self.assert_(info['formatted_list'].find('item') > 0)
  135. self.assert_(info['formatted_list'].find('somefolder') < 0)
  136. info = self.f.getBatchObjectListing({'b_start': 2})
  137. self.assertEqual(info['b_start'], 2)
  138. self.assertEqual(info['b_end'], 2)
  139. self.assert_(info['prev_batch_url'] != '')
  140. self.assertEqual(info['next_batch_url'], '')
  141. self.assert_(info['formatted_list'].find('item') < 0)
  142. self.assert_(info['formatted_list'].find('somefolder') > 0)
  143. def testObjectListingWithSpaces(self):
  144. # The option list must use value attributes to preserve spaces.
  145. name = " some folder "
  146. f2 = HBTreeFolder2(name)
  147. self.f._setObject(f2.id, f2)
  148. self.f.absolute_url = str
  149. info = self.f.getBatchObjectListing()
  150. expect = '<option value="%s">%s</option>' % (name, name)
  151. self.assert_(info['formatted_list'].find(expect) > 0)
  152. def testCleanup(self):
  153. self.assert_(self.f._cleanup())
  154. key = TrojanKey('a')
  155. self.f._htree[key] = 'b'
  156. self.assert_(self.f._cleanup())
  157. key.value = 'z'
  158. # With a key in the wrong place, there should now be damage.
  159. self.assert_(not self.f._cleanup())
  160. # Now it's fixed.
  161. self.assert_(self.f._cleanup())
  162. # Verify the management interface also works,
  163. # but don't test return values.
  164. self.f.manage_cleanup()
  165. key.value = 'a'
  166. self.f.manage_cleanup()
  167. def testIterItems(self):
  168. h = HBTreeFolder2()
  169. id_list = ['a-b', 'a-cd',
  170. 'b',
  171. 'd-a-b', 'd-c-d', 'd-f-a',
  172. 'e-cd', 'e-f',
  173. 'f']
  174. for i in id_list:
  175. h._setOb(i, tuple(i))
  176. while 1:
  177. for min in (None, 'a', 'a-a', 'a-b',
  178. 'a-b-a', 'a-c', 'a-cd',
  179. 'a-cde', 'a-d', 'aa', 'b',
  180. 'b-c-d', 'c', 'd'):
  181. if min:
  182. m = min.split('-')
  183. for i, id in enumerate(id_list):
  184. if m <= id.split('-'):
  185. break
  186. else:
  187. i += 1
  188. else:
  189. i = 0
  190. expected = [(i, tuple(i)) for i in id_list[i:]]
  191. self.assertEqual(expected, list(h._htree_iteritems(min)))
  192. if not id_list:
  193. break
  194. i = random.choice(id_list)
  195. id_list.remove(i)
  196. h._delOb(i)
  197. def testRestrictedIteration(self):
  198. """
  199. Check content iterators can be used by restricted python code.
  200. """
  201. # To let restricted python access methods on folder
  202. marker = object()
  203. saved_class_attributes = {}
  204. for method_id in ('objectIds', 'objectValues', 'objectItems'):
  205. roles_id = method_id + '__roles__'
  206. saved_class_attributes[roles_id] = getattr(HBTreeFolder2, roles_id,
  207. marker)
  208. setattr(HBTreeFolder2, roles_id, None)
  209. try:
  210. h = HBTreeFolder2()
  211. # whatever value, as long as it has an __of__
  212. h._setOb('foo', HBTreeFolder2())
  213. script = PythonScript('script')
  214. script.ZPythonScript_edit('h', dedent("""
  215. for dummy in h.objectIds():
  216. pass
  217. for dummy in h.objectValues():
  218. pass
  219. for dummy in h.objectItems():
  220. pass
  221. """))
  222. class DummyRequest(object):
  223. # To make Shared.DC.Scripts.Bindings.Bindings._getTraverseSubpath
  224. # happy
  225. other = {}
  226. script.REQUEST = DummyRequest
  227. script(h)
  228. finally:
  229. for roles_id, orig in saved_class_attributes.iteritems():
  230. if orig is marker:
  231. delattr(HBTreeFolder2, roles_id)
  232. else:
  233. setattr(HBTreeFolder2, roles_id, orig)
  234. @expectedFailure
  235. def _testPerformanceInDepth(self):
  236. """
  237. Check HBTreeFolder2 GET performance with the depth and the number of
  238. documents.
  239. """
  240. init_1 = dedent("""
  241. from Products.HBTreeFolder2.HBTreeFolder2 import HBTreeFolder2
  242. from Products.CMFDefault.File import File
  243. h_depth_1 = HBTreeFolder2()
  244. for i in xrange(%d):
  245. id = str(i)
  246. h_depth_1[id] = File(id)
  247. """)
  248. init_2 = dedent("""
  249. from Products.HBTreeFolder2.HBTreeFolder2 import HBTreeFolder2
  250. from Products.CMFDefault.File import File
  251. h_depth_2 = HBTreeFolder2()
  252. for i in xrange(10):
  253. for j in xrange(%d / 10):
  254. id = "-".join(map(str, (i,j)))
  255. h_depth_2[id] = File(id)
  256. """)
  257. init_3 = dedent("""
  258. from Products.HBTreeFolder2.HBTreeFolder2 import HBTreeFolder2
  259. from Products.CMFDefault.File import File
  260. h_depth_3 = HBTreeFolder2()
  261. for i in xrange(10):
  262. for j in xrange(10):
  263. for k in xrange(%d / (10 * 10)):
  264. id = "-".join(map(str, (i, j, k)))
  265. h_depth_3[id] = File(id)
  266. """)
  267. N = 1000
  268. # measure 100 times of each test with timeit()
  269. t1 = timeit.Timer("h_depth_1['555']", init_1 % N).timeit(100)
  270. t2 = timeit.Timer("h_depth_2['5-55']", init_2 % N).timeit(100)
  271. t3 = timeit.Timer("h_depth_3['5-5-5']", init_3 % N).timeit(100)
  272. ZopeTestCase._print("\nN = 1000\n")
  273. ZopeTestCase._print("L1=%s\tL2=%s\tL3=%s" % (t1, t2, t3))
  274. N = 10000 # The N is 10 times larger than the previous measurement
  275. t2_1 = timeit.Timer("h_depth_1['5555']", init_1 % N).timeit(100)
  276. t2_2 = timeit.Timer("h_depth_2['5-555']", init_2 % N).timeit(100)
  277. t2_3 = timeit.Timer("h_depth_3['5-5-55']", init_3 % N).timeit(100)
  278. ZopeTestCase._print("\nN = 10000\n")
  279. ZopeTestCase._print("L1'=%s\tL2'=%s\tL3'=%s" % (t2_1, t2_2, t2_3))
  280. N = 100000 # The N is 10 times larger than the pevious measurement
  281. t3_1 = timeit.Timer("h_depth_1['22222']", init_1 % N).timeit(100)
  282. t3_2 = timeit.Timer("h_depth_2['2-2222']", init_2 % N).timeit(100)
  283. t3_3 = timeit.Timer("h_depth_3['2-2-222']", init_3 % N).timeit(100)
  284. ZopeTestCase._print("\nN = 100000\n")
  285. ZopeTestCase._print("L1''=%s\tL2''=%s\tL3''=%s" % (t3_1, t3_2, t3_3))
  286. # These assert are should be True, but right now those are not passed
  287. # assert L2 is faster than L1, because the leaves are fewer than L1
  288. self.assertTrue(t1 > t2)
  289. self.assertTrue(t2_1 > t2_2)
  290. self.assertTrue(t3_1 > t3_2)
  291. # assert L3 is faster than L2, because the leaves are fewer than L1
  292. self.assertTrue(t2 > t3)
  293. self.assertTrue(t2_2 > t2_3)
  294. self.assertTrue(t3_2 > t3_3)
  295. class TrojanKey:
  296. """Pretends to be a consistent, immutable, humble citizen...
  297. then sweeps the rug out from under the HBTree.
  298. """
  299. def __init__(self, value):
  300. self.value = value
  301. def __cmp__(self, other):
  302. return cmp(self.value, other)
  303. def __hash__(self):
  304. return hash(self.value)
  305. def test_suite():
  306. return unittest.TestSuite((
  307. unittest.makeSuite(HBTreeFolder2Tests),
  308. ))
  309. if __name__ == '__main__':
  310. unittest.main(defaultTest='test_suite')