diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d89ced5a6b..7dd130aebf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ # # * Linux # * macOS -# * Windows (disabled) +# * Windows # * FreeBSD # # To skip certain builds see: @@ -20,15 +20,19 @@ on: [push, pull_request] name: build jobs: - # Linux + macOS + Windows Python 3.6+ - py3: - name: ${{ matrix.os }}-py36-plus + # Linux + macOS + Windows CPython 3.6+ + cp36: + name: cp36+, ${{ matrix.os }}, ${{ matrix.archs }} runs-on: ${{ matrix.os }} - timeout-minutes: 20 + timeout-minutes: 30 strategy: fail-fast: false matrix: - os: [ubuntu-20.04, macos-11, windows-2019] + include: + - {os: macos-11, archs: "x86_64 universal2"} + - {os: ubuntu-20.04, archs: "x86_64 i686"} + - {os: ubuntu-20.04, archs: "aarch64"} + - {os: windows-2019, archs: "AMD64 x86"} steps: - name: Cancel previous runs @@ -43,10 +47,16 @@ jobs: cache: pip cache-dependency-path: .github/workflows/build.yml + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + if: matrix.archs == 'aarch64' + - name: Run tests uses: pypa/cibuildwheel@v2.10.1 with: config-file: "./cibuildwheel.toml" + env: + CIBW_ARCHS: ${{ matrix.archs }} - name: Create wheels uses: actions/upload-artifact@v3 @@ -65,9 +75,9 @@ jobs: # Windows cp37+ tests # psutil tests do not like running from a virtualenv with python>=3.7 so # not using cibuildwheel for those. run them "manually" with this job. - windows-py3-test: - name: windows-py3-test-${{ matrix.python }}-${{ matrix.architecture }} - needs: py3 + windows-cp3-test: + name: windows tests ${{ matrix.python }} ${{ matrix.architecture }} + needs: cp36 runs-on: windows-2019 timeout-minutes: 20 strategy: @@ -105,9 +115,9 @@ jobs: python ../psutil/tests/test_memleaks.py shell: bash - # Linux + macOS + Python 2.7 & 3.5 - linux-macos-py2: - name: ${{ matrix.os }}-py27-py35 + # Linux + macOS + CPython 2.7 & 3.5 + linux-macos-cp27-cp35: + name: cp27/cp35, ${{ matrix.os }} runs-on: ${{ matrix.os }} timeout-minutes: 20 strategy: diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index f6113c5eb9..21700509b4 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -86,6 +86,7 @@ "HAS_IONICE", "HAS_MEMORY_MAPS", "HAS_PROC_CPU_NUM", "HAS_RLIMIT", "HAS_SENSORS_BATTERY", "HAS_BATTERY", "HAS_SENSORS_FANS", "HAS_SENSORS_TEMPERATURES", "HAS_MEMORY_FULL_INFO", "MACOS_11PLUS", + "QEMU_USER", # subprocesses 'pyrun', 'terminate', 'reap_children', 'spawn_testproc', 'spawn_zombie', 'spawn_children_pair', @@ -126,6 +127,7 @@ APPVEYOR = 'APPVEYOR' in os.environ GITHUB_ACTIONS = 'GITHUB_ACTIONS' in os.environ or 'CIBUILDWHEEL' in os.environ CI_TESTING = APPVEYOR or GITHUB_ACTIONS +QEMU_USER = LINUX and GITHUB_ACTIONS and platform.machine() == 'aarch64' # are we a 64 bit process? IS_64BIT = sys.maxsize > 2 ** 32 if MACOS: diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index f25cb9fa6f..4e14d636ff 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -42,6 +42,7 @@ from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES from psutil.tests import PYPY +from psutil.tests import QEMU_USER from psutil.tests import SKIP_SYSCONS from psutil.tests import VALID_PROC_STATUSES from psutil.tests import PsutilTestCase @@ -277,6 +278,7 @@ def test_net_if_addrs(self): self.assertIsInstance(addr.netmask, (str, type(None))) self.assertIsInstance(addr.broadcast, (str, type(None))) + @unittest.skipIf(QEMU_USER, 'QEMU user not supported') def test_net_if_stats(self): # Duplicate of test_system.py. Keep it anyway. for ifname, info in psutil.net_if_stats().items(): @@ -424,6 +426,9 @@ def test_all(self): failures = [] for info in self.iter_proc_info(): for name, value in info.items(): + if QEMU_USER and name == 'status': + # @unittest.skipIf(QEMU_USER, "QEMU user not supported") + continue meth = getattr(self, name) try: meth(value, info) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index c53dfd8a01..5fdfb7d479 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -36,6 +36,7 @@ from psutil.tests import HAS_GETLOADAVG from psutil.tests import HAS_RLIMIT from psutil.tests import PYPY +from psutil.tests import QEMU_USER from psutil.tests import TOLERANCE_DISK_USAGE from psutil.tests import TOLERANCE_SYS_MEM from psutil.tests import PsutilTestCase @@ -974,6 +975,7 @@ def test_ips(self): @unittest.skipIf(not LINUX, "LINUX only") +@unittest.skipIf(QEMU_USER, "QEMU user not supported") class TestSystemNetIfStats(PsutilTestCase): @unittest.skipIf(not which("ifconfig"), "ifconfig utility not available") @@ -1537,7 +1539,7 @@ def test_issue_687(self): with ThreadTask(): p = psutil.Process() threads = p.threads() - self.assertEqual(len(threads), 2) + self.assertEqual(len(threads), 3 if QEMU_USER else 2) tid = sorted(threads, key=lambda x: x.id)[1].id self.assertNotEqual(p.pid, tid) pt = psutil.Process(tid) @@ -2219,6 +2221,7 @@ def test_name(self): value = self.read_status_file("Name:") self.assertEqual(self.proc.name(), value) + @unittest.skipIf(QEMU_USER, "QEMU user not supported") def test_status(self): value = self.read_status_file("State:") value = value[value.find('(') + 1:value.rfind(')')] diff --git a/psutil/tests/test_memleaks.py b/psutil/tests/test_memleaks.py index e507e837c2..9199a7f6b2 100755 --- a/psutil/tests/test_memleaks.py +++ b/psutil/tests/test_memleaks.py @@ -43,6 +43,7 @@ from psutil.tests import HAS_SENSORS_BATTERY from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES +from psutil.tests import QEMU_USER from psutil.tests import TestMemoryLeak from psutil.tests import create_sockets from psutil.tests import get_testfn @@ -393,6 +394,7 @@ def test_disk_usage(self): times = FEW_TIMES if POSIX else self.times self.execute(lambda: psutil.disk_usage('.'), times=times) + @unittest.skipIf(QEMU_USER, "QEMU user not supported") def test_disk_partitions(self): self.execute(psutil.disk_partitions) @@ -428,6 +430,7 @@ def test_net_if_addrs(self): tolerance = 80 * 1024 if WINDOWS else self.tolerance self.execute(psutil.net_if_addrs, tolerance=tolerance) + @unittest.skipIf(QEMU_USER, "QEMU user not supported") def test_net_if_stats(self): self.execute(psutil.net_if_stats) diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py index ebbf7a6e88..aafad29655 100755 --- a/psutil/tests/test_posix.py +++ b/psutil/tests/test_posix.py @@ -26,6 +26,7 @@ from psutil.tests import CI_TESTING from psutil.tests import HAS_NET_IO_COUNTERS from psutil.tests import PYTHON_EXE +from psutil.tests import QEMU_USER from psutil.tests import PsutilTestCase from psutil.tests import mock from psutil.tests import retry_on_failure @@ -107,7 +108,11 @@ def ps_name(pid): field = "command" if SUNOS: field = "comm" - return ps(field, pid).split()[0] + command = ps(field, pid).split() + if QEMU_USER: + assert "/bin/qemu-" in command[0] + return command[1] + return command[0] def ps_args(pid): diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index b69678b9c3..525dbb6cb7 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -52,6 +52,7 @@ from psutil.tests import MACOS_11PLUS from psutil.tests import PYPY from psutil.tests import PYTHON_EXE +from psutil.tests import QEMU_USER from psutil.tests import PsutilTestCase from psutil.tests import ThreadTask from psutil.tests import call_until @@ -243,6 +244,7 @@ def test_cpu_percent_numcpus_none(self): psutil.Process().cpu_percent() assert m.called + @unittest.skipIf(QEMU_USER, "QEMU user not supported") def test_cpu_times(self): times = psutil.Process().cpu_times() assert (times.user > 0.0) or (times.system > 0.0), times @@ -254,6 +256,7 @@ def test_cpu_times(self): for name in times._fields: time.strftime("%H:%M:%S", time.localtime(getattr(times, name))) + @unittest.skipIf(QEMU_USER, "QEMU user not supported") def test_cpu_times_2(self): user_time, kernel_time = psutil.Process().cpu_times()[:2] utime, ktime = os.times()[:2] @@ -616,6 +619,8 @@ def test_memory_maps(self): for nt in maps: if not nt.path.startswith('['): + if QEMU_USER and "/bin/qemu-" in nt.path: + continue assert os.path.isabs(nt.path), nt.path if POSIX: try: @@ -679,6 +684,7 @@ def test_is_running(self): assert not p.is_running() assert not p.is_running() + @unittest.skipIf(QEMU_USER, "QEMU user not supported") def test_exe(self): p = self.spawn_psproc() exe = p.exe() @@ -724,6 +730,9 @@ def test_cmdline(self): self.assertEqual(' '.join(p.cmdline()[1:]), ' '.join(cmdline[1:])) return + if QEMU_USER: + self.assertEqual(' '.join(p.cmdline()[2:]), ' '.join(cmdline)) + return self.assertEqual(' '.join(p.cmdline()), ' '.join(cmdline)) @unittest.skipIf(PYPY, "broken on PYPY") @@ -732,6 +741,9 @@ def test_long_cmdline(self): create_exe(testfn) cmdline = [testfn] + (["0123456789"] * 20) p = self.spawn_psproc(cmdline) + if QEMU_USER: + self.assertEqual(p.cmdline()[2:], cmdline) + return self.assertEqual(p.cmdline(), cmdline) def test_name(self): @@ -740,7 +752,8 @@ def test_name(self): pyexe = os.path.basename(os.path.realpath(sys.executable)).lower() assert pyexe.startswith(name), (pyexe, name) - @unittest.skipIf(PYPY, "unreliable on PYPY") + @unittest.skipIf(PYPY or QEMU_USER, "unreliable on PYPY") + @unittest.skipIf(QEMU_USER, "unreliable on QEMU user") def test_long_name(self): testfn = self.get_testfn(suffix="0123456789" * 2) create_exe(testfn) @@ -751,6 +764,7 @@ def test_long_name(self): @unittest.skipIf(SUNOS, "broken on SUNOS") @unittest.skipIf(AIX, "broken on AIX") @unittest.skipIf(PYPY, "broken on PYPY") + @unittest.skipIf(QEMU_USER, "broken on QEMU user") def test_prog_w_funky_name(self): # Test that name(), exe() and cmdline() correctly handle programs # with funky chars such as spaces and ")", see: @@ -848,6 +862,7 @@ def test_nice(self): except psutil.AccessDenied: pass + @unittest.skipIf(QEMU_USER, "QEMU user not supported") def test_status(self): p = psutil.Process() self.assertEqual(p.status(), psutil.STATUS_RUNNING) @@ -1073,6 +1088,7 @@ def test_parent_disappeared(self): side_effect=psutil.NoSuchProcess(0, 'foo')): self.assertIsNone(p.parent()) + @unittest.skipIf(QEMU_USER, "QEMU user not supported") @retry_on_failure() def test_parents(self): parent = psutil.Process() diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index d6b7a21a4e..02dbcae01d 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -45,6 +45,7 @@ from psutil.tests import HAS_SENSORS_TEMPERATURES from psutil.tests import IS_64BIT from psutil.tests import PYPY +from psutil.tests import QEMU_USER from psutil.tests import UNICODE_SUFFIX from psutil.tests import PsutilTestCase from psutil.tests import check_net_address @@ -723,6 +724,7 @@ def test_net_io_counters_no_nics(self): self.assertEqual(psutil.net_io_counters(pernic=True), {}) assert m.called + @unittest.skipIf(QEMU_USER, 'QEMU user not supported') def test_net_if_addrs(self): nics = psutil.net_if_addrs() assert nics, nics @@ -800,6 +802,7 @@ def test_net_if_addrs_mac_null_bytes(self): else: self.assertEqual(addr.address, '06-3d-29-00-00-00') + @unittest.skipIf(QEMU_USER, 'QEMU user not supported') def test_net_if_stats(self): nics = psutil.net_if_stats() assert nics, nics