pmbootstrap/test/test_parse_kconfig.py

466 lines
14 KiB
Python

# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
""" Test pmb/parse/kconfig.py """
import pytest
import sys
import os
import pmb_test # noqa
import pmb.parse.kconfig
test_options_checked_count = None
@pytest.fixture
def args(tmpdir, request):
import pmb.parse
sys.argv = ["pmbootstrap.py", "kconfig", "check"]
args = pmb.parse.arguments()
args.log = args.work + "/log_testsuite.txt"
pmb.helpers.logging.init(args)
request.addfinalizer(pmb.helpers.logging.logfd.close)
return args
def patch_config(monkeypatch):
"""
Delete the real kconfig_options_* variables in pmb/config/__init__.py and
replace them with a very basic config for the tests. The idea is that it
should use all features of the kconfig check code, so we can test all the
code paths.
"""
for key in list(pmb.config.__dict__.keys()):
if key.startswith("kconfig_options"):
monkeypatch.delattr(pmb.config, key)
monkeypatch.setattr(pmb.config, "kconfig_options", {
">=0.0.0": { # all versions
"all": { # all arches
"ANDROID_PARANOID_NETWORK": False,
"BLK_DEV_INITRD": True,
"DEFAULT_HOSTNAME": "(none)",
},
},
">=2.6.0": {
"all": {
"BINFMT_ELF": True,
},
},
"<4.7.0": {
"all": {
"DEVPTS_MULTIPLE_INSTANCES": True,
},
},
"<5.2.0": {
"armhf armv7 x86": {
"LBDAF": True
},
},
}, False)
monkeypatch.setattr(pmb.config, "kconfig_options_waydroid", {
">=0.0.0": {
"all": {
"SQUASHFS": True,
"ANDROID_BINDERFS": False,
"ANDROID_BINDER_DEVICES": ["binder", "hwbinder", "vndbinder"],
}
},
}, False)
monkeypatch.setattr(pmb.config, "kconfig_options_nftables", {
">=3.13.0 <5.17": {
"all": {
"NFT_COUNTER": True,
},
},
}, False)
def test_get_all_component_names(monkeypatch):
patch_config(monkeypatch)
func = pmb.parse.kconfig.get_all_component_names
assert func() == ["waydroid", "nftables"]
def test_is_set():
config = ("CONFIG_WIREGUARD=m\n"
"# CONFIG_EXT2_FS is not set\n"
"CONFIG_EXT4_FS=y\n")
func = pmb.parse.kconfig.is_set
assert func(config, "WIREGUARD") is True
assert func(config, "EXT4_FS") is True
assert func(config, "NON_EXISTING") is False
def test_is_set_str():
config = 'CONFIG_DEFAULT_HOSTNAME="(none)"\n'
func = pmb.parse.kconfig.is_set_str
option = "DEFAULT_HOSTNAME"
assert func(config, option, "(none)") is True
assert func(config, option, "hello") is False
assert func(config, f"{option}_2", "(none)") is False
def test_is_in_array():
config = 'CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder"\n'
func = pmb.parse.kconfig.is_in_array
option = "ANDROID_BINDER_DEVICES"
assert func(config, option, "binder") is True
assert func(config, option, "hwbinder") is True
assert func(config, option, "vndbinder") is True
assert func(config, option, "invalidbinder") is False
assert func(config, f"{option}_2", "binder") is False
def test_check_option():
func = pmb.parse.kconfig.check_option
config = ('CONFIG_BOOL=m\n'
'CONFIG_LIST="a,b,c"\n'
'CONFIG_STR="test"\n')
path = "/home/user/myconfig.aarch64"
assert func("test", False, config, path, "BOOL", True) is True
assert func("test", True, config, path, "BOOL", True) is True
assert func("test", True, config, path, "NON_EXISTING", True) is False
assert func("test", True, config, path, "STR", "test") is True
assert func("test", True, config, path, "STR", "test2") is False
assert func("test", True, config, path, "LIST", ["a"]) is True
assert func("test", True, config, path, "LIST", ["d"]) is False
with pytest.raises(RuntimeError) as e:
func("test", True, config, path, "TEST", {"dict": "notsupported"})
assert "is not supported" in str(e.value)
with pytest.raises(RuntimeError) as e:
func("test", True, config, path, "TEST", None)
assert "is not supported" in str(e.value)
def test_check_config_options_set():
func = pmb.parse.kconfig.check_config_options_set
config = ('CONFIG_BOOL=m\n'
'CONFIG_LIST="a,b,c"\n'
'CONFIG_STR="test"\n')
path = "/home/user/myconfig.aarch64"
arch = "aarch64"
pkgver = "6.0"
component = "testcomponent"
# Skip check because version is too low
options = {
">=6.0.1": {
"all": {
"BOOL": False
}
}
}
assert func(config, path, arch, options, component, pkgver) is True
# Skip check because version is too high
options = {
"<6.0": {
"all": {
"BOOL": False
}
}
}
assert func(config, path, arch, options, component, pkgver) is True
# Skip with two version that don't match
options = {
"<6.2 >=6.0.1": {
"all": {
"BOOL": False
}
}
}
assert func(config, path, arch, options, component, pkgver) is True
# Version matches, arch does not match
options = {
">=6.0": {
"armhf": {
"BOOL": False
}
}
}
assert func(config, path, arch, options, component, pkgver) is True
# Version matches, arch matches (aarch64)
options = {
">=6.0": {
"aarch64": {
"BOOL": False
}
}
}
assert func(config, path, arch, options, component, pkgver) is False
# Version matches, arch matches (all)
options = {
">=6.0": {
"all": {
"BOOL": False
}
}
}
assert func(config, path, arch, options, component, pkgver) is False
# Version matches, arch matches (all), rule passes
options = {
">=6.0": {
"all": {
"BOOL": True
}
}
}
assert func(config, path, arch, options, component, pkgver) is True
def test_check_config_options_set_details(monkeypatch):
global test_options_checked_count
func = pmb.parse.kconfig.check_config_options_set
config = ('CONFIG_BOOL=m\n'
'CONFIG_LIST="a,b,c"\n'
'CONFIG_STR="test"\n')
path = "/home/user/myconfig.aarch64"
arch = "aarch64"
pkgver = "6.0"
component = "testcomponent"
def check_option_fake(*args, **kwargs):
global test_options_checked_count
test_options_checked_count += 1
return False
monkeypatch.setattr(pmb.parse.kconfig, "check_option", check_option_fake)
options = {
">=0.0.0": {
"all": {
"BOOL": False,
"STR": False,
}
}
}
# No details: stop after first error
details = False
test_options_checked_count = 0
assert func(config, path, arch, options, component, pkgver, details) is False
assert test_options_checked_count == 1
# Details: don't stop, do both checks
details = True
test_options_checked_count = 0
assert func(config, path, arch, options, component, pkgver, details) is False
assert test_options_checked_count == 2
def test_check_config(monkeypatch, tmpdir):
# Write test kernel config
tmpdir = str(tmpdir)
path = f"{tmpdir}/myconfig.aarch64"
with open(path, "w") as handle:
handle.write('CONFIG_BOOL=m\n'
'CONFIG_LIST="a,b,c"\n'
'CONFIG_STR="test"\n')
patch_config(monkeypatch)
func = pmb.parse.kconfig.check_config
arch = "aarch64"
pkgver = "6.0"
# Invalid component
components_list = ["invalid-component-name"]
with pytest.raises(AssertionError) as e:
func(path, arch, pkgver, components_list)
assert "invalid kconfig component name" in str(e.value)
# Fail base check
components_list = []
assert func(path, arch, pkgver, components_list) is False
# Fails base check, even with enforce=False
details = False
enforce = False
assert func(path, arch, pkgver, components_list, details, enforce) is False
# Pass base check
with open(path, "w") as handle:
handle.write('CONFIG_BLK_DEV_INITRD=y\n'
'CONFIG_DEFAULT_HOSTNAME="(none)"\n'
'CONFIG_BINFMT_ELF=y\n')
components_list = []
assert func(path, arch, pkgver, components_list) is True
# Fail additional check
components_list = ["waydroid"]
assert func(path, arch, pkgver, components_list) is False
# Fail additional check, but result is still True with enforce=False
components_list = ["waydroid"]
details = True
enforce = False
assert func(path, arch, pkgver, components_list, details, enforce) is True
def test_check(args, monkeypatch, tmpdir):
func = pmb.parse.kconfig.check
details = True
components_list = []
patch_config(monkeypatch)
# Create fake pmaports kernel structure
tmpdir = str(tmpdir)
monkeypatch.setattr(args, "aports", tmpdir)
path_aport = f"{tmpdir}/device/community/linux-nokia-n900"
path_apkbuild = f"{path_aport}/APKBUILD"
os.makedirs(path_aport)
# APKBUILD
with open(path_apkbuild, "w") as handle:
handle.write('pkgname=linux-nokia-n900\n'
'pkgver=5.15\n'
'options="pmb:kconfigcheck-nftables"\n')
# Non-existing #1
must_exist = True
pkgname = "linux-does-not-exist"
with pytest.raises(RuntimeError) as e:
func(args, pkgname, components_list, details, must_exist)
assert "Could not find aport" in str(e.value)
# Non-existing #2
must_exist = False
pkgname = "linux-does-not-exist"
assert func(args, pkgname, components_list, details, must_exist) is None
# Invalid kernel config name
path_kconfig = f"{path_aport}/config-nokia-n900_armv7"
with open(path_kconfig, "w") as handle:
handle.write('CONFIG_BOOL=m\n')
must_exist = True
pkgname = "linux-nokia-n900"
with pytest.raises(RuntimeError) as e:
func(args, pkgname, components_list, details, must_exist)
assert "is not a valid kernel config" in str(e.value)
os.unlink(path_kconfig)
# Pass checks of base and nftables
path_kconfig = f"{path_aport}/config-nokia-n900.armv7"
with open(path_kconfig, "w") as handle:
handle.write('CONFIG_BLK_DEV_INITRD=y\n'
'CONFIG_DEFAULT_HOSTNAME="(none)"\n'
'CONFIG_BINFMT_ELF=y\n'
'CONFIG_NFT_COUNTER=y\n')
must_exist = True
pkgname = "nokia-n900"
assert func(args, pkgname, components_list, details, must_exist) is True
# Don't pass nftables check
with open(path_kconfig, "w") as handle:
handle.write('CONFIG_BLK_DEV_INITRD=y\n'
'CONFIG_DEFAULT_HOSTNAME="(none)"\n'
'CONFIG_BINFMT_ELF=y\n')
assert func(args, pkgname, components_list, details, must_exist) is False
# Don't pass waydroid check (extra component check passed via cmdline)
with open(path_kconfig, "w") as handle:
handle.write('CONFIG_BLK_DEV_INITRD=y\n'
'CONFIG_DEFAULT_HOSTNAME="(none)"\n'
'CONFIG_BINFMT_ELF=y\n'
'CONFIG_NFT_COUNTER=y\n')
components_list = ["waydroid"]
assert func(args, pkgname, components_list, details, must_exist) is False
def test_extract_arch(tmpdir):
func = pmb.parse.kconfig.extract_arch
path = f"{tmpdir}/config"
with open(path, "w") as handle:
handle.write('CONFIG_ARM=y\n')
assert func(path) == "armv7"
with open(path, "w") as handle:
handle.write('CONFIG_ARM64=y\n')
assert func(path) == "aarch64"
with open(path, "w") as handle:
handle.write('CONFIG_RISCV=y\n')
assert func(path) == "riscv64"
with open(path, "w") as handle:
handle.write('CONFIG_X86_32=y\n')
assert func(path) == "x86"
with open(path, "w") as handle:
handle.write('CONFIG_X86_64=y\n')
assert func(path) == "x86_64"
with open(path, "w") as handle:
handle.write('hello')
assert func(path) == "unknown"
def test_extract_version(tmpdir):
func = pmb.parse.kconfig.extract_version
path = f"{tmpdir}/config"
with open(path, "w") as handle:
handle.write("#\n"
"# Automatically generated file; DO NOT EDIT.\n"
"# Linux/arm64 3.10.93 Kernel Configuration\n")
assert func(path) == "3.10.93"
with open(path, "w") as handle:
handle.write("#\n"
"# Automatically generated file; DO NOT EDIT.\n"
"# Linux/arm64 6.2.0 Kernel Configuration\n")
assert func(path) == "6.2.0"
with open(path, "w") as handle:
handle.write("#\n"
"# Automatically generated file; DO NOT EDIT.\n"
"# Linux/riscv 6.1.0-rc3 Kernel Configuration\n")
assert func(path) == "6.1.0_rc3"
with open(path, "w") as handle:
handle.write("#\n"
"# Automatically generated file; DO NOT EDIT.\n"
"# no version here\n")
assert func(path) == "unknown"
def test_check_file(tmpdir, monkeypatch):
patch_config(monkeypatch)
func = pmb.parse.kconfig.check_file
path = f"{tmpdir}/config"
# Fail the basic check
with open(path, "w") as handle:
handle.write("#\n"
"# Automatically generated file; DO NOT EDIT.\n"
"# Linux/arm64 3.10.93 Kernel Configuration\n"
"CONFIG_ARM64=y\n")
func(path) is False
# Pass the basic check
with open(path, "w") as handle:
handle.write("#\n"
"# Automatically generated file; DO NOT EDIT.\n"
"# Linux/arm64 3.10.93 Kernel Configuration\n"
"CONFIG_ARM64=y\n"
"BLK_DEV_INITRD=y\n"
"DEFAULT_HOSTNAME=\"(none)\"\n"
"BINFMT_ELF=y\n"
"DEVPTS_MULTIPLE_INSTANCES=y\n"
"LBDAF=y\n")
func(path) is True