test_osx.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 platform
  7. import re
  8. import time
  9. import psutil
  10. from psutil import MACOS
  11. from psutil import POSIX
  12. from psutil.tests import CI_TESTING
  13. from psutil.tests import HAS_BATTERY
  14. from psutil.tests import TOLERANCE_DISK_USAGE
  15. from psutil.tests import TOLERANCE_SYS_MEM
  16. from psutil.tests import PsutilTestCase
  17. from psutil.tests import pytest
  18. from psutil.tests import retry_on_failure
  19. from psutil.tests import sh
  20. from psutil.tests import spawn_testproc
  21. from psutil.tests import terminate
  22. if POSIX:
  23. from psutil._psutil_posix import getpagesize
  24. def sysctl(cmdline):
  25. """Expects a sysctl command with an argument and parse the result
  26. returning only the value of interest.
  27. """
  28. out = sh(cmdline)
  29. result = out.split()[1]
  30. try:
  31. return int(result)
  32. except ValueError:
  33. return result
  34. def vm_stat(field):
  35. """Wrapper around 'vm_stat' cmdline utility."""
  36. out = sh('vm_stat')
  37. for line in out.split('\n'):
  38. if field in line:
  39. break
  40. else:
  41. raise ValueError("line not found")
  42. return int(re.search(r'\d+', line).group(0)) * getpagesize()
  43. @pytest.mark.skipif(not MACOS, reason="MACOS only")
  44. class TestProcess(PsutilTestCase):
  45. @classmethod
  46. def setUpClass(cls):
  47. cls.pid = spawn_testproc().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(
  96. MACOS and platform.machine() == 'arm64', reason="skipped due to #1892"
  97. )
  98. def test_cpu_freq(self):
  99. freq = psutil.cpu_freq()
  100. assert freq.current * 1000 * 1000 == sysctl("sysctl hw.cpufrequency")
  101. assert freq.min * 1000 * 1000 == sysctl("sysctl hw.cpufrequency_min")
  102. assert freq.max * 1000 * 1000 == sysctl("sysctl hw.cpufrequency_max")
  103. # --- virtual mem
  104. def test_vmem_total(self):
  105. sysctl_hwphymem = sysctl('sysctl hw.memsize')
  106. assert sysctl_hwphymem == psutil.virtual_memory().total
  107. @pytest.mark.skipif(
  108. CI_TESTING and MACOS and platform.machine() == 'arm64',
  109. reason="skipped on MACOS + ARM64 + CI_TESTING",
  110. )
  111. @retry_on_failure()
  112. def test_vmem_free(self):
  113. vmstat_val = vm_stat("free")
  114. psutil_val = psutil.virtual_memory().free
  115. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  116. @retry_on_failure()
  117. def test_vmem_active(self):
  118. vmstat_val = vm_stat("active")
  119. psutil_val = psutil.virtual_memory().active
  120. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  121. @retry_on_failure()
  122. def test_vmem_inactive(self):
  123. vmstat_val = vm_stat("inactive")
  124. psutil_val = psutil.virtual_memory().inactive
  125. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  126. @retry_on_failure()
  127. def test_vmem_wired(self):
  128. vmstat_val = vm_stat("wired")
  129. psutil_val = psutil.virtual_memory().wired
  130. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  131. # --- swap mem
  132. @retry_on_failure()
  133. def test_swapmem_sin(self):
  134. vmstat_val = vm_stat("Pageins")
  135. psutil_val = psutil.swap_memory().sin
  136. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  137. @retry_on_failure()
  138. def test_swapmem_sout(self):
  139. vmstat_val = vm_stat("Pageout")
  140. psutil_val = psutil.swap_memory().sout
  141. assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM
  142. # --- network
  143. def test_net_if_stats(self):
  144. for name, stats in psutil.net_if_stats().items():
  145. try:
  146. out = sh(f"ifconfig {name}")
  147. except RuntimeError:
  148. pass
  149. else:
  150. assert stats.isup == ('RUNNING' in out), out
  151. assert stats.mtu == int(re.findall(r'mtu (\d+)', out)[0])
  152. # --- sensors_battery
  153. @pytest.mark.skipif(not HAS_BATTERY, reason="no battery")
  154. def test_sensors_battery(self):
  155. out = sh("pmset -g batt")
  156. percent = re.search(r"(\d+)%", out).group(1)
  157. drawing_from = re.search(r"Now drawing from '([^']+)'", out).group(1)
  158. power_plugged = drawing_from == "AC Power"
  159. psutil_result = psutil.sensors_battery()
  160. assert psutil_result.power_plugged == power_plugged
  161. assert psutil_result.percent == int(percent)