test_osx.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. """macOS specific tests."""
  6. import re
  7. import time
  8. import psutil
  9. from psutil import MACOS
  10. from psutil.tests import AARCH64
  11. from psutil.tests import CI_TESTING
  12. from psutil.tests import HAS_BATTERY
  13. from psutil.tests import TOLERANCE_DISK_USAGE
  14. from psutil.tests import TOLERANCE_SYS_MEM
  15. from psutil.tests import PsutilTestCase
  16. from psutil.tests import pytest
  17. from psutil.tests import retry_on_failure
  18. from psutil.tests import sh
  19. from psutil.tests import spawn_subproc
  20. from psutil.tests import terminate
  21. def sysctl(cmdline):
  22. """Expects a sysctl command with an argument and parse the result
  23. returning only the value of interest.
  24. """
  25. out = sh(cmdline)
  26. result = out.split()[1]
  27. try:
  28. return int(result)
  29. except ValueError:
  30. return result
  31. def vm_stat(field):
  32. """Wrapper around 'vm_stat' cmdline utility."""
  33. out = sh('vm_stat')
  34. for line in out.split('\n'):
  35. if field in line:
  36. break
  37. else:
  38. raise ValueError("line not found")
  39. return (
  40. int(re.search(r'\d+', line).group(0))
  41. * psutil._psplatform.cext.getpagesize()
  42. )
  43. @pytest.mark.skipif(not MACOS, reason="MACOS only")
  44. class TestProcess(PsutilTestCase):
  45. @classmethod
  46. def setUpClass(cls):
  47. cls.pid = spawn_subproc().pid
  48. @classmethod
  49. def tearDownClass(cls):
  50. terminate(cls.pid)
  51. def test_process_create_time(self):
  52. output = sh(f"ps -o lstart -p {self.pid}")
  53. start_ps = output.replace('STARTED', '').strip()
  54. hhmmss = start_ps.split(' ')[-2]
  55. year = start_ps.split(' ')[-1]
  56. start_psutil = psutil.Process(self.pid).create_time()
  57. assert hhmmss == time.strftime(
  58. "%H:%M:%S", time.localtime(start_psutil)
  59. )
  60. assert year == time.strftime("%Y", time.localtime(start_psutil))
  61. @pytest.mark.skipif(not MACOS, reason="MACOS only")
  62. class TestSystemAPIs(PsutilTestCase):
  63. # --- disk
  64. @retry_on_failure()
  65. def test_disks(self):
  66. # test psutil.disk_usage() and psutil.disk_partitions()
  67. # against "df -a"
  68. def df(path):
  69. out = sh(f'df -k "{path}"').strip()
  70. lines = out.split('\n')
  71. lines.pop(0)
  72. line = lines.pop(0)
  73. dev, total, used, free = line.split()[:4]
  74. if dev == 'none':
  75. dev = ''
  76. total = int(total) * 1024
  77. used = int(used) * 1024
  78. free = int(free) * 1024
  79. return dev, total, used, free
  80. for part in psutil.disk_partitions(all=False):
  81. usage = psutil.disk_usage(part.mountpoint)
  82. dev, total, used, free = df(part.mountpoint)
  83. assert part.device == dev
  84. assert usage.total == total
  85. assert abs(usage.free - free) < TOLERANCE_DISK_USAGE
  86. assert abs(usage.used - used) < TOLERANCE_DISK_USAGE
  87. # --- cpu
  88. def test_cpu_count_logical(self):
  89. num = sysctl("sysctl hw.logicalcpu")
  90. assert num == psutil.cpu_count(logical=True)
  91. def test_cpu_count_cores(self):
  92. num = sysctl("sysctl hw.physicalcpu")
  93. assert num == psutil.cpu_count(logical=False)
  94. # TODO: remove this once 1892 is fixed
  95. @pytest.mark.skipif(MACOS and AARCH64, reason="skipped due to #1892")
  96. def test_cpu_freq(self):
  97. freq = psutil.cpu_freq()
  98. assert freq.current * 1000 * 1000 == sysctl("sysctl hw.cpufrequency")
  99. assert freq.min * 1000 * 1000 == sysctl("sysctl hw.cpufrequency_min")
  100. assert freq.max * 1000 * 1000 == sysctl("sysctl hw.cpufrequency_max")
  101. # --- virtual mem
  102. def test_vmem_total(self):
  103. sysctl_hwphymem = sysctl('sysctl hw.memsize')
  104. assert sysctl_hwphymem == psutil.virtual_memory().total
  105. @pytest.mark.skipif(
  106. CI_TESTING and MACOS and AARCH64,
  107. reason="skipped on MACOS + ARM64 + CI_TESTING",
  108. )
  109. @retry_on_failure()
  110. def test_vmem_free(self):
  111. vmstat_val = vm_stat("free")
  112. psutil_val = psutil.virtual_memory().free
  113. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  114. @pytest.mark.skipif(
  115. CI_TESTING and MACOS and AARCH64,
  116. reason="skipped on MACOS + ARM64 + CI_TESTING",
  117. )
  118. @retry_on_failure()
  119. def test_vmem_active(self):
  120. vmstat_val = vm_stat("active")
  121. psutil_val = psutil.virtual_memory().active
  122. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  123. # XXX: fails too often
  124. @pytest.mark.skipif(CI_TESTING, reason="skipped on CI_TESTING")
  125. @retry_on_failure()
  126. def test_vmem_inactive(self):
  127. vmstat_val = vm_stat("inactive")
  128. psutil_val = psutil.virtual_memory().inactive
  129. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  130. @retry_on_failure()
  131. def test_vmem_wired(self):
  132. vmstat_val = vm_stat("wired")
  133. psutil_val = psutil.virtual_memory().wired
  134. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  135. # --- swap mem
  136. @retry_on_failure()
  137. def test_swapmem_sin(self):
  138. vmstat_val = vm_stat("Pageins")
  139. psutil_val = psutil.swap_memory().sin
  140. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  141. @retry_on_failure()
  142. def test_swapmem_sout(self):
  143. vmstat_val = vm_stat("Pageout")
  144. psutil_val = psutil.swap_memory().sout
  145. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  146. # --- network
  147. def test_net_if_stats(self):
  148. for name, stats in psutil.net_if_stats().items():
  149. try:
  150. out = sh(f"ifconfig {name}")
  151. except RuntimeError:
  152. pass
  153. else:
  154. assert stats.isup == ('RUNNING' in out), out
  155. assert stats.mtu == int(re.findall(r'mtu (\d+)', out)[0])
  156. # --- sensors_battery
  157. @pytest.mark.skipif(not HAS_BATTERY, reason="no battery")
  158. def test_sensors_battery(self):
  159. out = sh("pmset -g batt")
  160. percent = re.search(r"(\d+)%", out).group(1)
  161. drawing_from = re.search(r"Now drawing from '([^']+)'", out).group(1)
  162. power_plugged = drawing_from == "AC Power"
  163. psutil_result = psutil.sensors_battery()
  164. assert psutil_result.power_plugged == power_plugged
  165. assert psutil_result.percent == int(percent)
  166. # --- others
  167. def test_boot_time(self):
  168. out = sh('sysctl kern.boottime')
  169. a = float(re.search(r"sec\s*=\s*(\d+)", out).groups(0)[0])
  170. b = psutil.boot_time()
  171. assert a == b