VulpesOS/tundra/tests/test_ramdisk.py
OverSu 1311bed5e9 Release Preview 20 shared desktop, Android and Sargo updates
Restore Gaia settings and media workflows, extend native network adapters, and align the APK payload. Generate French/English changelogs and website entries from the simplified JSON source.

Validation: shared JS and Tundra unit tests; desktop workflows and persistence; signed APK payload parity; Sargo RAM and installed startup with boot readback. Android runtime qualification is recorded separately.
2026-10-13 00:19:45 +02:00

146 lines
7.4 KiB
Python

# SPDX-License-Identifier: MPL-2.0
import importlib.util
from pathlib import Path
import stat
import sys
import unittest
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/'tools'))
from ramdisk_reference import read_newc, resolve
from tundra import cpio
spec=importlib.util.spec_from_file_location('hal_policy',ROOT/'runtime/hal_policy.py')
policy=importlib.util.module_from_spec(spec);spec.loader.exec_module(policy)
class BootTests(unittest.TestCase):
def test_cpio_roundtrip_and_absolute_library_symlink(self):
data=cpio([('bin',stat.S_IFDIR|0o755,b'',0,0),('bin/tool',stat.S_IFREG|0o755,b'ELF',0,0),('bin/link',stat.S_IFLNK|0o777,b'/bin/tool',0,0)])
entries=read_newc(data)
self.assertEqual(resolve(entries,'bin/link'),b'ELF')
def test_hardlinked_busybox_payload_is_not_lost(self):
import gzip
data=bytearray(gzip.decompress(cpio([
('bin/busybox',stat.S_IFREG|0o755,b'',0,0),
('bin/last-applet',stat.S_IFREG|0o755,b'ELF payload',0,0)])))
offset=0
for _ in range(2):
fields=[int(data[offset+6+i*8:offset+14+i*8],16) for i in range(13)]
data[offset+6:offset+14]=b'00000007'
data[offset+38:offset+46]=b'00000002'
start=(offset+110+fields[11]+3)&~3
offset=(start+fields[6]+3)&~3
self.assertEqual(resolve(read_newc(bytes(data)),'bin/busybox'),b'ELF payload')
def test_cpio_rejects_invalid_header(self):
with self.assertRaises(ValueError):read_newc(b'x'*111)
def test_hal_keeps_display_but_removes_app_telemetry_and_flash_services(self):
text='''# upstream copyright
service vendor.hwcomposer-2-2 /vendor/bin/hw/composer
class hal animation
onrestart restart surfaceflinger
service vendor.pixelstats_vendor /vendor/bin/pixelstats
class hal
service flash_recovery /system/bin/install-recovery.sh
class main
on boot
start flash_recovery
start vendor.pixelstats_vendor
start vendor.hwcomposer-2-2
mount_all /vendor/etc/fstab.sargo
exec -- /system/bin/sh /vendor/bin/mutate.sh
write /dev/block/by-name/boot_a unsafe
write /sys/class/android_usb/android0/enable 1
write /dev/cpuset/cpus 0-7
'''
output,report=policy.filter_rc(text)
self.assertIn('start vendor.hwcomposer-2-2',output)
self.assertIn('# upstream copyright',output)
self.assertIn('write /dev/cpuset/cpus',output)
for forbidden in ('pixelstats','flash_recovery','mount_all','exec --','/dev/block','android_usb','restart surfaceflinger'):
self.assertNotIn(forbidden,output)
self.assertEqual(report['services'],['vendor.hwcomposer-2-2'])
def test_android_multiline_commands_preserve_their_section(self):
source = 'service zygote /system/bin/app_process \\\n --zygote\non boot\n write /dev/event-log-tags "# owned by logd\n"\n'
output, report = policy.filter_rc(source)
self.assertNotIn('app_process', output)
self.assertIn('write /dev/event-log-tags "# owned by logd\n"', output)
def test_ueventd_permissions_are_not_parsed_as_init_commands(self):
for name in ('ueventd.rc','ueventd.sargo.rc','ueventd.qcom.rc'):
self.assertFalse(policy.is_init_script(Path('/android/vendor')/name))
self.assertTrue(policy.is_init_script(Path('/android/init.sargo.rc')))
def test_trial_service_failure_does_not_request_device_reboot(self):
output,_=policy.filter_rc('service servicemanager /system/bin/servicemanager\n critical\n class core\n')
self.assertNotIn('critical',output)
self.assertIn('class core',output)
def test_hardware_policy_keeps_one_primary_audio_owner(self):
from unittest.mock import patch
source = '''service vendor.audio-hal-2-0 /vendor/bin/hw/android.hardware.audio@2.0-service
class hal
service vendor.camera-provider-2-4 /vendor/bin/hw/android.hardware.camera.provider@2.4-service
class hal
on boot
start vendor.audio-hal-2-0
start vendor.camera-provider-2-4
'''
with patch.object(policy, 'SERVICES', policy.SERVICES | policy.HARDWARE_SERVICES):
output, report = policy.filter_rc(source)
self.assertNotIn('vendor.audio-hal-2-0', output)
self.assertIn('vendor.camera-provider-2-4', report['services'])
def test_display_keeps_its_platform_properties_and_configstore(self):
output,report=policy.filter_rc('on post-fs-data\n load_system_props\nservice vendor.configstore-hal /vendor/bin/hw/android.hardware.configstore@1.1-service\n class hal animation\n user system\n')
self.assertIn('load_system_props',output)
self.assertIn('vendor.configstore-hal',report['services'])
def test_native_config_uses_a_valid_shell_and_denies_block_devices(self):
import tempfile
spec = importlib.util.spec_from_file_location('native_prepare', ROOT/'runtime/prepare-native.py')
native = importlib.util.module_from_spec(spec); spec.loader.exec_module(native)
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
(root/'opt/vulpes/trial').mkdir(parents=True)
(root/'opt/vulpes/trial/session.py').touch()
native.prepare(root)
shells = (root/'etc/shells').read_text().splitlines()
for line in (root/'etc/passwd').read_text().splitlines():
if line.startswith(('root:', 'droidian:')):
self.assertIn(line.split(':')[-1], shells)
config = (root/'etc/tundra/lxc.conf').read_text()
self.assertIn('lxc.cgroup.devices.deny = b *:* rwm', config)
self.assertIn('lxc.cgroup2.devices.deny = b *:* rwm', config)
self.assertNotIn('host', config)
self.assertIn('lxc.cgroup2.memory.max = 536870912', config)
service=(root/'etc/systemd/system/tundra-display.service').read_text()
self.assertIn('ExecStartPre=+chvt 7', service)
self.assertNotIn('ExecStartPost=', service)
# systemd-user's PAM stack includes these on the reference root.
for name in ('common-account', 'common-session-noninteractive'):
self.assertIn('pam_unix.so', (root/'etc/pam.d'/name).read_text())
self.assertIn('pam_deny.so', (root/'etc/pam.d/other').read_text())
def test_hal_denies_indirect_service_start_and_mount_of_block_device(self):
for line in ('setprop ctl.start zygote','setprop ctl.restart vendor.pixelstats_vendor',
'mount ext4 /dev/mmcblk0p72 /data','mount tmpfs /dev/block/x /data'):
self.assertFalse(policy.command_allowed(line.split()),line)
def test_interactive_boot_checks_are_opt_in(self):
import tempfile
spec = importlib.util.spec_from_file_location('native_prepare', ROOT/'runtime/prepare-native.py')
native = importlib.util.module_from_spec(spec); spec.loader.exec_module(native)
for diagnostics in (False, True):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
(root/'opt/vulpes/trial').mkdir(parents=True)
(root/'opt/vulpes/trial/session.py').touch()
native.prepare(root, diagnostics=diagnostics)
service = (root/'etc/systemd/system/tundra-display.service').read_text()
self.assertEqual('--diagnostics' in service, diagnostics)
if __name__=='__main__':unittest.main()