scrap_test.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. import os
  2. import sys
  3. if os.environ.get('SDL_VIDEODRIVER') == 'dummy':
  4. __tags__ = ('ignore', 'subprocess_ignore')
  5. import unittest
  6. from pygame.tests.test_utils import trunk_relative_path
  7. import pygame
  8. from pygame import scrap
  9. from pygame.compat import as_bytes
  10. class ScrapModuleTest(unittest.TestCase):
  11. @classmethod
  12. def setUpClass(cls):
  13. pygame.display.init()
  14. pygame.display.set_mode((1, 1))
  15. scrap.init()
  16. @classmethod
  17. def tearDownClass(cls):
  18. # scrap.quit() # Does not exist!
  19. pygame.display.quit()
  20. def test_init(self):
  21. """Ensures scrap module still initialized after multiple init calls."""
  22. scrap.init()
  23. scrap.init()
  24. self.assertTrue(scrap.get_init())
  25. def test_get_init(self):
  26. """Ensures get_init gets the init state."""
  27. self.assertTrue(scrap.get_init())
  28. def todo_test_contains(self):
  29. """Ensures contains works as expected."""
  30. self.fail()
  31. def todo_test_get(self):
  32. """Ensures get works as expected."""
  33. self.fail()
  34. def test_get__owned_empty_type(self):
  35. """Ensures get works when there is no data of the requested type
  36. in the clipboard and the clipboard is owned by the pygame application.
  37. """
  38. # Use a unique data type identifier to ensure there is no preexisting
  39. # data.
  40. DATA_TYPE = 'test_get__owned_empty_type'
  41. if scrap.lost():
  42. # Try to acquire the clipboard.
  43. scrap.put(pygame.SCRAP_TEXT, b'text to clipboard')
  44. if scrap.lost():
  45. self.skipTest('requires the pygame application to own the '
  46. 'clipboard')
  47. data = scrap.get(DATA_TYPE)
  48. self.assertIsNone(data)
  49. def todo_test_get_types(self):
  50. """Ensures get_types works as expected."""
  51. self.fail()
  52. def todo_test_lost(self):
  53. """Ensures lost works as expected."""
  54. self.fail()
  55. def test_set_mode(self):
  56. """Ensures set_mode works as expected."""
  57. scrap.set_mode(pygame.SCRAP_SELECTION)
  58. scrap.set_mode(pygame.SCRAP_CLIPBOARD)
  59. self.assertRaises(ValueError, scrap.set_mode, 1099)
  60. def test_put__text(self):
  61. """Ensures put can place text into the clipboard."""
  62. scrap.put(pygame.SCRAP_TEXT, as_bytes("Hello world"))
  63. self.assertEqual(scrap.get(pygame.SCRAP_TEXT), as_bytes("Hello world"))
  64. scrap.put(pygame.SCRAP_TEXT, as_bytes("Another String"))
  65. self.assertEqual(scrap.get(pygame.SCRAP_TEXT),
  66. as_bytes("Another String"))
  67. @unittest.skipIf('pygame.image' not in sys.modules,
  68. 'requires pygame.image module')
  69. def test_put__bmp_image(self):
  70. """Ensures put can place a BMP image into the clipboard."""
  71. sf = pygame.image.load(trunk_relative_path(
  72. "examples/data/asprite.bmp"))
  73. expected_string = pygame.image.tostring(sf, "RGBA")
  74. scrap.put(pygame.SCRAP_BMP, expected_string)
  75. self.assertEqual(scrap.get(pygame.SCRAP_BMP), expected_string)
  76. def test_put(self):
  77. """Ensures put can place data into the clipboard
  78. when using a user defined type identifier.
  79. """
  80. DATA_TYPE = 'arbitrary buffer'
  81. scrap.put(DATA_TYPE, as_bytes('buf'))
  82. r = scrap.get(DATA_TYPE)
  83. self.assertEqual(r, as_bytes('buf'))
  84. class ScrapModuleClipboardNotOwnedTest(unittest.TestCase):
  85. """Test the scrap module's functionality when the pygame application is
  86. not the current owner of the clipboard.
  87. A separate class is used to prevent tests that acquire the clipboard from
  88. interfering with these tests.
  89. """
  90. @classmethod
  91. def setUpClass(cls):
  92. pygame.display.init()
  93. pygame.display.set_mode((1, 1))
  94. scrap.init()
  95. @classmethod
  96. def tearDownClass(cls):
  97. # scrap.quit() # Does not exist!
  98. pygame.quit()
  99. pygame.display.quit()
  100. def _skip_if_clipboard_owned(self):
  101. # Skip test if the pygame application owns the clipboard. Currently,
  102. # there is no way to give up ownership.
  103. if not scrap.lost():
  104. self.skipTest('requires the pygame application to not own the '
  105. 'clipboard')
  106. def test_get__not_owned(self):
  107. """Ensures get works when there is no data of the requested type
  108. in the clipboard and the clipboard is not owned by the pygame
  109. application.
  110. """
  111. self._skip_if_clipboard_owned()
  112. # Use a unique data type identifier to ensure there is no preexisting
  113. # data.
  114. DATA_TYPE = 'test_get__not_owned'
  115. data = scrap.get(DATA_TYPE)
  116. self.assertIsNone(data)
  117. def test_get_types__not_owned(self):
  118. """Ensures get_types works when the clipboard is not owned
  119. by the pygame application.
  120. """
  121. self._skip_if_clipboard_owned()
  122. data_types = scrap.get_types()
  123. self.assertIsInstance(data_types, list)
  124. def test_contains__not_owned(self):
  125. """Ensures contains works when the clipboard is not owned
  126. by the pygame application.
  127. """
  128. self._skip_if_clipboard_owned()
  129. # Use a unique data type identifier to ensure there is no preexisting
  130. # data.
  131. DATA_TYPE = 'test_contains__not_owned'
  132. contains = scrap.contains(DATA_TYPE)
  133. self.assertFalse(contains)
  134. def test_lost__not_owned(self):
  135. """Ensures lost works when the clipboard is not owned
  136. by the pygame application.
  137. """
  138. self._skip_if_clipboard_owned()
  139. lost = scrap.lost()
  140. self.assertTrue(lost)
  141. class X11InteractiveTest(unittest.TestCase):
  142. __tags__ = ['ignore', 'subprocess_ignore']
  143. try:
  144. pygame.display.init()
  145. except Exception:
  146. pass
  147. else:
  148. if pygame.display.get_driver() == 'x11':
  149. __tags__ = ['interactive']
  150. pygame.display.quit()
  151. def test_issue_208(self):
  152. """PATCH: pygame.scrap on X11, fix copying into PRIMARY selection
  153. Copying into theX11 PRIMARY selection (mouse copy/paste) would not
  154. work due to a confusion between content type and clipboard type.
  155. """
  156. from pygame import display, event, freetype
  157. from pygame.locals import SCRAP_SELECTION, SCRAP_TEXT
  158. from pygame.locals import KEYDOWN, K_y, QUIT
  159. success = False
  160. freetype.init()
  161. font = freetype.Font(None, 24)
  162. display.init()
  163. display.set_caption("Interactive X11 Paste Test")
  164. screen = display.set_mode((600, 200))
  165. screen.fill(pygame.Color('white'))
  166. text = "Scrap put() succeeded."
  167. msg = ('Some text has been placed into the X11 clipboard.'
  168. ' Please click the center mouse button in an open'
  169. ' text window to retrieve it.'
  170. '\n\nDid you get "{}"? (y/n)').format(text)
  171. word_wrap(screen, msg, font, 6)
  172. display.flip()
  173. event.pump()
  174. scrap.init()
  175. scrap.set_mode(SCRAP_SELECTION)
  176. scrap.put(SCRAP_TEXT, text.encode('UTF-8'))
  177. while True:
  178. e = event.wait()
  179. if e.type == QUIT:
  180. break
  181. if e.type == KEYDOWN:
  182. success = (e.key == K_y)
  183. break
  184. pygame.display.quit()
  185. self.assertTrue(success)
  186. def word_wrap(surf, text, font, margin=0, color=(0, 0, 0)):
  187. font.origin = True
  188. surf_width, surf_height = surf.get_size()
  189. width = surf_width - 2 * margin
  190. height = surf_height - 2 * margin
  191. line_spacing = int(1.25 * font.get_sized_height())
  192. x, y = margin, margin + line_spacing
  193. space = font.get_rect(' ')
  194. for word in iwords(text):
  195. if word == '\n':
  196. x, y = margin, y + line_spacing
  197. else:
  198. bounds = font.get_rect(word)
  199. if x + bounds.width + bounds.x >= width:
  200. x, y = margin, y + line_spacing
  201. if x + bounds.width + bounds.x >= width:
  202. raise ValueError("word too wide for the surface")
  203. if y + bounds.height - bounds.y >= height:
  204. raise ValueError("text to long for the surface")
  205. font.render_to(surf, (x, y), None, color)
  206. x += bounds.width + space.width
  207. return x, y
  208. def iwords(text):
  209. # r"\n|[^ ]+"
  210. #
  211. head = 0
  212. tail = head
  213. end = len(text)
  214. while head < end:
  215. if text[head] == ' ':
  216. head += 1
  217. tail = head + 1
  218. elif text[head] == '\n':
  219. head += 1
  220. yield '\n'
  221. tail = head + 1
  222. elif tail == end:
  223. yield text[head:]
  224. head = end
  225. elif text[tail] == '\n':
  226. yield text[head:tail]
  227. head = tail
  228. elif text[tail] == ' ':
  229. yield text[head:tail]
  230. head = tail
  231. else:
  232. tail += 1
  233. if __name__ == '__main__':
  234. unittest.main()