blob: 003c2bee23519a4caf8a31e964e2cabab9f4524d [file] [log] [blame]
# Copyright (C) 2023 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import subprocess
import sys
import tempfile
import unittest
import re
import pathlib
from absl.testing import absltest
def load_arguments():
parser = argparse.ArgumentParser()
parser.add_argument("--expected_modules_list")
parser.add_argument("--expected_modules_recovery_list")
parser.add_argument("--expected_modules_charger_list")
parser.add_argument("--build_vendor_boot", action="store_true")
parser.add_argument("--build_vendor_kernel_boot", action="store_true")
parser.add_argument("files", nargs="*", default=[])
return parser.parse_known_args()
arguments = None
class InitramfsModulesLists(unittest.TestCase):
def _detect_decompression_cmd(self, initramfs):
"""Determines what commands to use for decompressing initramfs.img
Args:
initramfs: The path to the initramfs.img to decompress
Returns:
The command that should be used to decompress the image.
"""
magic_to_decompression_command = {
# GZIP
b'\x1f\x8b\x08': ["gzip", "-c", "-d"],
# LZ4
# The kernel build uses legacy LZ4 compression (i.e. lz4 -l ...),
# so the legacy LZ4 magic must be used in little-endian format.
b'\x02\x21\x4c\x18': ["lz4", "-c", "-d", "-l"],
}
max_magic_len = max(len(magic) for magic in magic_to_decompression_command)
with open(initramfs, "rb") as initramfs_file:
hdr = initramfs_file.read(max_magic_len)
self.assertIsNotNone(hdr)
for magic, command in magic_to_decompression_command.items():
if hdr.startswith(magic):
return command
self.fail("No suitable compression method found")
def _decompress_initramfs(self, initramfs, temp_dir):
"""Decompress initramfs into temp_dir.
Args:
initramfs: path to initramfs.img gzip file to be decompressed
temp_dir: directory in which to decompress initramfs.img into
"""
decompression_cmd = self._detect_decompression_cmd(initramfs)
with open(initramfs) as initramfs_file:
with subprocess.Popen(["cpio", "-i"], cwd=temp_dir,
stdin=subprocess.PIPE, stdout=subprocess.PIPE) as cpio_sp:
with subprocess.Popen(decompression_cmd, stdin=initramfs_file,
stdout=cpio_sp.stdin) as decompress_sp:
decompress_sp.communicate()
self.assertEqual(0, decompress_sp.returncode)
def _diff_modules_lists(self, modules_lists_map, modules_dir):
"""Compares generated modules lists against expected modules lists for equality.
Given a dictionary of modules.load* files as keys, and expected modules
lists as values, compares each key value pair for equality.
Args:
modules_lists_map: dictionary with modules lists to compare
modules_dir: directory that contains the modules.load* files
"""
for modules_load, expected_modules_list_path in modules_lists_map.items():
modules_load_path = pathlib.Path(modules_dir, modules_load)
self.assertTrue(modules_load_path.is_file(), f"Can't find {modules_load_path}")
with open(modules_load_path) as modules_load_file, \
open(expected_modules_list_path) as expected_modules_list_file:
modules_load_lines = [pathlib.Path(line.strip()).name for line in modules_load_file]
expected_modules_list_lines = [line.strip() for line in expected_modules_list_file]
self.assertCountEqual(modules_load_lines, expected_modules_list_lines)
def test_diff(self):
initramfs_list = [file
for file in arguments.files
if pathlib.Path(file).name == "initramfs.img"]
self.assertEqual(len(initramfs_list), 1)
initramfs = initramfs_list[0]
modules_lists_map = {}
if arguments.expected_modules_list:
modules_lists_map["modules.load"] = arguments.expected_modules_list
if arguments.expected_modules_recovery_list:
modules_lists_map["modules.load.recovery"] = arguments.expected_modules_recovery_list
if arguments.expected_modules_charger_list:
modules_lists_map["modules.load.charger"] = arguments.expected_modules_charger_list
with tempfile.TemporaryDirectory() as temp_dir:
self._decompress_initramfs(initramfs, temp_dir)
lib_modules = pathlib.Path(temp_dir, "lib/modules")
self.assertTrue(lib_modules.is_dir())
for kernel_version in lib_modules.iterdir():
modules_dir = pathlib.Path(lib_modules, kernel_version)
self._diff_modules_lists(modules_lists_map, modules_dir)
def _verify_modules_load_lists(self, modules_list_name, vendor_boot_name, image_files):
"""Given a modules.load* name, ensures that no extraneous such files exist.
This tests to ensure that if a modules.load* file exists, then no other
modules.load* file must exist, except for vendor_boot.modules.load* or
vendor_kernel_boot.modules.load.
Args:
modules_list_name: The name of the modules.load list.
vendor_boot_name: Either vendor_boot or vendor_kernel_boot.
image_files: The files associated with the kernel_images() target.
"""
modules_load_lists = [modules_list_name]
if vendor_boot_name:
modules_load_lists.append(f"{vendor_boot_name}.{modules_list_name}")
modules_load_list_re = re.compile(f".*{modules_list_name}$")
modules_load_list_matches = [pathlib.Path(file).name
for file in image_files
if modules_load_list_re.fullmatch(pathlib.Path(file).name)]
self.assertCountEqual(modules_load_lists, modules_load_list_matches)
def test_modules_lists_existence(self):
vendor_boot_name = None
if arguments.build_vendor_boot:
vendor_boot_name = "vendor_boot"
elif arguments.build_vendor_kernel_boot:
vendor_boot_name = "vendor_kernel_boot"
if arguments.expected_modules_list:
self._verify_modules_load_lists("modules.load", vendor_boot_name, arguments.files)
if arguments.expected_modules_recovery_list:
self._verify_modules_load_lists("modules.load.recovery", vendor_boot_name, arguments.files)
if arguments.expected_modules_charger_list:
self._verify_modules_load_lists("modules.load.charger", vendor_boot_name, arguments.files)
def _verify_modules_dep_contains_modules_lists(self, modules_lists, modules_dir):
"""Ensures that modules.dep contains all entries needed for modules.load*.
Given a list of modules lists, this function ensures that modules.dep
contains an entry for each module in all of the modules lists.
Args:
modules_lists: The list of modules.load* that need to be tested.
modules_dir: The directory in which the modules reside in.
"""
modules_dep_path = pathlib.Path(modules_dir, "modules.dep")
modules_dep_set = set()
self.assertTrue(modules_dep_path.is_file(), f"Can't find {modules_dep_path}")
with open(modules_dep_path) as modules_dep_file:
for line in modules_dep_file:
# depmod entries have the form:
# mod_path: dep_path_1 dep_path_2
mod_name = line.split(":")[0].strip()
modules_dep_set.add(pathlib.Path(mod_name).name)
for mod_list in modules_lists:
mod_list_path = pathlib.Path(modules_dir, mod_list)
self.assertTrue(mod_list_path.is_file(), f"Can't find {mod_list_path}")
mod_list_modules = set()
with open(mod_list_path) as mod_list_file:
for line in mod_list_file:
mod_list_modules.add(pathlib.Path(line.strip()).name)
self.assertTrue(mod_list_modules.issubset(modules_dep_set),
"modules.dep does not contain an entry for each module to be loaded")
def test_modules_dep_contains_all_modules_lists(self):
initramfs_list = [file
for file in arguments.files
if pathlib.Path(file).name == "initramfs.img"]
self.assertEqual(len(initramfs_list), 1)
initramfs = initramfs_list[0]
modules_lists = []
if arguments.expected_modules_list:
modules_lists.append("modules.load")
if arguments.expected_modules_recovery_list:
modules_lists.append("modules.load.recovery")
if arguments.expected_modules_charger_list:
modules_lists.append("modules.load.charger")
with tempfile.TemporaryDirectory() as temp_dir:
self._decompress_initramfs(initramfs, temp_dir)
lib_modules = pathlib.Path(temp_dir, "lib/modules")
self.assertTrue(lib_modules.is_dir())
for kernel_version in lib_modules.iterdir():
modules_dir = pathlib.Path(lib_modules, kernel_version)
self._verify_modules_dep_contains_modules_lists(modules_lists, modules_dir)
if __name__ == '__main__':
arguments, unknown = load_arguments()
sys.argv[1:] = unknown
absltest.main()