#!/usr/bin/env python3 # Copyright (C) 2022 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. # This test requires buildozer installed in $HOME, which is not accessible # via `bazel test`. Hence, execute this test with # build/kernel/kleaf/build_config_to_bazel_test.py # TODO(b/241320850): Move this to bazel py_test, then use: # absl.testing.parameterized # absltest.main from typing import Sequence import os import sys import tempfile import textwrap import unittest import unittest.mock import re import build_config_to_bazel _TEST_DATA = 'build/kernel/kleaf/tests/build_config_to_bazel_test_data' _UNEXPECTED_LIST = [k for k, v in build_config_to_bazel._IGNORED_BUILD_CONFIGS.items() if v == r"^(.|\n)*$"] class BuildConfigToBazelTest(unittest.TestCase): def setUp(self) -> None: self.environ = os.environ.copy() self.stdout = tempfile.TemporaryFile('w+') self.addCleanup(self.stdout.close) self.stderr = tempfile.TemporaryFile('w+') self.addCleanup(self.stderr.close) def _run_test(self, name: str, expected_list: list[str | tuple[int, str]], expected_files: dict[str, str], argv: Sequence[str] = ()): """ Args: name: build.config file name under test data expected_list: list of texts expected in the final output. Each token may be one of the following: - Text to be found in the final output (it can occur any number of times); or - A tuple, where the first element is the text to be found in the final output, and the second element is the expected number of occurrences. expected_files: dict, where keys are file names and values are expected content of the file argv: argv to build_config_to_bazel """ self.environ['BUILD_CONFIG'] = f'{_TEST_DATA}/{name}' argv = ['--stdout'] + list(argv) with unittest.mock.patch.object(build_config_to_bazel.BuildConfigToBazel, '_create_extra_file') as create_extra_file: try: args = build_config_to_bazel.parse_args(argv) builder = build_config_to_bazel.BuildConfigToBazel( args=args, stdout=self.stdout, stderr=self.stderr, environ=self.environ) builder._add_package_comment_for_test = True builder.run() except Exception: self.stderr.seek(0) sys.__stderr__.write(self.stderr.read()) raise self.stdout.seek(0) out = self.stdout.read() for expected in expected_list: with self.subTest('expect output', expected=expected): if isinstance(expected, tuple): expected_count, expected = expected self.assertEqual( expected_count, out.count(expected), f"{repr(expected)} not found {expected_count} time(s) in:\n{out}") else: self.assertTrue(expected in out, f"{repr(expected)} not found in:\n{out}") for unexpected in _UNEXPECTED_LIST: with self.subTest('unexpected output', unexpected=unexpected): self.assertFalse(re.search(f"\\b{unexpected}\\b", out), f"{repr(unexpected)} found in:\n{out}") for filename, content in expected_files.items(): with self.subTest('expect file', filename=filename): create_extra_file.assert_called_with(filename, content) def test_simple(self): expected_list = [ 'name = "simple"', '''srcs = glob( ["**"], exclude = [ "**/.*", "**/.*/**", "**/BUILD.bazel", "**/*.bzl", ], ) + ["//common:kernel_aarch64_sources"],''', 'build_config = "build.config.simple"', 'name = "simple_dist"', 'strip_modules = True,', ] self._run_test('build.config.simple', expected_list, {}) def test_override_target_name(self): expected_list = [ 'name = "mytarget"', 'name = "mytarget_dist"', ] self._run_test('build.config.simple', expected_list, {}, argv=['--target=mytarget']) def test_override_ack(self): expected_list = [ # check base_kernel comments contains these '//ack:kernel_aarch64', ] self._run_test('build.config.simple', expected_list, {}, argv=['--common-kernel-tree=ack']) def test_no_hermetic_tools(self): expected_list = [ # check that comments contains these '# FIXME: HERMETIC_TOOLCHAIN=0 not supported' ] self._run_test('build.config.no_hermetic_toolchain', expected_list, {}) def test_extra_cmds(self): expected_list = [ # check that comments contains these '# FIXME: EXTRA_CMDS' ] self._run_test('build.config.extra_cmds', expected_list, {}) def test_everything(self): # Check defined targets expected_list = [ '"everything"', '"everything_dist"', '"everything_images"', '"everything_dts"', '"everything_modules_install"', # BUILD_CONFIG 'build_config = "build.config.everything"', # BUILD_CONFIG_FRAGMENTS # check that comments contains these 'build.config.fragment', 'kernel_build_config', # FAST_BUILD # check that comments contains these '--config=fast', # LTO # check that comments contains these '--lto=thin', # FILES '"myfile/myfile1"', '"myfile/myfile2"', # KCONFIG_EXT_PREFIX f'kconfig_ext = "{_TEST_DATA}"', # UNSTRIPPED_MODULES 'collect_unstripped_modules = True', # KMI_SYMBOL_LIST 'kmi_symbol_list = "//common:android/abi_symbollist_mydevice"', # ADDITIONAL_KMI_SYMBOL_LISTS textwrap.indent(textwrap.dedent('''\ additional_kmi_symbol_lists = [ "//common:android/abi_symbollist_additional1", "//common:android/abi_symbollist_additional2", ],'''), ' '), # TRIM_NONLISTED_KMI 'trim_nonlisted_kmi = True', # KMI_SYMBOL_LIST_STRICT_MODE 'kmi_symbol_list_strict_mode = True', # KBUILD_SYMTYPES 'kbuild_symtypes = True', # GENERATE_VMLINUX_BTF 'generate_vmlinux_btf = True', # BUILD_BOOT_IMG 'build_boot = True', # BUILD_VENDOR_BOOT_IMG 'build_vendor_boot = True', # BUILD_DTBO_IMG 'build_dtbo = True', # BUILD_VENDOR_KERNEL_BOOT 'build_vendor_kernel_boot = True', # BUILD_INITRAMFS 'build_initramfs = True', # MKBOOTIMG_PATH # check that comments contains these 'mymkbootimg', # MODULES_OPTIONS f'modules_options = "//{_TEST_DATA}:modules.options.everything"', # MODULES_BLOCKLIST 'modules_blocklist = "modules_blocklist"', # MODULES_LIST 'modules_list = "modules_list"', # SYSTEM_DLKM_MODULES_BLOCKLIST 'system_dlkm_modules_blocklist = "system_dlkm_modules_blocklist"', # SYSTEM_DLKM_MODULES_LIST 'system_dlkm_modules_list = "system_dlkm_modules_list"', # SYSTEM_DLKM_PROPS 'system_dlkm_props = "system_dlkm_props"', # VENDOR_DLKM_MODULES_BLOCKLIST 'vendor_dlkm_modules_blocklist = "vendor_dlkm_modules_blocklist"', # VENDOR_DLKM_MODULES_LIST 'vendor_dlkm_modules_list = "vendor_dlkm_modules_list"', # VENDOR_DLKM_PROPS 'vendor_dlkm_props = "vendor_dlkm_props"', # GKI_BUILD_CONFIG 'base_kernel = "//common:kernel_aarch64"', # GKI_PREBUILTS_DIR # check that comments contains these 'prebuilts/gki', # DTS_EXT_DIR f'dtstree = "//{_TEST_DATA}:everything_dts"', # BUILD_GKI_CERTIFICATION_TOOLS f'"//build/kernel:gki_certification_tools"', # UNKNOWN_BUILD_CONFIG f'# FIXME: Unknown in build config: UNKNOWN_BUILD_CONFIG=1', # EXT_MODULES textwrap.dedent(f'''\ kernel_module( name = "ext_module1", # //{_TEST_DATA}/ext_module1:ext_module1 outs = None, # FIXME: set to the list of external modules in this package. You may run `tools/bazel build //{_TEST_DATA}/ext_module1:ext_module1` and follow the instructions in the error message. kernel_build = "//{_TEST_DATA}:everything", )'''), textwrap.dedent(f'''\ kernel_module( name = "ext_module2", # //{_TEST_DATA}/ext_module2:ext_module2 outs = None, # FIXME: set to the list of external modules in this package. You may run `tools/bazel build //{_TEST_DATA}/ext_module2:ext_module2` and follow the instructions in the error message. kernel_build = "//{_TEST_DATA}:everything", )'''), (3, f'"//{_TEST_DATA}/ext_module1"'), (3, f'"//{_TEST_DATA}/ext_module2"'), textwrap.dedent(f'''\ kernel_abi( name = "everything_abi",'''), # ABI_DEFINITION f'abi_definition = None', f'//common:androidabi.xml', # KMI_ENFORCED f'kmi_enforced = True', # KMI_SYMBOL_LIST_ADD_ONLY f'kmi_symbol_list_add_only = True', # KMI_SYMBOL_LIST_MODULE_GROUPING f'module_grouping = True', # DO_NOT_STRIP_MODULES f'strip_modules = False,', ] expected_files = { f'{_TEST_DATA}/modules.options.everything': "\n" + textwrap.dedent('''\ option foo param=value option bar param=value '''), } self._run_test('build.config.everything', expected_list, expected_files) if __name__ == '__main__': unittest.main(verbosity=2)