test_system.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """Tests for system APIS."""
  6. import datetime
  7. import enum
  8. import errno
  9. import os
  10. import platform
  11. import pprint
  12. import shutil
  13. import signal
  14. import socket
  15. import sys
  16. import time
  17. from unittest import mock
  18. import psutil
  19. from psutil import AIX
  20. from psutil import BSD
  21. from psutil import FREEBSD
  22. from psutil import LINUX
  23. from psutil import MACOS
  24. from psutil import NETBSD
  25. from psutil import OPENBSD
  26. from psutil import POSIX
  27. from psutil import SUNOS
  28. from psutil import WINDOWS
  29. from psutil._common import broadcast_addr
  30. from psutil.tests import AARCH64
  31. from psutil.tests import ASCII_FS
  32. from psutil.tests import CI_TESTING
  33. from psutil.tests import GITHUB_ACTIONS
  34. from psutil.tests import GLOBAL_TIMEOUT
  35. from psutil.tests import HAS_BATTERY
  36. from psutil.tests import HAS_CPU_FREQ
  37. from psutil.tests import HAS_GETLOADAVG
  38. from psutil.tests import HAS_NET_IO_COUNTERS
  39. from psutil.tests import HAS_SENSORS_BATTERY
  40. from psutil.tests import HAS_SENSORS_FANS
  41. from psutil.tests import HAS_SENSORS_TEMPERATURES
  42. from psutil.tests import IS_64BIT
  43. from psutil.tests import MACOS_12PLUS
  44. from psutil.tests import PYPY
  45. from psutil.tests import UNICODE_SUFFIX
  46. from psutil.tests import PsutilTestCase
  47. from psutil.tests import check_net_address
  48. from psutil.tests import pytest
  49. from psutil.tests import retry_on_failure
  50. # ===================================================================
  51. # --- System-related API tests
  52. # ===================================================================
  53. class TestProcessIter(PsutilTestCase):
  54. def test_pid_presence(self):
  55. assert os.getpid() in [x.pid for x in psutil.process_iter()]
  56. sproc = self.spawn_testproc()
  57. assert sproc.pid in [x.pid for x in psutil.process_iter()]
  58. p = psutil.Process(sproc.pid)
  59. p.kill()
  60. p.wait()
  61. assert sproc.pid not in [x.pid for x in psutil.process_iter()]
  62. def test_no_duplicates(self):
  63. ls = list(psutil.process_iter())
  64. assert sorted(ls, key=lambda x: x.pid) == sorted(
  65. set(ls), key=lambda x: x.pid
  66. )
  67. def test_emulate_nsp(self):
  68. list(psutil.process_iter()) # populate cache
  69. for x in range(2):
  70. with mock.patch(
  71. 'psutil.Process.as_dict',
  72. side_effect=psutil.NoSuchProcess(os.getpid()),
  73. ):
  74. assert not list(psutil.process_iter(attrs=["cpu_times"]))
  75. psutil.process_iter.cache_clear() # repeat test without cache
  76. def test_emulate_access_denied(self):
  77. list(psutil.process_iter()) # populate cache
  78. for x in range(2):
  79. with mock.patch(
  80. 'psutil.Process.as_dict',
  81. side_effect=psutil.AccessDenied(os.getpid()),
  82. ):
  83. with pytest.raises(psutil.AccessDenied):
  84. list(psutil.process_iter(attrs=["cpu_times"]))
  85. psutil.process_iter.cache_clear() # repeat test without cache
  86. def test_attrs(self):
  87. for p in psutil.process_iter(attrs=['pid']):
  88. assert list(p.info.keys()) == ['pid']
  89. # yield again
  90. for p in psutil.process_iter(attrs=['pid']):
  91. assert list(p.info.keys()) == ['pid']
  92. with pytest.raises(ValueError):
  93. list(psutil.process_iter(attrs=['foo']))
  94. with mock.patch(
  95. "psutil._psplatform.Process.cpu_times",
  96. side_effect=psutil.AccessDenied(0, ""),
  97. ) as m:
  98. for p in psutil.process_iter(attrs=["pid", "cpu_times"]):
  99. assert p.info['cpu_times'] is None
  100. assert p.info['pid'] >= 0
  101. assert m.called
  102. with mock.patch(
  103. "psutil._psplatform.Process.cpu_times",
  104. side_effect=psutil.AccessDenied(0, ""),
  105. ) as m:
  106. flag = object()
  107. for p in psutil.process_iter(
  108. attrs=["pid", "cpu_times"], ad_value=flag
  109. ):
  110. assert p.info['cpu_times'] is flag
  111. assert p.info['pid'] >= 0
  112. assert m.called
  113. def test_cache_clear(self):
  114. list(psutil.process_iter()) # populate cache
  115. assert psutil._pmap
  116. psutil.process_iter.cache_clear()
  117. assert not psutil._pmap
  118. class TestProcessAPIs(PsutilTestCase):
  119. @pytest.mark.skipif(
  120. PYPY and WINDOWS,
  121. reason="spawn_testproc() unreliable on PYPY + WINDOWS",
  122. )
  123. def test_wait_procs(self):
  124. def callback(p):
  125. pids.append(p.pid)
  126. pids = []
  127. sproc1 = self.spawn_testproc()
  128. sproc2 = self.spawn_testproc()
  129. sproc3 = self.spawn_testproc()
  130. procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]
  131. with pytest.raises(ValueError):
  132. psutil.wait_procs(procs, timeout=-1)
  133. with pytest.raises(TypeError):
  134. psutil.wait_procs(procs, callback=1)
  135. t = time.time()
  136. gone, alive = psutil.wait_procs(procs, timeout=0.01, callback=callback)
  137. assert time.time() - t < 0.5
  138. assert not gone
  139. assert len(alive) == 3
  140. assert not pids
  141. for p in alive:
  142. assert not hasattr(p, 'returncode')
  143. @retry_on_failure(30)
  144. def test_1(procs, callback):
  145. gone, alive = psutil.wait_procs(
  146. procs, timeout=0.03, callback=callback
  147. )
  148. assert len(gone) == 1
  149. assert len(alive) == 2
  150. return gone, alive
  151. sproc3.terminate()
  152. gone, alive = test_1(procs, callback)
  153. assert sproc3.pid in [x.pid for x in gone]
  154. if POSIX:
  155. assert gone.pop().returncode == -signal.SIGTERM
  156. else:
  157. assert gone.pop().returncode == 1
  158. assert pids == [sproc3.pid]
  159. for p in alive:
  160. assert not hasattr(p, 'returncode')
  161. @retry_on_failure(30)
  162. def test_2(procs, callback):
  163. gone, alive = psutil.wait_procs(
  164. procs, timeout=0.03, callback=callback
  165. )
  166. assert len(gone) == 3
  167. assert len(alive) == 0
  168. return gone, alive
  169. sproc1.terminate()
  170. sproc2.terminate()
  171. gone, alive = test_2(procs, callback)
  172. assert set(pids) == {sproc1.pid, sproc2.pid, sproc3.pid}
  173. for p in gone:
  174. assert hasattr(p, 'returncode')
  175. @pytest.mark.skipif(
  176. PYPY and WINDOWS,
  177. reason="spawn_testproc() unreliable on PYPY + WINDOWS",
  178. )
  179. def test_wait_procs_no_timeout(self):
  180. sproc1 = self.spawn_testproc()
  181. sproc2 = self.spawn_testproc()
  182. sproc3 = self.spawn_testproc()
  183. procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]
  184. for p in procs:
  185. p.terminate()
  186. psutil.wait_procs(procs)
  187. def test_pid_exists(self):
  188. sproc = self.spawn_testproc()
  189. assert psutil.pid_exists(sproc.pid)
  190. p = psutil.Process(sproc.pid)
  191. p.kill()
  192. p.wait()
  193. assert not psutil.pid_exists(sproc.pid)
  194. assert not psutil.pid_exists(-1)
  195. assert psutil.pid_exists(0) == (0 in psutil.pids())
  196. def test_pid_exists_2(self):
  197. pids = psutil.pids()
  198. for pid in pids:
  199. try:
  200. assert psutil.pid_exists(pid)
  201. except AssertionError:
  202. # in case the process disappeared in meantime fail only
  203. # if it is no longer in psutil.pids()
  204. time.sleep(0.1)
  205. assert pid not in psutil.pids()
  206. pids = range(max(pids) + 15000, max(pids) + 16000)
  207. for pid in pids:
  208. assert not psutil.pid_exists(pid)
  209. class TestMiscAPIs(PsutilTestCase):
  210. def test_boot_time(self):
  211. bt = psutil.boot_time()
  212. assert isinstance(bt, float)
  213. assert bt > 0
  214. assert bt < time.time()
  215. @pytest.mark.skipif(
  216. CI_TESTING and not psutil.users(), reason="unreliable on CI"
  217. )
  218. def test_users(self):
  219. users = psutil.users()
  220. assert users
  221. for user in users:
  222. with self.subTest(user=user):
  223. assert user.name
  224. assert isinstance(user.name, str)
  225. assert isinstance(user.terminal, (str, type(None)))
  226. if user.host is not None:
  227. assert isinstance(user.host, (str, type(None)))
  228. user.terminal # noqa: B018
  229. user.host # noqa: B018
  230. assert user.started > 0.0
  231. datetime.datetime.fromtimestamp(user.started)
  232. if WINDOWS or OPENBSD:
  233. assert user.pid is None
  234. else:
  235. psutil.Process(user.pid)
  236. def test_os_constants(self):
  237. names = [
  238. "POSIX",
  239. "WINDOWS",
  240. "LINUX",
  241. "MACOS",
  242. "FREEBSD",
  243. "OPENBSD",
  244. "NETBSD",
  245. "BSD",
  246. "SUNOS",
  247. ]
  248. for name in names:
  249. assert isinstance(getattr(psutil, name), bool), name
  250. if os.name == 'posix':
  251. assert psutil.POSIX
  252. assert not psutil.WINDOWS
  253. names.remove("POSIX")
  254. if "linux" in sys.platform.lower():
  255. assert psutil.LINUX
  256. names.remove("LINUX")
  257. elif "bsd" in sys.platform.lower():
  258. assert psutil.BSD
  259. assert [psutil.FREEBSD, psutil.OPENBSD, psutil.NETBSD].count(
  260. True
  261. ) == 1
  262. names.remove("BSD")
  263. names.remove("FREEBSD")
  264. names.remove("OPENBSD")
  265. names.remove("NETBSD")
  266. elif (
  267. "sunos" in sys.platform.lower()
  268. or "solaris" in sys.platform.lower()
  269. ):
  270. assert psutil.SUNOS
  271. names.remove("SUNOS")
  272. elif "darwin" in sys.platform.lower():
  273. assert psutil.MACOS
  274. names.remove("MACOS")
  275. else:
  276. assert psutil.WINDOWS
  277. assert not psutil.POSIX
  278. names.remove("WINDOWS")
  279. # assert all other constants are set to False
  280. for name in names:
  281. assert not getattr(psutil, name), name
  282. class TestMemoryAPIs(PsutilTestCase):
  283. def test_virtual_memory(self):
  284. mem = psutil.virtual_memory()
  285. assert mem.total > 0, mem
  286. assert mem.available > 0, mem
  287. assert 0 <= mem.percent <= 100, mem
  288. assert mem.used > 0, mem
  289. assert mem.free >= 0, mem
  290. for name in mem._fields:
  291. value = getattr(mem, name)
  292. if name != 'percent':
  293. assert isinstance(value, int)
  294. if name != 'total':
  295. if not value >= 0:
  296. raise self.fail(f"{name!r} < 0 ({value})")
  297. if value > mem.total:
  298. raise self.fail(
  299. f"{name!r} > total (total={mem.total}, {name}={value})"
  300. )
  301. def test_swap_memory(self):
  302. mem = psutil.swap_memory()
  303. assert mem._fields == (
  304. 'total',
  305. 'used',
  306. 'free',
  307. 'percent',
  308. 'sin',
  309. 'sout',
  310. )
  311. assert mem.total >= 0, mem
  312. assert mem.used >= 0, mem
  313. if mem.total > 0:
  314. # likely a system with no swap partition
  315. assert mem.free > 0, mem
  316. else:
  317. assert mem.free == 0, mem
  318. assert 0 <= mem.percent <= 100, mem
  319. assert mem.sin >= 0, mem
  320. assert mem.sout >= 0, mem
  321. class TestCpuAPIs(PsutilTestCase):
  322. def test_cpu_count_logical(self):
  323. logical = psutil.cpu_count()
  324. assert logical is not None
  325. assert logical == len(psutil.cpu_times(percpu=True))
  326. assert logical >= 1
  327. if os.path.exists("/proc/cpuinfo"):
  328. with open("/proc/cpuinfo") as fd:
  329. cpuinfo_data = fd.read()
  330. if "physical id" not in cpuinfo_data:
  331. raise pytest.skip("cpuinfo doesn't include physical id")
  332. def test_cpu_count_cores(self):
  333. logical = psutil.cpu_count()
  334. cores = psutil.cpu_count(logical=False)
  335. if cores is None:
  336. raise pytest.skip("cpu_count_cores() is None")
  337. if WINDOWS and sys.getwindowsversion()[:2] <= (6, 1): # <= Vista
  338. assert cores is None
  339. else:
  340. assert cores >= 1
  341. assert logical >= cores
  342. def test_cpu_count_none(self):
  343. # https://github.com/giampaolo/psutil/issues/1085
  344. for val in (-1, 0, None):
  345. with mock.patch(
  346. 'psutil._psplatform.cpu_count_logical', return_value=val
  347. ) as m:
  348. assert psutil.cpu_count() is None
  349. assert m.called
  350. with mock.patch(
  351. 'psutil._psplatform.cpu_count_cores', return_value=val
  352. ) as m:
  353. assert psutil.cpu_count(logical=False) is None
  354. assert m.called
  355. def test_cpu_times(self):
  356. # Check type, value >= 0, str().
  357. total = 0
  358. times = psutil.cpu_times()
  359. sum(times)
  360. for cp_time in times:
  361. assert isinstance(cp_time, float)
  362. assert cp_time >= 0.0
  363. total += cp_time
  364. assert round(abs(total - sum(times)), 6) == 0
  365. str(times)
  366. # CPU times are always supposed to increase over time
  367. # or at least remain the same and that's because time
  368. # cannot go backwards.
  369. # Surprisingly sometimes this might not be the case (at
  370. # least on Windows and Linux), see:
  371. # https://github.com/giampaolo/psutil/issues/392
  372. # https://github.com/giampaolo/psutil/issues/645
  373. # if not WINDOWS:
  374. # last = psutil.cpu_times()
  375. # for x in range(100):
  376. # new = psutil.cpu_times()
  377. # for field in new._fields:
  378. # new_t = getattr(new, field)
  379. # last_t = getattr(last, field)
  380. # self.assertGreaterEqual(
  381. # new_t, last_t,
  382. # msg="{} {}".format(new_t, last_t))
  383. # last = new
  384. def test_cpu_times_time_increases(self):
  385. # Make sure time increases between calls.
  386. t1 = sum(psutil.cpu_times())
  387. stop_at = time.time() + GLOBAL_TIMEOUT
  388. while time.time() < stop_at:
  389. t2 = sum(psutil.cpu_times())
  390. if t2 > t1:
  391. return
  392. raise self.fail("time remained the same")
  393. def test_per_cpu_times(self):
  394. # Check type, value >= 0, str().
  395. for times in psutil.cpu_times(percpu=True):
  396. total = 0
  397. sum(times)
  398. for cp_time in times:
  399. assert isinstance(cp_time, float)
  400. assert cp_time >= 0.0
  401. total += cp_time
  402. assert round(abs(total - sum(times)), 6) == 0
  403. str(times)
  404. assert len(psutil.cpu_times(percpu=True)[0]) == len(
  405. psutil.cpu_times(percpu=False)
  406. )
  407. # Note: in theory CPU times are always supposed to increase over
  408. # time or remain the same but never go backwards. In practice
  409. # sometimes this is not the case.
  410. # This issue seemd to be afflict Windows:
  411. # https://github.com/giampaolo/psutil/issues/392
  412. # ...but it turns out also Linux (rarely) behaves the same.
  413. # last = psutil.cpu_times(percpu=True)
  414. # for x in range(100):
  415. # new = psutil.cpu_times(percpu=True)
  416. # for index in range(len(new)):
  417. # newcpu = new[index]
  418. # lastcpu = last[index]
  419. # for field in newcpu._fields:
  420. # new_t = getattr(newcpu, field)
  421. # last_t = getattr(lastcpu, field)
  422. # self.assertGreaterEqual(
  423. # new_t, last_t, msg="{} {}".format(lastcpu, newcpu))
  424. # last = new
  425. def test_per_cpu_times_2(self):
  426. # Simulate some work load then make sure time have increased
  427. # between calls.
  428. tot1 = psutil.cpu_times(percpu=True)
  429. giveup_at = time.time() + GLOBAL_TIMEOUT
  430. while True:
  431. if time.time() >= giveup_at:
  432. return self.fail("timeout")
  433. tot2 = psutil.cpu_times(percpu=True)
  434. for t1, t2 in zip(tot1, tot2):
  435. t1, t2 = psutil._cpu_busy_time(t1), psutil._cpu_busy_time(t2)
  436. difference = t2 - t1
  437. if difference >= 0.05:
  438. return None
  439. @pytest.mark.skipif(
  440. CI_TESTING and OPENBSD, reason="unreliable on OPENBSD + CI"
  441. )
  442. @retry_on_failure(30)
  443. def test_cpu_times_comparison(self):
  444. # Make sure the sum of all per cpu times is almost equal to
  445. # base "one cpu" times. On OpenBSD the sum of per-CPUs is
  446. # higher for some reason.
  447. base = psutil.cpu_times()
  448. per_cpu = psutil.cpu_times(percpu=True)
  449. summed_values = base._make([sum(num) for num in zip(*per_cpu)])
  450. for field in base._fields:
  451. with self.subTest(field=field, base=base, per_cpu=per_cpu):
  452. assert (
  453. abs(getattr(base, field) - getattr(summed_values, field))
  454. < 2
  455. )
  456. def _test_cpu_percent(self, percent, last_ret, new_ret):
  457. try:
  458. assert isinstance(percent, float)
  459. assert percent >= 0.0
  460. assert percent <= 100.0 * psutil.cpu_count()
  461. except AssertionError as err:
  462. raise AssertionError(
  463. "\n{}\nlast={}\nnew={}".format(
  464. err, pprint.pformat(last_ret), pprint.pformat(new_ret)
  465. )
  466. )
  467. def test_cpu_percent(self):
  468. last = psutil.cpu_percent(interval=0.001)
  469. for _ in range(100):
  470. new = psutil.cpu_percent(interval=None)
  471. self._test_cpu_percent(new, last, new)
  472. last = new
  473. with pytest.raises(ValueError):
  474. psutil.cpu_percent(interval=-1)
  475. def test_per_cpu_percent(self):
  476. last = psutil.cpu_percent(interval=0.001, percpu=True)
  477. assert len(last) == psutil.cpu_count()
  478. for _ in range(100):
  479. new = psutil.cpu_percent(interval=None, percpu=True)
  480. for percent in new:
  481. self._test_cpu_percent(percent, last, new)
  482. last = new
  483. with pytest.raises(ValueError):
  484. psutil.cpu_percent(interval=-1, percpu=True)
  485. def test_cpu_times_percent(self):
  486. last = psutil.cpu_times_percent(interval=0.001)
  487. for _ in range(100):
  488. new = psutil.cpu_times_percent(interval=None)
  489. for percent in new:
  490. self._test_cpu_percent(percent, last, new)
  491. self._test_cpu_percent(sum(new), last, new)
  492. last = new
  493. with pytest.raises(ValueError):
  494. psutil.cpu_times_percent(interval=-1)
  495. def test_per_cpu_times_percent(self):
  496. last = psutil.cpu_times_percent(interval=0.001, percpu=True)
  497. assert len(last) == psutil.cpu_count()
  498. for _ in range(100):
  499. new = psutil.cpu_times_percent(interval=None, percpu=True)
  500. for cpu in new:
  501. for percent in cpu:
  502. self._test_cpu_percent(percent, last, new)
  503. self._test_cpu_percent(sum(cpu), last, new)
  504. last = new
  505. def test_per_cpu_times_percent_negative(self):
  506. # see: https://github.com/giampaolo/psutil/issues/645
  507. psutil.cpu_times_percent(percpu=True)
  508. zero_times = [
  509. x._make([0 for x in range(len(x._fields))])
  510. for x in psutil.cpu_times(percpu=True)
  511. ]
  512. with mock.patch('psutil.cpu_times', return_value=zero_times):
  513. for cpu in psutil.cpu_times_percent(percpu=True):
  514. for percent in cpu:
  515. self._test_cpu_percent(percent, None, None)
  516. def test_cpu_stats(self):
  517. # Tested more extensively in per-platform test modules.
  518. infos = psutil.cpu_stats()
  519. assert infos._fields == (
  520. 'ctx_switches',
  521. 'interrupts',
  522. 'soft_interrupts',
  523. 'syscalls',
  524. )
  525. for name in infos._fields:
  526. value = getattr(infos, name)
  527. assert value >= 0
  528. # on AIX, ctx_switches is always 0
  529. if not AIX and name in {'ctx_switches', 'interrupts'}:
  530. assert value > 0
  531. # TODO: remove this once 1892 is fixed
  532. @pytest.mark.skipif(
  533. MACOS and platform.machine() == 'arm64', reason="skipped due to #1892"
  534. )
  535. @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported")
  536. def test_cpu_freq(self):
  537. def check_ls(ls):
  538. for nt in ls:
  539. assert nt._fields == ('current', 'min', 'max')
  540. if nt.max != 0.0:
  541. assert nt.current <= nt.max
  542. for name in nt._fields:
  543. value = getattr(nt, name)
  544. assert isinstance(value, (int, float))
  545. assert value >= 0
  546. ls = psutil.cpu_freq(percpu=True)
  547. if (FREEBSD or AARCH64) and not ls:
  548. raise pytest.skip(
  549. "returns empty list on FreeBSD and Linux aarch64"
  550. )
  551. assert ls, ls
  552. check_ls([psutil.cpu_freq(percpu=False)])
  553. if LINUX:
  554. assert len(ls) == psutil.cpu_count()
  555. @pytest.mark.skipif(not HAS_GETLOADAVG, reason="not supported")
  556. def test_getloadavg(self):
  557. loadavg = psutil.getloadavg()
  558. assert len(loadavg) == 3
  559. for load in loadavg:
  560. assert isinstance(load, float)
  561. assert load >= 0.0
  562. class TestDiskAPIs(PsutilTestCase):
  563. @pytest.mark.skipif(
  564. PYPY and not IS_64BIT, reason="unreliable on PYPY32 + 32BIT"
  565. )
  566. def test_disk_usage(self):
  567. usage = psutil.disk_usage(os.getcwd())
  568. assert usage._fields == ('total', 'used', 'free', 'percent')
  569. assert usage.total > 0, usage
  570. assert usage.used > 0, usage
  571. assert usage.free > 0, usage
  572. assert usage.total > usage.used, usage
  573. assert usage.total > usage.free, usage
  574. assert 0 <= usage.percent <= 100, usage.percent
  575. if hasattr(shutil, 'disk_usage'):
  576. # py >= 3.3, see: http://bugs.python.org/issue12442
  577. shutil_usage = shutil.disk_usage(os.getcwd())
  578. tolerance = 5 * 1024 * 1024 # 5MB
  579. assert usage.total == shutil_usage.total
  580. assert abs(usage.free - shutil_usage.free) < tolerance
  581. if not MACOS_12PLUS:
  582. # see https://github.com/giampaolo/psutil/issues/2147
  583. assert abs(usage.used - shutil_usage.used) < tolerance
  584. # if path does not exist OSError ENOENT is expected across
  585. # all platforms
  586. fname = self.get_testfn()
  587. with pytest.raises(FileNotFoundError):
  588. psutil.disk_usage(fname)
  589. @pytest.mark.skipif(not ASCII_FS, reason="not an ASCII fs")
  590. def test_disk_usage_unicode(self):
  591. # See: https://github.com/giampaolo/psutil/issues/416
  592. with pytest.raises(UnicodeEncodeError):
  593. psutil.disk_usage(UNICODE_SUFFIX)
  594. def test_disk_usage_bytes(self):
  595. psutil.disk_usage(b'.')
  596. def test_disk_partitions(self):
  597. def check_ntuple(nt):
  598. assert isinstance(nt.device, str)
  599. assert isinstance(nt.mountpoint, str)
  600. assert isinstance(nt.fstype, str)
  601. assert isinstance(nt.opts, str)
  602. # all = False
  603. ls = psutil.disk_partitions(all=False)
  604. assert ls
  605. for disk in ls:
  606. check_ntuple(disk)
  607. if WINDOWS and 'cdrom' in disk.opts:
  608. continue
  609. if not POSIX:
  610. assert os.path.exists(disk.device), disk
  611. else:
  612. # we cannot make any assumption about this, see:
  613. # http://goo.gl/p9c43
  614. disk.device # noqa: B018
  615. # on modern systems mount points can also be files
  616. assert os.path.exists(disk.mountpoint), disk
  617. assert disk.fstype, disk
  618. # all = True
  619. ls = psutil.disk_partitions(all=True)
  620. assert ls
  621. for disk in psutil.disk_partitions(all=True):
  622. check_ntuple(disk)
  623. if not WINDOWS and disk.mountpoint:
  624. try:
  625. os.stat(disk.mountpoint)
  626. except OSError as err:
  627. if GITHUB_ACTIONS and MACOS and err.errno == errno.EIO:
  628. continue
  629. # http://mail.python.org/pipermail/python-dev/
  630. # 2012-June/120787.html
  631. if err.errno not in {errno.EPERM, errno.EACCES}:
  632. raise
  633. else:
  634. assert os.path.exists(disk.mountpoint), disk
  635. # ---
  636. def find_mount_point(path):
  637. path = os.path.abspath(path)
  638. while not os.path.ismount(path):
  639. path = os.path.dirname(path)
  640. return path.lower()
  641. mount = find_mount_point(__file__)
  642. mounts = [
  643. x.mountpoint.lower()
  644. for x in psutil.disk_partitions(all=True)
  645. if x.mountpoint
  646. ]
  647. assert mount in mounts
  648. @pytest.mark.skipif(
  649. LINUX and not os.path.exists('/proc/diskstats'),
  650. reason="/proc/diskstats not available on this linux version",
  651. )
  652. @pytest.mark.skipif(
  653. CI_TESTING and not psutil.disk_io_counters(), reason="unreliable on CI"
  654. ) # no visible disks
  655. def test_disk_io_counters(self):
  656. def check_ntuple(nt):
  657. assert nt[0] == nt.read_count
  658. assert nt[1] == nt.write_count
  659. assert nt[2] == nt.read_bytes
  660. assert nt[3] == nt.write_bytes
  661. if not (OPENBSD or NETBSD):
  662. assert nt[4] == nt.read_time
  663. assert nt[5] == nt.write_time
  664. if LINUX:
  665. assert nt[6] == nt.read_merged_count
  666. assert nt[7] == nt.write_merged_count
  667. assert nt[8] == nt.busy_time
  668. elif FREEBSD:
  669. assert nt[6] == nt.busy_time
  670. for name in nt._fields:
  671. assert getattr(nt, name) >= 0, nt
  672. ret = psutil.disk_io_counters(perdisk=False)
  673. assert ret is not None, "no disks on this system?"
  674. check_ntuple(ret)
  675. ret = psutil.disk_io_counters(perdisk=True)
  676. # make sure there are no duplicates
  677. assert len(ret) == len(set(ret))
  678. for key in ret:
  679. assert key, key
  680. check_ntuple(ret[key])
  681. def test_disk_io_counters_no_disks(self):
  682. # Emulate a case where no disks are installed, see:
  683. # https://github.com/giampaolo/psutil/issues/1062
  684. with mock.patch(
  685. 'psutil._psplatform.disk_io_counters', return_value={}
  686. ) as m:
  687. assert psutil.disk_io_counters(perdisk=False) is None
  688. assert psutil.disk_io_counters(perdisk=True) == {}
  689. assert m.called
  690. class TestNetAPIs(PsutilTestCase):
  691. @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported")
  692. def test_net_io_counters(self):
  693. def check_ntuple(nt):
  694. assert nt[0] == nt.bytes_sent
  695. assert nt[1] == nt.bytes_recv
  696. assert nt[2] == nt.packets_sent
  697. assert nt[3] == nt.packets_recv
  698. assert nt[4] == nt.errin
  699. assert nt[5] == nt.errout
  700. assert nt[6] == nt.dropin
  701. assert nt[7] == nt.dropout
  702. assert nt.bytes_sent >= 0, nt
  703. assert nt.bytes_recv >= 0, nt
  704. assert nt.packets_sent >= 0, nt
  705. assert nt.packets_recv >= 0, nt
  706. assert nt.errin >= 0, nt
  707. assert nt.errout >= 0, nt
  708. assert nt.dropin >= 0, nt
  709. assert nt.dropout >= 0, nt
  710. ret = psutil.net_io_counters(pernic=False)
  711. check_ntuple(ret)
  712. ret = psutil.net_io_counters(pernic=True)
  713. assert ret != []
  714. for key in ret:
  715. assert key
  716. assert isinstance(key, str)
  717. check_ntuple(ret[key])
  718. @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported")
  719. def test_net_io_counters_no_nics(self):
  720. # Emulate a case where no NICs are installed, see:
  721. # https://github.com/giampaolo/psutil/issues/1062
  722. with mock.patch(
  723. 'psutil._psplatform.net_io_counters', return_value={}
  724. ) as m:
  725. assert psutil.net_io_counters(pernic=False) is None
  726. assert psutil.net_io_counters(pernic=True) == {}
  727. assert m.called
  728. def test_net_if_addrs(self):
  729. nics = psutil.net_if_addrs()
  730. assert nics, nics
  731. nic_stats = psutil.net_if_stats()
  732. # Not reliable on all platforms (net_if_addrs() reports more
  733. # interfaces).
  734. # self.assertEqual(sorted(nics.keys()),
  735. # sorted(psutil.net_io_counters(pernic=True).keys()))
  736. families = {socket.AF_INET, socket.AF_INET6, psutil.AF_LINK}
  737. for nic, addrs in nics.items():
  738. assert isinstance(nic, str)
  739. assert len(set(addrs)) == len(addrs)
  740. for addr in addrs:
  741. assert isinstance(addr.family, int)
  742. assert isinstance(addr.address, str)
  743. assert isinstance(addr.netmask, (str, type(None)))
  744. assert isinstance(addr.broadcast, (str, type(None)))
  745. assert addr.family in families
  746. assert isinstance(addr.family, enum.IntEnum)
  747. if nic_stats[nic].isup:
  748. # Do not test binding to addresses of interfaces
  749. # that are down
  750. if addr.family == socket.AF_INET:
  751. with socket.socket(addr.family) as s:
  752. s.bind((addr.address, 0))
  753. elif addr.family == socket.AF_INET6:
  754. info = socket.getaddrinfo(
  755. addr.address,
  756. 0,
  757. socket.AF_INET6,
  758. socket.SOCK_STREAM,
  759. 0,
  760. socket.AI_PASSIVE,
  761. )[0]
  762. af, socktype, proto, _canonname, sa = info
  763. with socket.socket(af, socktype, proto) as s:
  764. s.bind(sa)
  765. for ip in (
  766. addr.address,
  767. addr.netmask,
  768. addr.broadcast,
  769. addr.ptp,
  770. ):
  771. if ip is not None:
  772. # TODO: skip AF_INET6 for now because I get:
  773. # AddressValueError: Only hex digits permitted in
  774. # u'c6f3%lxcbr0' in u'fe80::c8e0:fff:fe54:c6f3%lxcbr0'
  775. if addr.family != socket.AF_INET6:
  776. check_net_address(ip, addr.family)
  777. # broadcast and ptp addresses are mutually exclusive
  778. if addr.broadcast:
  779. assert addr.ptp is None
  780. elif addr.ptp:
  781. assert addr.broadcast is None
  782. # check broadcast address
  783. if (
  784. addr.broadcast
  785. and addr.netmask
  786. and addr.family in {socket.AF_INET, socket.AF_INET6}
  787. ):
  788. assert addr.broadcast == broadcast_addr(addr)
  789. if BSD or MACOS or SUNOS:
  790. if hasattr(socket, "AF_LINK"):
  791. assert psutil.AF_LINK == socket.AF_LINK
  792. elif LINUX:
  793. assert psutil.AF_LINK == socket.AF_PACKET
  794. elif WINDOWS:
  795. assert psutil.AF_LINK == -1
  796. def test_net_if_addrs_mac_null_bytes(self):
  797. # Simulate that the underlying C function returns an incomplete
  798. # MAC address. psutil is supposed to fill it with null bytes.
  799. # https://github.com/giampaolo/psutil/issues/786
  800. if POSIX:
  801. ret = [('em1', psutil.AF_LINK, '06:3d:29', None, None, None)]
  802. else:
  803. ret = [('em1', -1, '06-3d-29', None, None, None)]
  804. with mock.patch(
  805. 'psutil._psplatform.net_if_addrs', return_value=ret
  806. ) as m:
  807. addr = psutil.net_if_addrs()['em1'][0]
  808. assert m.called
  809. if POSIX:
  810. assert addr.address == '06:3d:29:00:00:00'
  811. else:
  812. assert addr.address == '06-3d-29-00-00-00'
  813. def test_net_if_stats(self):
  814. nics = psutil.net_if_stats()
  815. assert nics, nics
  816. all_duplexes = (
  817. psutil.NIC_DUPLEX_FULL,
  818. psutil.NIC_DUPLEX_HALF,
  819. psutil.NIC_DUPLEX_UNKNOWN,
  820. )
  821. for name, stats in nics.items():
  822. assert isinstance(name, str)
  823. isup, duplex, speed, mtu, flags = stats
  824. assert isinstance(isup, bool)
  825. assert duplex in all_duplexes
  826. assert duplex in all_duplexes
  827. assert speed >= 0
  828. assert mtu >= 0
  829. assert isinstance(flags, str)
  830. @pytest.mark.skipif(
  831. not (LINUX or BSD or MACOS), reason="LINUX or BSD or MACOS specific"
  832. )
  833. def test_net_if_stats_enodev(self):
  834. # See: https://github.com/giampaolo/psutil/issues/1279
  835. with mock.patch(
  836. 'psutil._psutil_posix.net_if_mtu',
  837. side_effect=OSError(errno.ENODEV, ""),
  838. ) as m:
  839. ret = psutil.net_if_stats()
  840. assert ret == {}
  841. assert m.called
  842. class TestSensorsAPIs(PsutilTestCase):
  843. @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported")
  844. def test_sensors_temperatures(self):
  845. temps = psutil.sensors_temperatures()
  846. for name, entries in temps.items():
  847. assert isinstance(name, str)
  848. for entry in entries:
  849. assert isinstance(entry.label, str)
  850. if entry.current is not None:
  851. assert entry.current >= 0
  852. if entry.high is not None:
  853. assert entry.high >= 0
  854. if entry.critical is not None:
  855. assert entry.critical >= 0
  856. @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported")
  857. def test_sensors_temperatures_fahreneit(self):
  858. d = {'coretemp': [('label', 50.0, 60.0, 70.0)]}
  859. with mock.patch(
  860. "psutil._psplatform.sensors_temperatures", return_value=d
  861. ) as m:
  862. temps = psutil.sensors_temperatures(fahrenheit=True)['coretemp'][0]
  863. assert m.called
  864. assert temps.current == 122.0
  865. assert temps.high == 140.0
  866. assert temps.critical == 158.0
  867. @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason="not supported")
  868. @pytest.mark.skipif(not HAS_BATTERY, reason="no battery")
  869. def test_sensors_battery(self):
  870. ret = psutil.sensors_battery()
  871. assert ret.percent >= 0
  872. assert ret.percent <= 100
  873. if ret.secsleft not in {
  874. psutil.POWER_TIME_UNKNOWN,
  875. psutil.POWER_TIME_UNLIMITED,
  876. }:
  877. assert ret.secsleft >= 0
  878. elif ret.secsleft == psutil.POWER_TIME_UNLIMITED:
  879. assert ret.power_plugged
  880. assert isinstance(ret.power_plugged, bool)
  881. @pytest.mark.skipif(not HAS_SENSORS_FANS, reason="not supported")
  882. def test_sensors_fans(self):
  883. fans = psutil.sensors_fans()
  884. for name, entries in fans.items():
  885. assert isinstance(name, str)
  886. for entry in entries:
  887. assert isinstance(entry.label, str)
  888. assert isinstance(entry.current, int)
  889. assert entry.current >= 0