mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-08-04 14:46:03 +00:00
100 lines
3.9 KiB
Python
100 lines
3.9 KiB
Python
#!/usr/bin/env python
|
|
# License: GPL v3 Copyright: 2026, Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
from kitty.child import memory_used_by_process_tree_rooted_at
|
|
from kitty.constants import is_macos, kitty_exe
|
|
|
|
from . import BaseTest
|
|
|
|
|
|
class ChildMemoryTest(BaseTest):
|
|
|
|
def _spawn_allocating_child(self, alloc_bytes: int) -> subprocess.Popen:
|
|
p = subprocess.Popen(
|
|
[kitty_exe(), '+runpy', f'''\
|
|
import sys, time
|
|
buf = bytearray({alloc_bytes})
|
|
for i in range(0, {alloc_bytes}, 4096):
|
|
buf[i] = 1
|
|
sys.stdout.write("ready\\n")
|
|
sys.stdout.flush()
|
|
time.sleep(300)
|
|
'''],
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
line = p.stdout.readline().strip()
|
|
p.stdout.close()
|
|
if line != b'ready':
|
|
p.kill()
|
|
p.wait()
|
|
raise AssertionError(f'Unexpected output from allocating child: {line!r}')
|
|
return p
|
|
|
|
def _terminate(self, p: subprocess.Popen) -> None:
|
|
p.terminate()
|
|
p.wait()
|
|
|
|
def test_memory_returns_positive_for_live_process(self):
|
|
mem = memory_used_by_process_tree_rooted_at(os.getpid())
|
|
self.assertGreater(mem, 0)
|
|
|
|
def test_memory_returns_minus_one_for_nonexistent_pid(self):
|
|
self.ae(memory_used_by_process_tree_rooted_at(99999999), -1)
|
|
|
|
def test_memory_accounts_for_child_allocation(self):
|
|
# Verify that a child's known resident allocation shows up in the
|
|
# measurement. check_if_cgroup_root=True falls back to per-process
|
|
# tree walk when pid is not the cgroup root (the common case when
|
|
# running under a shared session cgroup), so this exercises the tree
|
|
# walk path on Linux and the always-tree-walk path on macOS.
|
|
alloc = 20 * 1024 * 1024 # 20 MiB
|
|
child = self._spawn_allocating_child(alloc)
|
|
try:
|
|
mem = memory_used_by_process_tree_rooted_at(child.pid, check_if_cgroup_root=True)
|
|
self.assertGreater(
|
|
mem, alloc // 2,
|
|
f'Expected at least {alloc // 2} bytes for a {alloc}-byte allocation, got {mem}',
|
|
)
|
|
finally:
|
|
self._terminate(child)
|
|
|
|
def test_memory_of_parent_tree_includes_child(self):
|
|
# Measuring the current process tree must yield more than measuring
|
|
# the child alone, because the test runner itself occupies memory.
|
|
alloc = 20 * 1024 * 1024 # 20 MiB
|
|
child = self._spawn_allocating_child(alloc)
|
|
try:
|
|
mem_child = memory_used_by_process_tree_rooted_at(child.pid, check_if_cgroup_root=True)
|
|
mem_tree = memory_used_by_process_tree_rooted_at(os.getpid(), check_if_cgroup_root=True)
|
|
self.assertGreater(
|
|
mem_tree, mem_child,
|
|
'Parent tree memory should exceed child-only memory',
|
|
)
|
|
finally:
|
|
self._terminate(child)
|
|
|
|
def test_memory_cgroup_path_returns_positive(self):
|
|
# The fast cgroup path (check_if_cgroup_root=False, the default) must
|
|
# return a usable value on Linux.
|
|
if is_macos:
|
|
self.skipTest('cgroup not available on macOS')
|
|
mem = memory_used_by_process_tree_rooted_at(os.getpid())
|
|
self.assertGreater(mem, 0)
|
|
|
|
def test_memory_cgroup_and_tree_walk_both_positive(self):
|
|
# Both the cgroup path and the tree-walk path should give positive
|
|
# results for a process that is alive.
|
|
if is_macos:
|
|
self.skipTest('cgroup path not applicable on macOS')
|
|
alloc = 10 * 1024 * 1024 # 10 MiB
|
|
child = self._spawn_allocating_child(alloc)
|
|
try:
|
|
mem_cgroup = memory_used_by_process_tree_rooted_at(child.pid, check_if_cgroup_root=False)
|
|
mem_walk = memory_used_by_process_tree_rooted_at(child.pid, check_if_cgroup_root=True)
|
|
self.assertGreater(mem_cgroup, 0)
|
|
self.assertGreater(mem_walk, 0)
|
|
finally:
|
|
self._terminate(child)
|