blob: 499c469f24c1ee31ef6c8cb9a5cfb5b82a9e0bc1 [file] [edit]
import unittest
import os
import shutil
import tempfile
from pathlib import Path
import sys
from unittest.mock import patch, MagicMock
# Import the script to test
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".")))
import kconfig_gen_symbols
import kconfig_utils
class TestKconfigGenSymbols(unittest.TestCase):
def setUp(self):
self.test_dir = tempfile.mkdtemp()
self.zephyr_base = Path(self.test_dir) / "zephyr"
self.zephyr_base.mkdir()
# Create minimal Zephyr structure
(self.zephyr_base / "scripts" / "kconfig").mkdir(parents=True)
(self.zephyr_base / "arch" / "arm").mkdir(parents=True)
(self.zephyr_base / "soc" / "nordic" / "nrf52").mkdir(parents=True)
(self.zephyr_base / "boards" / "arm" / "nrf52840dk").mkdir(parents=True)
(self.zephyr_base / "cmake" / "toolchain" / "zephyr").mkdir(parents=True)
# Create some Kconfig files
(self.zephyr_base / "Kconfig").write_text("mainmenu \"Test\"\nsource \"arch/Kconfig\"\n")
(self.zephyr_base / "arch" / "Kconfig").write_text("config ARCH_ARM\n bool \"ARM\"\n")
(self.zephyr_base / "soc" / "nordic" / "nrf52" / "soc.yml").touch()
(self.zephyr_base / "soc" / "nordic" / "nrf52" / "Kconfig").write_text("config SOC_NRF52840\n bool \"nRF52840\"\n")
(self.zephyr_base / "boards" / "arm" / "nrf52840dk" / "board.yml").write_text("boards:\n - name: nrf52840dk\n")
(self.zephyr_base / "boards" / "arm" / "nrf52840dk" / "Kconfig").write_text("config BOARD_NRF52840DK\n bool \"nRF52840DK\"\n")
# Mock kconfiglib
self.kconfig_mock = MagicMock()
sys.modules['kconfiglib'] = self.kconfig_mock
# Default mock for Kconfig
self.mock_kconf = MagicMock()
self.kconfig_mock.Kconfig.return_value = self.mock_kconf
self.mock_kconf.warnings = []
def tearDown(self):
shutil.rmtree(self.test_dir)
def test_find_soc_source_directories(self):
dirs = kconfig_gen_symbols.find_soc_source_directories(str(self.zephyr_base))
self.assertTrue(any("nordic/nrf52" in d for d in dirs))
# Test with OOT modules
module_dir = os.path.join(self.test_dir, "my_module")
(Path(module_dir) / "soc" / "my_soc").mkdir(parents=True)
(Path(module_dir) / "soc" / "my_soc" / "soc.yml").touch()
dirs_with_mods = kconfig_gen_symbols.find_soc_source_directories(str(self.zephyr_base), [module_dir])
self.assertTrue(any("nordic/nrf52" in d for d in dirs_with_mods))
self.assertTrue(any("my_module/soc/my_soc" in d for d in dirs_with_mods))
def test_find_board_source_directories(self):
dirs = kconfig_gen_symbols.find_board_source_directories([str(self.zephyr_base / "boards")])
self.assertTrue(any("nrf52840dk" in d for d in dirs))
def test_discover_board_variants_yaml(self):
# 1. Test singular 'board'
board_dir_1 = Path(self.test_dir) / "board1"
board_dir_1.mkdir()
(board_dir_1 / "board.yml").write_text("""
board:
name: test_board_1
socs:
- name: nrf52840
""")
# 2. Test plural 'boards'
board_dir_2 = Path(self.test_dir) / "board2"
board_dir_2.mkdir()
(board_dir_2 / "board.yml").write_text("""
boards:
- name: test_board_2
socs:
- name: stm32f405
- name: test_board_3
socs:
- name: esp32
""")
soc_to_clusters = {"nrf52840": ["cpu0"]}
symbols = {}
kconfig_gen_symbols.discover_board_variants([str(board_dir_1), str(board_dir_2)], soc_to_clusters, symbols)
self.assertIn("CONFIG_BOARD_TEST_BOARD_1_NRF52840", symbols)
self.assertIn("CONFIG_BOARD_TEST_BOARD_1_NRF52840_CPU0", symbols)
self.assertIn("CONFIG_BOARD_TEST_BOARD_2_STM32F405", symbols)
self.assertIn("CONFIG_BOARD_TEST_BOARD_3_ESP32", symbols)
@patch('kconfig_utils.setup_kconfiglib')
@patch('kconfig_utils.generate_kconfig_dts')
def test_main_execution(self, mock_gen_dts, mock_setup):
output_file = os.path.join(self.test_dir, "BUILD.bazel")
# Mock kconfiglib behavior
mock_kconf = MagicMock()
self.kconfig_mock.Kconfig.return_value = mock_kconf
mock_kconf.warnings = []
# Define some symbols
sym1 = MagicMock()
sym1.type = 2 # BOOL
sym1.name = "MY_SYMBOL"
sym2 = MagicMock()
sym2.type = 4 # INT
sym2.name = "MY_INT"
mock_kconf.syms = {"MY_SYMBOL": sym1, "MY_INT": sym2}
# Set kconfiglib constants
self.kconfig_mock.UNKNOWN = 0
self.kconfig_mock.INT = 4
self.kconfig_mock.HEX = 5
self.kconfig_mock.STRING = 6
# Run main
with patch('sys.argv', ['kconfig_gen_symbols.py', '--zephyr-base', str(self.zephyr_base), '--output', output_file]):
kconfig_gen_symbols.main()
# Verify kconfiglib was called
self.kconfig_mock.Kconfig.assert_called()
content = Path(output_file).read_text()
self.assertIn('bool_flag(name = "CONFIG_MY_SYMBOL"', content)
self.assertIn('int_flag(name = "CONFIG_MY_INT"', content)
@patch('kconfig_utils.generate_kconfig_dts')
def test_logging_template_fallback(self, mock_gen_dts):
app_dir = Path(self.test_dir) / "app"
app_dir.mkdir()
(app_dir / "Kconfig").write_text('module = MY_APP\nsource "subsys/logging/Kconfig.template.log_config"')
output_file = os.path.join(self.test_dir, "BUILD.bazel")
self.kconfig_mock.Kconfig.return_value.syms = {}
with patch('sys.argv', ['kconfig_gen_symbols.py', '--zephyr-base', str(self.zephyr_base), '--modules', str(app_dir), '--output', output_file]):
kconfig_gen_symbols.main()
content = Path(output_file).read_text()
self.assertIn('CONFIG_MY_APP_LOG_LEVEL', content)
@patch('kconfig_utils.setup_kconfiglib')
@patch('kconfig_utils.generate_kconfig_dts')
@patch('kconfig_gen_symbols.find_soc_source_directories')
@patch('kconfig_gen_symbols.find_board_source_directories')
def test_main_smoke(self, mock_agg, mock_find_board, mock_find_soc, mock_gen_dts):
# Smoke test to ensure no NameErrors in main
output_file = os.path.join(self.test_dir, "BUILD.bazel")
mock_find_soc.return_value = []
mock_find_board.return_value = []
# Mock kconfiglib behavior
mock_kconf = MagicMock()
self.kconfig_mock.Kconfig.return_value = mock_kconf
mock_kconf.warnings = []
mock_kconf.syms = {}
with patch('sys.argv', ['kconfig_gen_symbols.py', '--zephyr-base', str(self.zephyr_base), '--output', output_file]):
kconfig_gen_symbols.main()
@patch('kconfig_utils.setup_kconfiglib')
@patch('kconfig_utils.generate_kconfig_dts')
def test_warnings_not_fatal(self, mock_gen_dts, mock_setup):
output_file = os.path.join(self.test_dir, "BUILD.bazel")
# Mock kconfiglib to produce warnings
mock_kconf = MagicMock()
self.kconfig_mock.Kconfig.return_value = mock_kconf
mock_kconf.warnings = ["Warning 1", "Warning 2"]
mock_kconf.syms = {}
with patch('sys.argv', ['kconfig_gen_symbols.py', '--zephyr-base', str(self.zephyr_base), '--output', output_file]):
kconfig_gen_symbols.main()
# Verify that it completed and generated warnings file
output_dir = os.path.dirname(os.path.abspath(output_file))
warnings_file = os.path.join(output_dir, "kconfig_warnings.c")
self.assertTrue(os.path.exists(warnings_file))
content = Path(warnings_file).read_text()
self.assertIn('#warning "Kconfig warning: Warning 1"', content)
self.assertIn('#warning "Kconfig warning: Warning 2"', content)
def test_setup_kconfig_environment_with_bzlmod_modules(self):
tmp_dir = os.path.join(self.test_dir, "tmp")
module_dir = os.path.join(self.test_dir, "external", "+_repo_rules+hal_nordic")
os.makedirs(module_dir, exist_ok=True)
kconfig_gen_symbols.setup_kconfig_environment(str(self.zephyr_base), tmp_dir, [module_dir])
self.assertEqual(os.environ["ZEPHYR_HAL_NORDIC_MODULE_DIR"], os.path.abspath(module_dir))
kconfig_env_file = os.path.join(tmp_dir, "kconfig_module_dirs.env")
self.assertTrue(os.path.exists(kconfig_env_file))
content = Path(kconfig_env_file).read_text()
self.assertIn(f"ZEPHYR_HAL_NORDIC_MODULE_DIR={os.path.abspath(module_dir)}", content)
if __name__ == '__main__':
unittest.main()