/tests/src/python/test_qgsmaprenderercache.py

https://github.com/ricardogsilva/Quantum-GIS · Python · 318 lines · 216 code · 47 blank · 55 comment · 4 complexity · 362b893d81195c4234d1ed035cab7608 MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. """QGIS Unit tests for QgsMapRendererCache.
  3. .. note:: This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version.
  7. """
  8. __author__ = 'Nyall Dawson'
  9. __date__ = '1/02/2017'
  10. __copyright__ = 'Copyright 2017, The QGIS Project'
  11. import qgis # NOQA
  12. from qgis.core import (QgsMapRendererCache,
  13. QgsRectangle,
  14. QgsVectorLayer,
  15. QgsProject,
  16. QgsMapToPixel)
  17. from qgis.testing import start_app, unittest
  18. from qgis.PyQt.QtCore import QCoreApplication
  19. from qgis.PyQt.QtGui import QImage
  20. from time import sleep
  21. start_app()
  22. class TestQgsMapRendererCache(unittest.TestCase):
  23. def testSetCacheImages(self):
  24. cache = QgsMapRendererCache()
  25. # not set image
  26. im = cache.cacheImage('littlehands')
  27. self.assertTrue(im.isNull())
  28. self.assertFalse(cache.hasCacheImage('littlehands'))
  29. # set image
  30. im = QImage(200, 200, QImage.Format_RGB32)
  31. cache.setCacheImage('littlehands', im)
  32. self.assertFalse(im.isNull())
  33. self.assertEqual(cache.cacheImage('littlehands'), im)
  34. self.assertTrue(cache.hasCacheImage('littlehands'))
  35. # test another not set image when cache has images
  36. self.assertTrue(cache.cacheImage('bad').isNull())
  37. self.assertFalse(cache.hasCacheImage('bad'))
  38. # clear cache image
  39. cache.clearCacheImage('not in cache') # no crash!
  40. cache.clearCacheImage('littlehands')
  41. im = cache.cacheImage('littlehands')
  42. self.assertTrue(im.isNull())
  43. self.assertFalse(cache.hasCacheImage('littlehands'))
  44. # clear whole cache
  45. im = QImage(200, 200, QImage.Format_RGB32)
  46. cache.setCacheImage('littlehands', im)
  47. self.assertFalse(im.isNull())
  48. self.assertTrue(cache.hasCacheImage('littlehands'))
  49. cache.clear()
  50. im = cache.cacheImage('littlehands')
  51. self.assertTrue(im.isNull())
  52. self.assertFalse(cache.hasCacheImage('littlehands'))
  53. def testInit(self):
  54. cache = QgsMapRendererCache()
  55. extent = QgsRectangle(1, 2, 3, 4)
  56. self.assertFalse(cache.init(extent, 1000))
  57. # add a cache image
  58. im = QImage(200, 200, QImage.Format_RGB32)
  59. cache.setCacheImage('layer', im)
  60. self.assertFalse(cache.cacheImage('layer').isNull())
  61. self.assertTrue(cache.hasCacheImage('layer'))
  62. # re init, without changing extent or scale
  63. self.assertTrue(cache.init(extent, 1000))
  64. # image should still be in cache
  65. self.assertFalse(cache.cacheImage('layer').isNull())
  66. self.assertTrue(cache.hasCacheImage('layer'))
  67. # reinit with different scale
  68. self.assertFalse(cache.init(extent, 2000))
  69. # cache should be cleared
  70. self.assertTrue(cache.cacheImage('layer').isNull())
  71. self.assertFalse(cache.hasCacheImage('layer'))
  72. # readd image to cache
  73. cache.setCacheImage('layer', im)
  74. self.assertFalse(cache.cacheImage('layer').isNull())
  75. self.assertTrue(cache.hasCacheImage('layer'))
  76. # change extent
  77. self.assertFalse(cache.init(QgsRectangle(11, 12, 13, 14), 2000))
  78. # cache should be cleared
  79. self.assertTrue(cache.cacheImage('layer').isNull())
  80. self.assertFalse(cache.hasCacheImage('layer'))
  81. def testRequestRepaintSimple(self):
  82. """ test requesting repaint with a single dependent layer """
  83. layer = QgsVectorLayer("Point?field=fldtxt:string",
  84. "layer", "memory")
  85. QgsProject.instance().addMapLayers([layer])
  86. self.assertTrue(layer.isValid())
  87. # add image to cache
  88. cache = QgsMapRendererCache()
  89. im = QImage(200, 200, QImage.Format_RGB32)
  90. cache.setCacheImage('xxx', im, [layer])
  91. self.assertFalse(cache.cacheImage('xxx').isNull())
  92. self.assertTrue(cache.hasCacheImage('xxx'))
  93. # trigger repaint on layer
  94. layer.triggerRepaint()
  95. # cache image should be cleared
  96. self.assertTrue(cache.cacheImage('xxx').isNull())
  97. self.assertFalse(cache.hasCacheImage('xxx'))
  98. QgsProject.instance().removeMapLayer(layer.id())
  99. # test that cache is also cleared on deferred update
  100. layer = QgsVectorLayer("Point?field=fldtxt:string",
  101. "layer", "memory")
  102. cache.setCacheImage('xxx', im, [layer])
  103. layer.triggerRepaint(True)
  104. self.assertFalse(cache.hasCacheImage('xxx'))
  105. def testInvalidateCacheForLayer(self):
  106. """ test invalidating the cache for a layer """
  107. layer = QgsVectorLayer("Point?field=fldtxt:string",
  108. "layer", "memory")
  109. QgsProject.instance().addMapLayers([layer])
  110. self.assertTrue(layer.isValid())
  111. # add image to cache
  112. cache = QgsMapRendererCache()
  113. im = QImage(200, 200, QImage.Format_RGB32)
  114. cache.setCacheImage('xxx', im, [layer])
  115. self.assertFalse(cache.cacheImage('xxx').isNull())
  116. self.assertTrue(cache.hasCacheImage('xxx'))
  117. # invalidate cache for layer
  118. cache.invalidateCacheForLayer(layer)
  119. # cache image should be cleared
  120. self.assertTrue(cache.cacheImage('xxx').isNull())
  121. self.assertFalse(cache.hasCacheImage('xxx'))
  122. QgsProject.instance().removeMapLayer(layer.id())
  123. def testRequestRepaintMultiple(self):
  124. """ test requesting repaint with multiple dependent layers """
  125. layer1 = QgsVectorLayer("Point?field=fldtxt:string",
  126. "layer1", "memory")
  127. layer2 = QgsVectorLayer("Point?field=fldtxt:string",
  128. "layer2", "memory")
  129. QgsProject.instance().addMapLayers([layer1, layer2])
  130. self.assertTrue(layer1.isValid())
  131. self.assertTrue(layer2.isValid())
  132. # add image to cache - no dependent layers
  133. cache = QgsMapRendererCache()
  134. im1 = QImage(200, 200, QImage.Format_RGB32)
  135. cache.setCacheImage('nolayer', im1)
  136. self.assertFalse(cache.cacheImage('nolayer').isNull())
  137. self.assertTrue(cache.hasCacheImage('nolayer'))
  138. # trigger repaint on layer
  139. layer1.triggerRepaint()
  140. layer1.triggerRepaint() # do this a couple of times - we don't want errors due to multiple disconnects, etc
  141. layer2.triggerRepaint()
  142. layer2.triggerRepaint()
  143. # cache image should still exist - it's not dependent on layers
  144. self.assertFalse(cache.cacheImage('nolayer').isNull())
  145. self.assertTrue(cache.hasCacheImage('nolayer'))
  146. # image depends on 1 layer
  147. im_l1 = QImage(200, 200, QImage.Format_RGB32)
  148. cache.setCacheImage('im1', im_l1, [layer1])
  149. # image depends on 2 layers
  150. im_l1_l2 = QImage(200, 200, QImage.Format_RGB32)
  151. cache.setCacheImage('im1_im2', im_l1_l2, [layer1, layer2])
  152. # image depends on 2nd layer alone
  153. im_l2 = QImage(200, 200, QImage.Format_RGB32)
  154. cache.setCacheImage('im2', im_l2, [layer2])
  155. self.assertFalse(cache.cacheImage('im1').isNull())
  156. self.assertTrue(cache.hasCacheImage('im1'))
  157. self.assertFalse(cache.cacheImage('im1_im2').isNull())
  158. self.assertTrue(cache.hasCacheImage('im1_im2'))
  159. self.assertFalse(cache.cacheImage('im2').isNull())
  160. self.assertTrue(cache.hasCacheImage('im2'))
  161. # trigger repaint layer 1 (check twice - don't want disconnect errors)
  162. for i in range(2):
  163. layer1.triggerRepaint()
  164. # should be cleared
  165. self.assertTrue(cache.cacheImage('im1').isNull())
  166. self.assertFalse(cache.hasCacheImage('im1'))
  167. self.assertTrue(cache.cacheImage('im1_im2').isNull())
  168. self.assertFalse(cache.hasCacheImage('im1_im2'))
  169. # should be retained
  170. self.assertTrue(cache.hasCacheImage('im2'))
  171. self.assertFalse(cache.cacheImage('im2').isNull())
  172. self.assertEqual(cache.cacheImage('im2'), im_l2)
  173. self.assertTrue(cache.hasCacheImage('nolayer'))
  174. self.assertFalse(cache.cacheImage('nolayer').isNull())
  175. self.assertEqual(cache.cacheImage('nolayer'), im1)
  176. # trigger repaint layer 2
  177. for i in range(2):
  178. layer2.triggerRepaint()
  179. # should be cleared
  180. self.assertFalse(cache.hasCacheImage('im1'))
  181. self.assertTrue(cache.cacheImage('im1').isNull())
  182. self.assertFalse(cache.hasCacheImage('im1_im2'))
  183. self.assertTrue(cache.cacheImage('im1_im2').isNull())
  184. self.assertFalse(cache.hasCacheImage('im2'))
  185. self.assertTrue(cache.cacheImage('im2').isNull())
  186. # should be retained
  187. self.assertTrue(cache.hasCacheImage('nolayer'))
  188. self.assertFalse(cache.cacheImage('nolayer').isNull())
  189. self.assertEqual(cache.cacheImage('nolayer'), im1)
  190. def testDependentLayers(self):
  191. # bad layer tests
  192. cache = QgsMapRendererCache()
  193. self.assertEqual(cache.dependentLayers('not a layer'), [])
  194. layer1 = QgsVectorLayer("Point?field=fldtxt:string",
  195. "layer1", "memory")
  196. layer2 = QgsVectorLayer("Point?field=fldtxt:string",
  197. "layer2", "memory")
  198. im = QImage(200, 200, QImage.Format_RGB32)
  199. cache.setCacheImage('no depends', im, [])
  200. self.assertEqual(cache.dependentLayers('no depends'), [])
  201. cache.setCacheImage('depends', im, [layer1, layer2])
  202. self.assertEqual(set(cache.dependentLayers('depends')), set([layer1, layer2]))
  203. def testLayerRemoval(self):
  204. """test that cached image is cleared when a dependent layer is removed"""
  205. cache = QgsMapRendererCache()
  206. layer1 = QgsVectorLayer("Point?field=fldtxt:string",
  207. "layer1", "memory")
  208. layer2 = QgsVectorLayer("Point?field=fldtxt:string",
  209. "layer2", "memory")
  210. im = QImage(200, 200, QImage.Format_RGB32)
  211. cache.setCacheImage('depends', im, [layer1, layer2])
  212. cache.setCacheImage('depends2', im, [layer1])
  213. cache.setCacheImage('depends3', im, [layer2])
  214. cache.setCacheImage('no depends', im, [])
  215. self.assertTrue(cache.hasCacheImage('depends'))
  216. self.assertTrue(cache.hasCacheImage('depends2'))
  217. self.assertTrue(cache.hasCacheImage('depends3'))
  218. self.assertTrue(cache.hasCacheImage('no depends'))
  219. # try deleting a layer
  220. layer2 = None
  221. self.assertFalse(cache.hasCacheImage('depends'))
  222. self.assertTrue(cache.hasCacheImage('depends2'))
  223. self.assertFalse(cache.hasCacheImage('depends3'))
  224. self.assertTrue(cache.hasCacheImage('no depends'))
  225. layer1 = None
  226. self.assertFalse(cache.hasCacheImage('depends'))
  227. self.assertFalse(cache.hasCacheImage('depends2'))
  228. self.assertFalse(cache.hasCacheImage('depends3'))
  229. self.assertTrue(cache.hasCacheImage('no depends'))
  230. def testClearOnLayerAutoRefresh(self):
  231. """ test that cache is cleared when layer auto refresh is triggered """
  232. cache = QgsMapRendererCache()
  233. layer1 = QgsVectorLayer("Point?field=fldtxt:string",
  234. "layer1", "memory")
  235. im = QImage(200, 200, QImage.Format_RGB32)
  236. cache.setCacheImage('l1', im, [layer1])
  237. self.assertTrue(cache.hasCacheImage('l1'))
  238. layer1.setAutoRefreshInterval(100)
  239. layer1.setAutoRefreshEnabled(True)
  240. self.assertTrue(cache.hasCacheImage('l1'))
  241. # wait a second...
  242. sleep(1)
  243. for i in range(100):
  244. QCoreApplication.processEvents()
  245. # cache should be cleared
  246. self.assertFalse(cache.hasCacheImage('l1'))
  247. def testSetCacheImageDifferentParams(self):
  248. """
  249. Test setting cache image with different parameters
  250. """
  251. cache = QgsMapRendererCache()
  252. cache.updateParameters(QgsRectangle(1, 1, 3, 3), QgsMapToPixel(5))
  253. im = QImage(200, 200, QImage.Format_RGB32)
  254. cache.setCacheImage('im1', im, [])
  255. self.assertEqual(cache.cacheImage('im1').width(), 200)
  256. # if existing cached image exists with matching parameters, we don't store a new image -- old
  257. # one should still be retained
  258. im = QImage(201, 201, QImage.Format_RGB32)
  259. cache.setCacheImageWithParameters('im1', im, QgsRectangle(1, 1, 3, 4), QgsMapToPixel(5), [])
  260. self.assertEqual(cache.cacheImage('im1').width(), 200)
  261. cache.setCacheImageWithParameters('im1', im, QgsRectangle(1, 1, 3, 3), QgsMapToPixel(6), [])
  262. self.assertEqual(cache.cacheImage('im1').width(), 200)
  263. # replace with matching parameters
  264. cache.setCacheImageWithParameters('im1', im, QgsRectangle(1, 1, 3, 3), QgsMapToPixel(5), [])
  265. self.assertEqual(cache.cacheImage('im1').width(), 201)
  266. im = QImage(202, 202, QImage.Format_RGB32)
  267. cache.setCacheImage('im1', im, [])
  268. self.assertEqual(cache.cacheImage('im1').width(), 202)
  269. if __name__ == '__main__':
  270. unittest.main()