test_testutils.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
  4. # Use of this source code is governed by a BSD-style license that can be
  5. # found in the LICENSE file.
  6. """
  7. Tests for testing utils (psutil.tests namespace).
  8. """
  9. import collections
  10. import contextlib
  11. import errno
  12. import os
  13. import socket
  14. import stat
  15. import subprocess
  16. from psutil import FREEBSD
  17. from psutil import NETBSD
  18. from psutil import POSIX
  19. from psutil._common import open_binary
  20. from psutil._common import open_text
  21. from psutil._common import supports_ipv6
  22. from psutil.tests import bind_socket
  23. from psutil.tests import bind_unix_socket
  24. from psutil.tests import call_until
  25. from psutil.tests import chdir
  26. from psutil.tests import CI_TESTING
  27. from psutil.tests import create_sockets
  28. from psutil.tests import get_free_port
  29. from psutil.tests import HAS_CONNECTIONS_UNIX
  30. from psutil.tests import is_namedtuple
  31. from psutil.tests import mock
  32. from psutil.tests import process_namespace
  33. from psutil.tests import PsutilTestCase
  34. from psutil.tests import PYTHON_EXE
  35. from psutil.tests import reap_children
  36. from psutil.tests import retry
  37. from psutil.tests import retry_on_failure
  38. from psutil.tests import safe_mkdir
  39. from psutil.tests import safe_rmpath
  40. from psutil.tests import serialrun
  41. from psutil.tests import system_namespace
  42. from psutil.tests import tcp_socketpair
  43. from psutil.tests import terminate
  44. from psutil.tests import TestMemoryLeak
  45. from psutil.tests import unittest
  46. from psutil.tests import unix_socketpair
  47. from psutil.tests import wait_for_file
  48. from psutil.tests import wait_for_pid
  49. import psutil
  50. import psutil.tests
  51. # ===================================================================
  52. # --- Unit tests for test utilities.
  53. # ===================================================================
  54. class TestRetryDecorator(PsutilTestCase):
  55. @mock.patch('time.sleep')
  56. def test_retry_success(self, sleep):
  57. # Fail 3 times out of 5; make sure the decorated fun returns.
  58. @retry(retries=5, interval=1, logfun=None)
  59. def foo():
  60. while queue:
  61. queue.pop()
  62. 1 / 0
  63. return 1
  64. queue = list(range(3))
  65. self.assertEqual(foo(), 1)
  66. self.assertEqual(sleep.call_count, 3)
  67. @mock.patch('time.sleep')
  68. def test_retry_failure(self, sleep):
  69. # Fail 6 times out of 5; th function is supposed to raise exc.
  70. @retry(retries=5, interval=1, logfun=None)
  71. def foo():
  72. while queue:
  73. queue.pop()
  74. 1 / 0
  75. return 1
  76. queue = list(range(6))
  77. self.assertRaises(ZeroDivisionError, foo)
  78. self.assertEqual(sleep.call_count, 5)
  79. @mock.patch('time.sleep')
  80. def test_exception_arg(self, sleep):
  81. @retry(exception=ValueError, interval=1)
  82. def foo():
  83. raise TypeError
  84. self.assertRaises(TypeError, foo)
  85. self.assertEqual(sleep.call_count, 0)
  86. @mock.patch('time.sleep')
  87. def test_no_interval_arg(self, sleep):
  88. # if interval is not specified sleep is not supposed to be called
  89. @retry(retries=5, interval=None, logfun=None)
  90. def foo():
  91. 1 / 0
  92. self.assertRaises(ZeroDivisionError, foo)
  93. self.assertEqual(sleep.call_count, 0)
  94. @mock.patch('time.sleep')
  95. def test_retries_arg(self, sleep):
  96. @retry(retries=5, interval=1, logfun=None)
  97. def foo():
  98. 1 / 0
  99. self.assertRaises(ZeroDivisionError, foo)
  100. self.assertEqual(sleep.call_count, 5)
  101. @mock.patch('time.sleep')
  102. def test_retries_and_timeout_args(self, sleep):
  103. self.assertRaises(ValueError, retry, retries=5, timeout=1)
  104. class TestSyncTestUtils(PsutilTestCase):
  105. def test_wait_for_pid(self):
  106. wait_for_pid(os.getpid())
  107. nopid = max(psutil.pids()) + 99999
  108. with mock.patch('psutil.tests.retry.__iter__', return_value=iter([0])):
  109. self.assertRaises(psutil.NoSuchProcess, wait_for_pid, nopid)
  110. def test_wait_for_file(self):
  111. testfn = self.get_testfn()
  112. with open(testfn, 'w') as f:
  113. f.write('foo')
  114. wait_for_file(testfn)
  115. assert not os.path.exists(testfn)
  116. def test_wait_for_file_empty(self):
  117. testfn = self.get_testfn()
  118. with open(testfn, 'w'):
  119. pass
  120. wait_for_file(testfn, empty=True)
  121. assert not os.path.exists(testfn)
  122. def test_wait_for_file_no_file(self):
  123. testfn = self.get_testfn()
  124. with mock.patch('psutil.tests.retry.__iter__', return_value=iter([0])):
  125. self.assertRaises(IOError, wait_for_file, testfn)
  126. def test_wait_for_file_no_delete(self):
  127. testfn = self.get_testfn()
  128. with open(testfn, 'w') as f:
  129. f.write('foo')
  130. wait_for_file(testfn, delete=False)
  131. assert os.path.exists(testfn)
  132. def test_call_until(self):
  133. ret = call_until(lambda: 1, "ret == 1")
  134. self.assertEqual(ret, 1)
  135. class TestFSTestUtils(PsutilTestCase):
  136. def test_open_text(self):
  137. with open_text(__file__) as f:
  138. self.assertEqual(f.mode, 'rt')
  139. def test_open_binary(self):
  140. with open_binary(__file__) as f:
  141. self.assertEqual(f.mode, 'rb')
  142. def test_safe_mkdir(self):
  143. testfn = self.get_testfn()
  144. safe_mkdir(testfn)
  145. assert os.path.isdir(testfn)
  146. safe_mkdir(testfn)
  147. assert os.path.isdir(testfn)
  148. def test_safe_rmpath(self):
  149. # test file is removed
  150. testfn = self.get_testfn()
  151. open(testfn, 'w').close()
  152. safe_rmpath(testfn)
  153. assert not os.path.exists(testfn)
  154. # test no exception if path does not exist
  155. safe_rmpath(testfn)
  156. # test dir is removed
  157. os.mkdir(testfn)
  158. safe_rmpath(testfn)
  159. assert not os.path.exists(testfn)
  160. # test other exceptions are raised
  161. with mock.patch('psutil.tests.os.stat',
  162. side_effect=OSError(errno.EINVAL, "")) as m:
  163. with self.assertRaises(OSError):
  164. safe_rmpath(testfn)
  165. assert m.called
  166. def test_chdir(self):
  167. testfn = self.get_testfn()
  168. base = os.getcwd()
  169. os.mkdir(testfn)
  170. with chdir(testfn):
  171. self.assertEqual(os.getcwd(), os.path.join(base, testfn))
  172. self.assertEqual(os.getcwd(), base)
  173. class TestProcessUtils(PsutilTestCase):
  174. def test_reap_children(self):
  175. subp = self.spawn_testproc()
  176. p = psutil.Process(subp.pid)
  177. assert p.is_running()
  178. reap_children()
  179. assert not p.is_running()
  180. assert not psutil.tests._pids_started
  181. assert not psutil.tests._subprocesses_started
  182. def test_spawn_children_pair(self):
  183. child, grandchild = self.spawn_children_pair()
  184. self.assertNotEqual(child.pid, grandchild.pid)
  185. assert child.is_running()
  186. assert grandchild.is_running()
  187. children = psutil.Process().children()
  188. self.assertEqual(children, [child])
  189. children = psutil.Process().children(recursive=True)
  190. self.assertEqual(len(children), 2)
  191. self.assertIn(child, children)
  192. self.assertIn(grandchild, children)
  193. self.assertEqual(child.ppid(), os.getpid())
  194. self.assertEqual(grandchild.ppid(), child.pid)
  195. terminate(child)
  196. assert not child.is_running()
  197. assert grandchild.is_running()
  198. terminate(grandchild)
  199. assert not grandchild.is_running()
  200. @unittest.skipIf(not POSIX, "POSIX only")
  201. def test_spawn_zombie(self):
  202. parent, zombie = self.spawn_zombie()
  203. self.assertEqual(zombie.status(), psutil.STATUS_ZOMBIE)
  204. def test_terminate(self):
  205. # by subprocess.Popen
  206. p = self.spawn_testproc()
  207. terminate(p)
  208. self.assertProcessGone(p)
  209. terminate(p)
  210. # by psutil.Process
  211. p = psutil.Process(self.spawn_testproc().pid)
  212. terminate(p)
  213. self.assertProcessGone(p)
  214. terminate(p)
  215. # by psutil.Popen
  216. cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"]
  217. p = psutil.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  218. terminate(p)
  219. self.assertProcessGone(p)
  220. terminate(p)
  221. # by PID
  222. pid = self.spawn_testproc().pid
  223. terminate(pid)
  224. self.assertProcessGone(p)
  225. terminate(pid)
  226. # zombie
  227. if POSIX:
  228. parent, zombie = self.spawn_zombie()
  229. terminate(parent)
  230. terminate(zombie)
  231. self.assertProcessGone(parent)
  232. self.assertProcessGone(zombie)
  233. class TestNetUtils(PsutilTestCase):
  234. def bind_socket(self):
  235. port = get_free_port()
  236. with contextlib.closing(bind_socket(addr=('', port))) as s:
  237. self.assertEqual(s.getsockname()[1], port)
  238. @unittest.skipIf(not POSIX, "POSIX only")
  239. def test_bind_unix_socket(self):
  240. name = self.get_testfn()
  241. sock = bind_unix_socket(name)
  242. with contextlib.closing(sock):
  243. self.assertEqual(sock.family, socket.AF_UNIX)
  244. self.assertEqual(sock.type, socket.SOCK_STREAM)
  245. self.assertEqual(sock.getsockname(), name)
  246. assert os.path.exists(name)
  247. assert stat.S_ISSOCK(os.stat(name).st_mode)
  248. # UDP
  249. name = self.get_testfn()
  250. sock = bind_unix_socket(name, type=socket.SOCK_DGRAM)
  251. with contextlib.closing(sock):
  252. self.assertEqual(sock.type, socket.SOCK_DGRAM)
  253. def tcp_tcp_socketpair(self):
  254. addr = ("127.0.0.1", get_free_port())
  255. server, client = tcp_socketpair(socket.AF_INET, addr=addr)
  256. with contextlib.closing(server):
  257. with contextlib.closing(client):
  258. # Ensure they are connected and the positions are
  259. # correct.
  260. self.assertEqual(server.getsockname(), addr)
  261. self.assertEqual(client.getpeername(), addr)
  262. self.assertNotEqual(client.getsockname(), addr)
  263. @unittest.skipIf(not POSIX, "POSIX only")
  264. @unittest.skipIf(NETBSD or FREEBSD,
  265. "/var/run/log UNIX socket opened by default")
  266. def test_unix_socketpair(self):
  267. p = psutil.Process()
  268. num_fds = p.num_fds()
  269. assert not p.connections(kind='unix')
  270. name = self.get_testfn()
  271. server, client = unix_socketpair(name)
  272. try:
  273. assert os.path.exists(name)
  274. assert stat.S_ISSOCK(os.stat(name).st_mode)
  275. self.assertEqual(p.num_fds() - num_fds, 2)
  276. self.assertEqual(len(p.connections(kind='unix')), 2)
  277. self.assertEqual(server.getsockname(), name)
  278. self.assertEqual(client.getpeername(), name)
  279. finally:
  280. client.close()
  281. server.close()
  282. def test_create_sockets(self):
  283. with create_sockets() as socks:
  284. fams = collections.defaultdict(int)
  285. types = collections.defaultdict(int)
  286. for s in socks:
  287. fams[s.family] += 1
  288. # work around http://bugs.python.org/issue30204
  289. types[s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)] += 1
  290. self.assertGreaterEqual(fams[socket.AF_INET], 2)
  291. if supports_ipv6():
  292. self.assertGreaterEqual(fams[socket.AF_INET6], 2)
  293. if POSIX and HAS_CONNECTIONS_UNIX:
  294. self.assertGreaterEqual(fams[socket.AF_UNIX], 2)
  295. self.assertGreaterEqual(types[socket.SOCK_STREAM], 2)
  296. self.assertGreaterEqual(types[socket.SOCK_DGRAM], 2)
  297. @serialrun
  298. class TestMemLeakClass(TestMemoryLeak):
  299. def test_times(self):
  300. def fun():
  301. cnt['cnt'] += 1
  302. cnt = {'cnt': 0}
  303. self.execute(fun, times=10, warmup_times=15)
  304. self.assertEqual(cnt['cnt'], 26)
  305. def test_param_err(self):
  306. self.assertRaises(ValueError, self.execute, lambda: 0, times=0)
  307. self.assertRaises(ValueError, self.execute, lambda: 0, times=-1)
  308. self.assertRaises(ValueError, self.execute, lambda: 0, warmup_times=-1)
  309. self.assertRaises(ValueError, self.execute, lambda: 0, tolerance=-1)
  310. self.assertRaises(ValueError, self.execute, lambda: 0, retries=-1)
  311. @retry_on_failure()
  312. @unittest.skipIf(CI_TESTING, "skipped on CI")
  313. def test_leak_mem(self):
  314. ls = []
  315. def fun(ls=ls):
  316. ls.append("x" * 24 * 1024)
  317. try:
  318. # will consume around 3M in total
  319. self.assertRaisesRegex(AssertionError, "extra-mem",
  320. self.execute, fun, times=50)
  321. finally:
  322. del ls
  323. def test_unclosed_files(self):
  324. def fun():
  325. f = open(__file__)
  326. self.addCleanup(f.close)
  327. box.append(f)
  328. box = []
  329. kind = "fd" if POSIX else "handle"
  330. self.assertRaisesRegex(AssertionError, "unclosed " + kind,
  331. self.execute, fun)
  332. def test_tolerance(self):
  333. def fun():
  334. ls.append("x" * 24 * 1024)
  335. ls = []
  336. times = 100
  337. self.execute(fun, times=times, warmup_times=0,
  338. tolerance=200 * 1024 * 1024)
  339. self.assertEqual(len(ls), times + 1)
  340. def test_execute_w_exc(self):
  341. def fun():
  342. 1 / 0
  343. self.execute_w_exc(ZeroDivisionError, fun)
  344. with self.assertRaises(ZeroDivisionError):
  345. self.execute_w_exc(OSError, fun)
  346. def fun():
  347. pass
  348. with self.assertRaises(AssertionError):
  349. self.execute_w_exc(ZeroDivisionError, fun)
  350. class TestTestingUtils(PsutilTestCase):
  351. def test_process_namespace(self):
  352. p = psutil.Process()
  353. ns = process_namespace(p)
  354. ns.test()
  355. fun = [x for x in ns.iter(ns.getters) if x[1] == 'ppid'][0][0]
  356. self.assertEqual(fun(), p.ppid())
  357. def test_system_namespace(self):
  358. ns = system_namespace()
  359. fun = [x for x in ns.iter(ns.getters) if x[1] == 'net_if_addrs'][0][0]
  360. self.assertEqual(fun(), psutil.net_if_addrs())
  361. class TestOtherUtils(PsutilTestCase):
  362. def test_is_namedtuple(self):
  363. assert is_namedtuple(collections.namedtuple('foo', 'a b c')(1, 2, 3))
  364. assert not is_namedtuple(tuple())
  365. if __name__ == '__main__':
  366. from psutil.tests.runner import run_from_name
  367. run_from_name(__file__)