Advertisement
asweigart

Untitled

Jun 3rd, 2019
256
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.13 KB | None | 0 0
  1. from __future__ import division, print_function
  2.  
  3. import pytest
  4. import unittest
  5. import sys
  6. import os
  7. import time
  8. import threading
  9. from collections import namedtuple # Added in Python 2.6.
  10.  
  11.  
  12. sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
  13. import pyautogui
  14.  
  15. runningOnPython2 = sys.version_info[0] == 2
  16.  
  17. if runningOnPython2:
  18. INPUT_FUNC = raw_input
  19. else:
  20. INPUT_FUNC = input
  21.  
  22.  
  23. try:
  24. import pytweening
  25. except:
  26. assert False, 'The PyTweening module must be installed to complete the tests: pip install pytweening'
  27.  
  28. try:
  29. import pyscreeze
  30. except:
  31. assert False, 'The PyScreeze module must be installed to complete the tests: pip install pyscreeze'
  32.  
  33. try:
  34. import pygetwindow
  35. except:
  36. assert False, 'The PyGetWindow module must be installed to complete the tests: pip install pygetwindow'
  37.  
  38. # TODO - note that currently most of the click-related functionality is not tested.
  39.  
  40.  
  41. class P(namedtuple('P', ['x', 'y'])):
  42. '''Simple, immutable, 2D point/vector class, including some basic
  43. arithmetic functions.
  44. '''
  45. def __str__(self):
  46. return '{0},{1}'.format(self.x, self.y)
  47.  
  48. def __repr__(self):
  49. return 'P({0}, {1})'.format(self.x, self.y)
  50.  
  51. def __eq__(self, other):
  52. return self.x == other.x and self.y == other.y
  53.  
  54. def __ne__(self, other):
  55. return self.x != other.x and self.y != other.y
  56.  
  57. def __add__(self, other):
  58. return P(self.x + other.x, self.y + other.y)
  59.  
  60. def __sub__(self, other):
  61. return P(self.x - other.x, self.y - other.y)
  62.  
  63. def __mul__(self, other):
  64. return P(self.x * other, self.y * other)
  65.  
  66. def __rmul__(self, other):
  67. return self.__mul__(other)
  68.  
  69. def __floordiv__(self, other):
  70. return P(self.x // other, self.y // other)
  71.  
  72. def __truediv__(self, other):
  73. return P(self.x / other, self.y / other)
  74.  
  75. def __neg__(self):
  76. return P(-self.x, -self.y)
  77.  
  78. def __pos__(self):
  79. return self
  80.  
  81. def __neg__(self):
  82. return P(abs(self.x), abs(self.y))
  83.  
  84.  
  85. class TestGeneral(unittest.TestCase):
  86. def setUp(self):
  87. self.oldFailsafeSetting = pyautogui.FAILSAFE
  88. pyautogui.FAILSAFE = False
  89. pyautogui.moveTo(42, 42) # make sure failsafe isn't triggered during this test
  90. pyautogui.FAILSAFE = True
  91.  
  92.  
  93. def tearDown(self):
  94. pyautogui.FAILSAFE = self.oldFailsafeSetting
  95.  
  96.  
  97. def test_accessibleNames(self):
  98. # Check that all the functions are defined.
  99.  
  100. # mouse-related API
  101. pyautogui.moveTo
  102. pyautogui.moveRel
  103. pyautogui.dragTo
  104. pyautogui.dragRel
  105. pyautogui.mouseDown
  106. pyautogui.mouseUp
  107. pyautogui.click
  108. pyautogui.rightClick
  109. pyautogui.doubleClick
  110. pyautogui.tripleClick
  111.  
  112. # keyboard-related API
  113. pyautogui.typewrite
  114. pyautogui.hotkey
  115. pyautogui.keyDown
  116. pyautogui.keyUp
  117. pyautogui.press
  118.  
  119. # The functions implemented in the platform-specific modules should also show up in the pyautogui namespace:
  120. pyautogui.position
  121. pyautogui.size
  122. pyautogui.scroll
  123. pyautogui.hscroll
  124. pyautogui.vscroll
  125.  
  126. # util API
  127. pyautogui.KEYBOARD_KEYS
  128. pyautogui.isShiftCharacter
  129.  
  130. # Screenshot-related API
  131. pyautogui.locateAll
  132. pyautogui.locate
  133. pyautogui.locateOnScreen
  134. pyautogui.locateAllOnScreen
  135. pyautogui.locateCenterOnScreen
  136. pyautogui.center
  137. pyautogui.pixelMatchesColor
  138. pyautogui.pixel
  139. pyautogui.screenshot
  140. pyautogui.grab
  141.  
  142. # TODO(denilsonsa): I believe we should get rid of these symbols. If someone wants tweening, import pytweening module instead!
  143. # Tweening-related API
  144. pyautogui.getPointOnLine
  145. pyautogui.linear
  146. pyautogui.easeInQuad
  147. pyautogui.easeOutQuad
  148. pyautogui.easeInOutQuad
  149. pyautogui.easeInCubic
  150. pyautogui.easeOutCubic
  151. pyautogui.easeInOutCubic
  152. pyautogui.easeInQuart
  153. pyautogui.easeOutQuart
  154. pyautogui.easeInOutQuart
  155. pyautogui.easeInQuint
  156. pyautogui.easeOutQuint
  157. pyautogui.easeInOutQuint
  158. pyautogui.easeInSine
  159. pyautogui.easeOutSine
  160. pyautogui.easeInOutSine
  161. pyautogui.easeInExpo
  162. pyautogui.easeOutExpo
  163. pyautogui.easeInOutExpo
  164. pyautogui.easeInCirc
  165. pyautogui.easeOutCirc
  166. pyautogui.easeInOutCirc
  167. pyautogui.easeInElastic
  168. pyautogui.easeOutElastic
  169. pyautogui.easeInOutElastic
  170. pyautogui.easeInBack
  171. pyautogui.easeOutBack
  172. pyautogui.easeInOutBack
  173. pyautogui.easeInBounce
  174. pyautogui.easeOutBounce
  175. pyautogui.easeInOutBounce
  176.  
  177.  
  178. def test_size(self):
  179. width, height = pyautogui.size()
  180.  
  181. self.assertTrue(isinstance(width, int), 'Type of width is %s' % (type(width)))
  182. self.assertTrue(isinstance(height, int), 'Type of height is %s' % (type(height)))
  183. self.assertTrue(width > 0, 'Width is set to %s' % (width))
  184. self.assertTrue(height > 0, 'Height is set to %s' % (height))
  185.  
  186.  
  187. def test_position(self):
  188. mousex, mousey = pyautogui.position()
  189.  
  190. self.assertTrue(isinstance(mousex, int), 'Type of mousex is %s' % (type(mousex)))
  191. self.assertTrue(isinstance(mousey, int), 'Type of mousey is %s' % (type(mousey)))
  192.  
  193. # Test passing x and y arguments to position().
  194. pyautogui.moveTo(mousex + 1, mousey + 1)
  195. x, y = pyautogui.position(mousex, None)
  196. self.assertEqual(x, mousex)
  197. self.assertNotEqual(y, mousey)
  198.  
  199. x, y = pyautogui.position(None, mousey)
  200. self.assertNotEqual(x, mousex)
  201. self.assertEqual(y, mousey)
  202.  
  203.  
  204. def test_onScreen(self):
  205. zero = P(0, 0)
  206. xone = P(1, 0)
  207. yone = P(0, 1)
  208. size = P(*pyautogui.size())
  209. half = size / 2
  210.  
  211. on_screen = [
  212. zero,
  213. zero + xone,
  214. zero + yone,
  215. zero + xone + yone,
  216. half,
  217. size - xone - yone,
  218. ]
  219. off_screen = [
  220. zero - xone,
  221. zero - yone,
  222. zero - xone - yone,
  223. size - xone,
  224. size - yone,
  225. size,
  226. ]
  227.  
  228. for value, coords in [(True, on_screen), (False, off_screen)]:
  229. for coord in coords:
  230. self.assertEqual(value, pyautogui.onScreen(*coord), 'onScreen({0}, {1}) should be {2}'.format(coord.x, coord.y, value))
  231. self.assertEqual(value, pyautogui.onScreen(list(coord)), 'onScreen([{0}, {1}]) should be {2}'.format(coord.x, coord.y, value))
  232. self.assertEqual(value, pyautogui.onScreen(tuple(coord)), 'onScreen(({0}, {1})) should be {2}'.format(coord.x, coord.y, value))
  233. self.assertEqual(value, pyautogui.onScreen(coord), 'onScreen({0}) should be {1}'.format(repr(coord), value))
  234.  
  235. # These can raise either ValueError or TypeError.
  236. with self.assertRaises(ValueError):
  237. pyautogui.onScreen([0, 0], 0)
  238. with self.assertRaises(ValueError):
  239. pyautogui.onScreen((0, 0), 0)
  240. with self.assertRaises(TypeError):
  241. pyautogui.onScreen(0, 0, 0)
  242. with self.assertRaises(TypeError):
  243. pyautogui.onScreen(0)
  244.  
  245. def test_pause(self):
  246. oldValue = pyautogui.PAUSE
  247.  
  248. startTime = time.time()
  249. pyautogui.PAUSE = 0.35 # there should be a 0.35 second pause after each call
  250. pyautogui.moveTo(1, 1)
  251. pyautogui.moveRel(0,1)
  252. pyautogui.moveTo(1, 1)
  253.  
  254. elapsed = time.time() - startTime
  255. self.assertTrue(1.0 < elapsed < 1.1, 'Took %s seconds, expected 1.0 < 1.1 seconds.' % (elapsed))
  256.  
  257. pyautogui.PAUSE = oldValue # restore the old PAUSE value
  258.  
  259.  
  260. class TestMouse(unittest.TestCase):
  261. # NOTE - The user moving the mouse during many of these tests will cause them to fail.
  262.  
  263. # There is no need to test all tweening functions.
  264. TWEENS = [
  265. 'linear',
  266. 'easeInElastic',
  267. 'easeOutElastic',
  268. 'easeInOutElastic',
  269. 'easeInBack',
  270. 'easeOutBack',
  271. 'easeInOutBack',
  272. ]
  273.  
  274. def setUp(self):
  275. self.oldFailsafeSetting = pyautogui.FAILSAFE
  276. self.center = P(*pyautogui.size()) // 2
  277.  
  278. pyautogui.FAILSAFE = False
  279. pyautogui.moveTo(*self.center) # make sure failsafe isn't triggered during this test
  280. pyautogui.FAILSAFE = True
  281.  
  282. def tearDown(self):
  283. pyautogui.FAILSAFE = self.oldFailsafeSetting
  284.  
  285. def test_moveTo(self):
  286. # moving the mouse
  287. desired = self.center
  288. pyautogui.moveTo(*desired)
  289. mousepos = P(*pyautogui.position())
  290. self.assertEqual(mousepos, desired)
  291.  
  292. # no coordinate specified (should be a NO-OP)
  293. pyautogui.moveTo(None, None)
  294. mousepos = P(*pyautogui.position())
  295. self.assertEqual(mousepos, desired)
  296.  
  297. # moving the mouse to a new location
  298. desired += P(42, 42)
  299. pyautogui.moveTo(*desired)
  300. mousepos = P(*pyautogui.position())
  301. self.assertEqual(mousepos, desired)
  302.  
  303. # moving the mouse over time (1/5 second)
  304. desired -= P(42, 42)
  305. pyautogui.moveTo(desired.x, desired.y, duration=0.2)
  306. mousepos = P(*pyautogui.position())
  307. self.assertEqual(mousepos, desired)
  308.  
  309. # moving the mouse with only x specified
  310. desired -= P(42, 0)
  311. pyautogui.moveTo(desired.x, None)
  312. mousepos = P(*pyautogui.position())
  313. self.assertEqual(mousepos, desired)
  314.  
  315. # ...and only y specified
  316. desired -= P(0, 42)
  317. pyautogui.moveTo(None, desired.y)
  318. mousepos = P(*pyautogui.position())
  319. self.assertEqual(mousepos, desired)
  320.  
  321. # Passing a list instead of separate x and y.
  322. desired += P(42, 42)
  323. pyautogui.moveTo(list(desired))
  324. mousepos = P(*pyautogui.position())
  325. self.assertEqual(mousepos, desired)
  326.  
  327. # Passing a tuple instead of separate x and y.
  328. desired += P(42, 42)
  329. pyautogui.moveTo(tuple(desired))
  330. mousepos = P(*pyautogui.position())
  331. self.assertEqual(mousepos, desired)
  332.  
  333. # Passing a sequence-like object instead of separate x and y.
  334. desired -= P(42, 42)
  335. pyautogui.moveTo(desired)
  336. mousepos = P(*pyautogui.position())
  337. self.assertEqual(mousepos, desired)
  338.  
  339. def test_moveToWithTween(self):
  340. origin = self.center - P(100, 100)
  341. destination = self.center + P(100, 100)
  342.  
  343. def resetMouse():
  344. pyautogui.moveTo(*origin)
  345. mousepos = P(*pyautogui.position())
  346. self.assertEqual(mousepos, origin)
  347.  
  348. for tweenName in self.TWEENS:
  349. tweenFunc = getattr(pyautogui, tweenName)
  350. resetMouse()
  351. pyautogui.moveTo(destination.x, destination.y, duration=pyautogui.MINIMUM_DURATION * 2, tween=tweenFunc)
  352. mousepos = P(*pyautogui.position())
  353. self.assertEqual(mousepos, destination, '%s tween move failed. mousepos set to %s instead of %s' % (tweenName, mousepos, destination))
  354.  
  355. def test_moveRel(self):
  356. # start at the center
  357. desired = self.center
  358. pyautogui.moveTo(*desired)
  359. mousepos = P(*pyautogui.position())
  360. self.assertEqual(mousepos, desired)
  361.  
  362. # move down and right
  363. desired += P(42, 42)
  364. pyautogui.moveRel(42, 42)
  365. mousepos = P(*pyautogui.position())
  366. self.assertEqual(mousepos, desired)
  367.  
  368. # move up and left
  369. desired -= P(42, 42)
  370. pyautogui.moveRel(-42, -42)
  371. mousepos = P(*pyautogui.position())
  372. self.assertEqual(mousepos, desired)
  373.  
  374. # move right
  375. desired += P(42, 0)
  376. pyautogui.moveRel(42, None)
  377. mousepos = P(*pyautogui.position())
  378. self.assertEqual(mousepos, desired)
  379.  
  380. # move down
  381. desired += P(0, 42)
  382. pyautogui.moveRel(None, 42)
  383. mousepos = P(*pyautogui.position())
  384. self.assertEqual(mousepos, desired)
  385.  
  386. # move left
  387. desired += P(-42, 0)
  388. pyautogui.moveRel(-42, None)
  389. mousepos = P(*pyautogui.position())
  390. self.assertEqual(mousepos, desired)
  391.  
  392. # move up
  393. desired += P(0, -42)
  394. pyautogui.moveRel(None, -42)
  395. mousepos = P(*pyautogui.position())
  396. self.assertEqual(mousepos, desired)
  397.  
  398. # Passing a list instead of separate x and y.
  399. desired += P(42, 42)
  400. pyautogui.moveRel([42, 42])
  401. mousepos = P(*pyautogui.position())
  402. self.assertEqual(mousepos, desired)
  403.  
  404. # Passing a tuple instead of separate x and y.
  405. desired -= P(42, 42)
  406. pyautogui.moveRel((-42, -42))
  407. mousepos = P(*pyautogui.position())
  408. self.assertEqual(mousepos, desired)
  409.  
  410. # Passing a sequence-like object instead of separate x and y.
  411. desired += P(42, 42)
  412. pyautogui.moveRel(P(42, 42))
  413. mousepos = P(*pyautogui.position())
  414. self.assertEqual(mousepos, desired)
  415.  
  416. def test_moveRelWithTween(self):
  417. origin = self.center - P(100, 100)
  418. delta = P(200, 200)
  419. destination = origin + delta
  420.  
  421. def resetMouse():
  422. pyautogui.moveTo(*origin)
  423. mousepos = P(*pyautogui.position())
  424. self.assertEqual(mousepos, origin)
  425.  
  426. for tweenName in self.TWEENS:
  427. tweenFunc = getattr(pyautogui, tweenName)
  428. resetMouse()
  429. pyautogui.moveRel(delta.x, delta.y, duration=pyautogui.MINIMUM_DURATION * 2, tween=tweenFunc)
  430. mousepos = P(*pyautogui.position())
  431. self.assertEqual(mousepos, destination, '%s tween move failed. mousepos set to %s instead of %s' % (tweenName, mousepos, destination))
  432.  
  433. def test_scroll(self):
  434. # TODO - currently this just checks that scrolling doesn't result in an error.
  435. pyautogui.scroll(1)
  436. pyautogui.scroll(-1)
  437. pyautogui.hscroll(1)
  438. pyautogui.hscroll(-1)
  439. pyautogui.vscroll(1)
  440. pyautogui.vscroll(-1)
  441.  
  442.  
  443. class TypewriteThread(threading.Thread):
  444. def __init__(self, msg, interval=0.0):
  445. super(TypewriteThread, self).__init__()
  446. self.msg = msg
  447. self.interval = interval
  448.  
  449.  
  450. def run(self):
  451. time.sleep(0.25) # NOTE: BE SURE TO ACCOUNT FOR THIS QUARTER SECOND FOR TIMING TESTS!
  452. pyautogui.typewrite(self.msg, self.interval)
  453.  
  454.  
  455. class PressThread(threading.Thread):
  456. def __init__(self, keysArg):
  457. super(PressThread, self).__init__()
  458. self.keysArg = keysArg
  459.  
  460.  
  461. def run(self):
  462. time.sleep(0.25) # NOTE: BE SURE TO ACCOUNT FOR THIS QUARTER SECOND FOR TIMING TESTS!
  463. pyautogui.press(self.keysArg)
  464.  
  465.  
  466. class TestKeyboard(unittest.TestCase):
  467. # NOTE: The terminal window running this script must be in focus during the keyboard tests.
  468. # You cannot run this as a scheduled task or remotely.
  469.  
  470. def setUp(self):
  471. self.oldFailsafeSetting = pyautogui.FAILSAFE
  472. pyautogui.FAILSAFE = False
  473. pyautogui.moveTo(42, 42) # make sure failsafe isn't triggered during this test
  474. pyautogui.FAILSAFE = True
  475.  
  476.  
  477. def tearDown(self):
  478. pyautogui.FAILSAFE = self.oldFailsafeSetting
  479.  
  480.  
  481. def test_typewrite(self):
  482. # 'Hello world!\n' test
  483. t = TypewriteThread('Hello world!\n')
  484. t.start()
  485. response = INPUT_FUNC()
  486. self.assertEqual(response, 'Hello world!')
  487.  
  488. # 'Hello world!\n' as a list argument
  489. t = TypewriteThread(list('Hello world!\n'))
  490. t.start()
  491. response = INPUT_FUNC()
  492. self.assertEqual(response, 'Hello world!')
  493.  
  494. # All printable ASCII characters test
  495. allKeys = []
  496. for c in range(32, 127):
  497. allKeys.append(chr(c))
  498. allKeys = ''.join(allKeys)
  499.  
  500. t = TypewriteThread(allKeys + '\n')
  501. t.start()
  502. response = INPUT_FUNC()
  503. self.assertEqual(response, allKeys)
  504.  
  505.  
  506. def checkForValidCharacters(self, msg):
  507. for c in msg:
  508. self.assertTrue(pyautogui.isValidKey(c), '"%c" is not a valid key on platform %s' % (c, sys.platform))
  509.  
  510.  
  511. def test_typewrite_slow(self):
  512.  
  513. # Test out the interval parameter to make sure it adds pauses.
  514. t = TypewriteThread('Hello world!\n', 0.1)
  515. startTime = time.time()
  516. t.start()
  517. response = INPUT_FUNC()
  518. self.assertEqual(response, 'Hello world!')
  519. elapsed = time.time() - startTime
  520. self.assertTrue(1.0 < elapsed < 2.0, 'Took %s seconds, expected 1.0 < x 2.0 seconds.' % (elapsed))
  521.  
  522.  
  523. def test_typewrite_editable(self):
  524. # Backspace test
  525. t = TypewriteThread(['a', 'b', 'c', '\b', 'backspace', 'x', 'y', 'z', '\n'])
  526. t.start()
  527. response = INPUT_FUNC()
  528. self.assertEqual(response, 'axyz')
  529.  
  530. # TODO - Currently the arrow keys don't seem to work entirely correctly on OS X.
  531. if sys.platform != 'darwin':
  532. # Arrow key test
  533. t = TypewriteThread(['a', 'b', 'c', 'left', 'left', 'right', 'x', '\n'])
  534. t.start()
  535. response = INPUT_FUNC()
  536. self.assertEqual(response, 'abxc')
  537.  
  538. # Del key test
  539. t = TypewriteThread(['a', 'b', 'c', 'left', 'left','left', 'del', 'delete', '\n'])
  540. t.start()
  541. response = INPUT_FUNC()
  542. self.assertEqual(response, 'c')
  543.  
  544. # Home and end key test
  545. t = TypewriteThread(['a', 'b', 'c', 'home', 'x','end', 'z', '\n'])
  546. t.start()
  547. response = INPUT_FUNC()
  548. self.assertEqual(response, 'xabcz')
  549.  
  550.  
  551. def test_press(self):
  552. # '' test
  553. t = PressThread('enter')
  554. t.start()
  555. response = INPUT_FUNC()
  556. self.assertEqual(response, '')
  557.  
  558. # 'a' test, also test sending list of 1- and multi-length strings
  559. t = PressThread(['a', 'enter'])
  560. t.start()
  561. response = INPUT_FUNC()
  562. self.assertEqual(response, 'a')
  563.  
  564. # 'ba' test, also test sending list of 1- and multi-length strings
  565. t = PressThread(['a', 'left', 'b', 'enter'])
  566. t.start()
  567. response = INPUT_FUNC()
  568. self.assertEqual(response, 'ba')
  569.  
  570.  
  571. def test_typewrite_space(self):
  572. # Backspace test
  573. t = TypewriteThread(['space', ' ', '\n']) # test both 'space' and ' '
  574. t.start()
  575. response = INPUT_FUNC()
  576. self.assertEqual(response, ' ')
  577.  
  578.  
  579. class TestFailSafe(unittest.TestCase):
  580. def test_failsafe(self):
  581. self.oldFailsafeSetting = pyautogui.FAILSAFE
  582. pyautogui.FAILSAFE = False
  583. pyautogui.moveTo(42, 42) # make sure mouse is not in failsafe position to begin with
  584.  
  585. pyautogui.FAILSAFE = True
  586. self.assertRaises(pyautogui.FailSafeException, pyautogui.moveTo, 0, 0)
  587.  
  588. pyautogui.FAILSAFE = False
  589. pyautogui.moveTo(0, 0)# This line should not cause the fail safe exception to be raised.
  590.  
  591. pyautogui.FAILSAFE = self.oldFailsafeSetting
  592.  
  593.  
  594. if __name__ == '__main__':
  595. unittest.main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement