test_testutils.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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. """Tests for testing utils (psutil.tests namespace)."""
  7. import collections
  8. import contextlib
  9. import errno
  10. import os
  11. import socket
  12. import stat
  13. import subprocess
  14. import unittest
  15. import psutil
  16. import psutil.tests
  17. from psutil import FREEBSD
  18. from psutil import NETBSD
  19. from psutil import POSIX
  20. from psutil._common import open_binary
  21. from psutil._common import open_text
  22. from psutil._common import supports_ipv6
  23. from psutil.tests import CI_TESTING
  24. from psutil.tests import COVERAGE
  25. from psutil.tests import HAS_CONNECTIONS_UNIX
  26. from psutil.tests import PYTHON_EXE
  27. from psutil.tests import PYTHON_EXE_ENV
  28. from psutil.tests import PsutilTestCase
  29. from psutil.tests import TestMemoryLeak
  30. from psutil.tests import bind_socket
  31. from psutil.tests import bind_unix_socket
  32. from psutil.tests import call_until
  33. from psutil.tests import chdir
  34. from psutil.tests import create_sockets
  35. from psutil.tests import get_free_port
  36. from psutil.tests import is_namedtuple
  37. from psutil.tests import mock
  38. from psutil.tests import process_namespace
  39. from psutil.tests import reap_children
  40. from psutil.tests import retry
  41. from psutil.tests import retry_on_failure
  42. from psutil.tests import safe_mkdir
  43. from psutil.tests import safe_rmpath
  44. from psutil.tests import serialrun
  45. from psutil.tests import system_namespace
  46. from psutil.tests import tcp_socketpair
  47. from psutil.tests import terminate
  48. from psutil.tests import unix_socketpair
  49. from psutil.tests import wait_for_file
  50. from psutil.tests import wait_for_pid
  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 # noqa
  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 # noqa
  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 # noqa
  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 # noqa
  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, 'r')
  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.assertPidGone(p.pid)
  209. terminate(p)
  210. # by psutil.Process
  211. p = psutil.Process(self.spawn_testproc().pid)
  212. terminate(p)
  213. self.assertPidGone(p.pid)
  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. env=PYTHON_EXE_ENV)
  219. terminate(p)
  220. self.assertPidGone(p.pid)
  221. terminate(p)
  222. # by PID
  223. pid = self.spawn_testproc().pid
  224. terminate(pid)
  225. self.assertPidGone(p.pid)
  226. terminate(pid)
  227. # zombie
  228. if POSIX:
  229. parent, zombie = self.spawn_zombie()
  230. terminate(parent)
  231. terminate(zombie)
  232. self.assertPidGone(parent.pid)
  233. self.assertPidGone(zombie.pid)
  234. class TestNetUtils(PsutilTestCase):
  235. def bind_socket(self):
  236. port = get_free_port()
  237. with contextlib.closing(bind_socket(addr=('', port))) as s:
  238. self.assertEqual(s.getsockname()[1], port)
  239. @unittest.skipIf(not POSIX, "POSIX only")
  240. def test_bind_unix_socket(self):
  241. name = self.get_testfn()
  242. sock = bind_unix_socket(name)
  243. with contextlib.closing(sock):
  244. self.assertEqual(sock.family, socket.AF_UNIX)
  245. self.assertEqual(sock.type, socket.SOCK_STREAM)
  246. self.assertEqual(sock.getsockname(), name)
  247. assert os.path.exists(name)
  248. assert stat.S_ISSOCK(os.stat(name).st_mode)
  249. # UDP
  250. name = self.get_testfn()
  251. sock = bind_unix_socket(name, type=socket.SOCK_DGRAM)
  252. with contextlib.closing(sock):
  253. self.assertEqual(sock.type, socket.SOCK_DGRAM)
  254. def tcp_tcp_socketpair(self):
  255. addr = ("127.0.0.1", get_free_port())
  256. server, client = tcp_socketpair(socket.AF_INET, addr=addr)
  257. with contextlib.closing(server):
  258. with contextlib.closing(client):
  259. # Ensure they are connected and the positions are
  260. # correct.
  261. self.assertEqual(server.getsockname(), addr)
  262. self.assertEqual(client.getpeername(), addr)
  263. self.assertNotEqual(client.getsockname(), addr)
  264. @unittest.skipIf(not POSIX, "POSIX only")
  265. @unittest.skipIf(NETBSD or FREEBSD,
  266. "/var/run/log UNIX socket opened by default")
  267. def test_unix_socketpair(self):
  268. p = psutil.Process()
  269. num_fds = p.num_fds()
  270. assert not p.connections(kind='unix')
  271. name = self.get_testfn()
  272. server, client = unix_socketpair(name)
  273. try:
  274. assert os.path.exists(name)
  275. assert stat.S_ISSOCK(os.stat(name).st_mode)
  276. self.assertEqual(p.num_fds() - num_fds, 2)
  277. self.assertEqual(len(p.connections(kind='unix')), 2)
  278. self.assertEqual(server.getsockname(), name)
  279. self.assertEqual(client.getpeername(), name)
  280. finally:
  281. client.close()
  282. server.close()
  283. def test_create_sockets(self):
  284. with create_sockets() as socks:
  285. fams = collections.defaultdict(int)
  286. types = collections.defaultdict(int)
  287. for s in socks:
  288. fams[s.family] += 1
  289. # work around http://bugs.python.org/issue30204
  290. types[s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)] += 1
  291. self.assertGreaterEqual(fams[socket.AF_INET], 2)
  292. if supports_ipv6():
  293. self.assertGreaterEqual(fams[socket.AF_INET6], 2)
  294. if POSIX and HAS_CONNECTIONS_UNIX:
  295. self.assertGreaterEqual(fams[socket.AF_UNIX], 2)
  296. self.assertGreaterEqual(types[socket.SOCK_STREAM], 2)
  297. self.assertGreaterEqual(types[socket.SOCK_DGRAM], 2)
  298. @serialrun
  299. class TestMemLeakClass(TestMemoryLeak):
  300. @retry_on_failure()
  301. def test_times(self):
  302. def fun():
  303. cnt['cnt'] += 1
  304. cnt = {'cnt': 0}
  305. self.execute(fun, times=10, warmup_times=15)
  306. self.assertEqual(cnt['cnt'], 26)
  307. def test_param_err(self):
  308. self.assertRaises(ValueError, self.execute, lambda: 0, times=0)
  309. self.assertRaises(ValueError, self.execute, lambda: 0, times=-1)
  310. self.assertRaises(ValueError, self.execute, lambda: 0, warmup_times=-1)
  311. self.assertRaises(ValueError, self.execute, lambda: 0, tolerance=-1)
  312. self.assertRaises(ValueError, self.execute, lambda: 0, retries=-1)
  313. @retry_on_failure()
  314. @unittest.skipIf(CI_TESTING, "skipped on CI")
  315. @unittest.skipIf(COVERAGE, "skipped during test coverage")
  316. def test_leak_mem(self):
  317. ls = []
  318. def fun(ls=ls):
  319. ls.append("x" * 24 * 1024)
  320. try:
  321. # will consume around 3M in total
  322. self.assertRaisesRegex(AssertionError, "extra-mem",
  323. self.execute, fun, times=50)
  324. finally:
  325. del ls
  326. def test_unclosed_files(self):
  327. def fun():
  328. f = open(__file__)
  329. self.addCleanup(f.close)
  330. box.append(f)
  331. box = []
  332. kind = "fd" if POSIX else "handle"
  333. self.assertRaisesRegex(AssertionError, "unclosed " + kind,
  334. self.execute, fun)
  335. def test_tolerance(self):
  336. def fun():
  337. ls.append("x" * 24 * 1024)
  338. ls = []
  339. times = 100
  340. self.execute(fun, times=times, warmup_times=0,
  341. tolerance=200 * 1024 * 1024)
  342. self.assertEqual(len(ls), times + 1)
  343. def test_execute_w_exc(self):
  344. def fun_1():
  345. 1 / 0 # noqa
  346. self.execute_w_exc(ZeroDivisionError, fun_1)
  347. with self.assertRaises(ZeroDivisionError):
  348. self.execute_w_exc(OSError, fun_1)
  349. def fun_2():
  350. pass
  351. with self.assertRaises(AssertionError):
  352. self.execute_w_exc(ZeroDivisionError, fun_2)
  353. class TestTestingUtils(PsutilTestCase):
  354. def test_process_namespace(self):
  355. p = psutil.Process()
  356. ns = process_namespace(p)
  357. ns.test()
  358. fun = [x for x in ns.iter(ns.getters) if x[1] == 'ppid'][0][0]
  359. self.assertEqual(fun(), p.ppid())
  360. def test_system_namespace(self):
  361. ns = system_namespace()
  362. fun = [x for x in ns.iter(ns.getters) if x[1] == 'net_if_addrs'][0][0]
  363. self.assertEqual(fun(), psutil.net_if_addrs())
  364. class TestOtherUtils(PsutilTestCase):
  365. def test_is_namedtuple(self):
  366. assert is_namedtuple(collections.namedtuple('foo', 'a b c')(1, 2, 3))
  367. assert not is_namedtuple(tuple())
  368. if __name__ == '__main__':
  369. from psutil.tests.runner import run_from_name
  370. run_from_name(__file__)