From patchwork Sat Jan 10 20:08:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1431 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1768075742; bh=1tG3Ds0jGvznQWvDZmwJqx3+yrS9a3QWPc26LzChEdY=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=PHM3qFSkppz6i5/Lf67VuPgu/eA39LA5QHvfzbjaGB02gPw6P4XuXGqYJXznO1rEX ldbkFOFdLML1tpF1P1pwTbqlDPNl4PZtePfxD5231WA8kxNUUbEN4Cw/hHRCkUHX7C w/EK576wbz1KRwMNG/G63Z9iV+aCiu95ylGi9DKvJeJJ89DKeERVznACxF8QNpuCGM cpSlJ4VpXNKUDNmw+4EIpZC7lqo+oRfixi9OxkwD/xkepn1fUzcY+iWTUehd/hLOzl 3t1FqgT7XPbSxaGkMxQTyOVg6To+yKJpMUXIA+59pKjW/4yndHot5MPWScOf7gY792 x+2PgOvro6H6g== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 4660369221 for ; Sat, 10 Jan 2026 13:09:02 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id R2v1l2JftuYR for ; Sat, 10 Jan 2026 13:09:02 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1768075742; bh=1tG3Ds0jGvznQWvDZmwJqx3+yrS9a3QWPc26LzChEdY=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=PHM3qFSkppz6i5/Lf67VuPgu/eA39LA5QHvfzbjaGB02gPw6P4XuXGqYJXznO1rEX ldbkFOFdLML1tpF1P1pwTbqlDPNl4PZtePfxD5231WA8kxNUUbEN4Cw/hHRCkUHX7C w/EK576wbz1KRwMNG/G63Z9iV+aCiu95ylGi9DKvJeJJ89DKeERVznACxF8QNpuCGM cpSlJ4VpXNKUDNmw+4EIpZC7lqo+oRfixi9OxkwD/xkepn1fUzcY+iWTUehd/hLOzl 3t1FqgT7XPbSxaGkMxQTyOVg6To+yKJpMUXIA+59pKjW/4yndHot5MPWScOf7gY792 x+2PgOvro6H6g== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 339A269275 for ; Sat, 10 Jan 2026 13:09:02 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1768075740; bh=VkLjdBqnTH/wn0si4j88t5l46a3OwTWxliyCoArNba4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WP4lcFRrB2tx/y8r0J1vjyMhkBwyKnqKzzj1XDs1jh2hwGqS6gIoQvp2EVXz6BTG7 hF11HSbxoY4SCClBCKLgQnJcnAtCdQwiU2ardyC6mIeNoz044lKVMfRAtqUMGvlbhj PMeiiVMGnvX0oI0qW4H4PkVr3jkIy2Yw8vudrvU5RIgz2CBSCIg4Xux7y3gv8wJUAx dzp5tfI9foK659G7JOCRoGcGAEy3pTMKj8hszfCcox0HLeSQ1NwOTxQ+rfHJpy6yJI NyjhTU6JwlrFkDO6JDhRydSc5KDLzNoBrRqHzIKHT+KsIielF/86FG/pBRo33YIixt NEDqdViLncfNQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 7458E69221; Sat, 10 Jan 2026 13:09:00 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id FdOOzauWaG4w; Sat, 10 Jan 2026 13:09:00 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1768075736; bh=owpYexKoK1/8zpuQMcIZM2RIjSb8RP+MZtfYRmBM1iA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dXtiL6iud2bprct3a+/znUZkbTIxYh0Z1drHDT1cwy6ls64MwhJjJcvh5WCyejKZt CMMZX38GVEer/YSpT0qqV0zbe/lgcGd+kCEDxg5ThllW4JwFsFNdUKIAkm1w7022aF SSfeVT+gfsTuaYtiQj/EWCZ2ByTAvhwphVfZQJQ/VGFim7BXfpeVgWvdSVbFbvIbfI Y5Wspar6PSCtFwsa40s0MZNMRrNk2gnemeUedxr0oaxCm2Dugp4IiQVXlUovVhLHN/ c5R5Pytf305v5wycSXmwfMP+vUIRS/5cz0Vtc7gV2pjIEGwfxON0wu33Qmdp75UKHN 5CyFX4fYbV8gg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id DF150690E7; Sat, 10 Jan 2026 13:08:55 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Sat, 10 Jan 2026 13:08:06 -0700 Message-ID: <20260110200828.168672-4-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260110200828.168672-1-sjg@u-boot.org> References: <20260110200828.168672-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: EM2ISPAYXGUQBEBXJJFLS5OFNB3LA5MJ X-Message-ID-Hash: EM2ISPAYXGUQBEBXJJFLS5OFNB3LA5MJ X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass , "Claude Opus 4 . 5" X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 03/18] buildman: Move cfgutil tests to test_cfgutil.py List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Move the cfgutil-related tests from test.py to a new test_cfgutil.py file for better organisation: - TestAdjustCfg: Tests for adjust_cfg_line(), adjust_cfg_lines(), convert_list_to_dict(), and check_cfg_lines() - TestProcessConfig: Tests for process_config() This follows the pattern of other test files like test_builder.py and test_boards.py Co-developed-by: Claude Opus 4.5 Signed-off-by: Simon Glass --- tools/buildman/main.py | 4 +- tools/buildman/test.py | 215 ------------------------------ tools/buildman/test_cfgutil.py | 234 +++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 216 deletions(-) create mode 100644 tools/buildman/test_cfgutil.py diff --git a/tools/buildman/main.py b/tools/buildman/main.py index 5831dbf1222..c8502c370f9 100755 --- a/tools/buildman/main.py +++ b/tools/buildman/main.py @@ -40,6 +40,7 @@ def run_tests(skip_net_tests, debug, verbose, args): from buildman import test_boards from buildman import test_bsettings from buildman import test_builder + from buildman import test_cfgutil test_name = args.terms and args.terms[0] or None if skip_net_tests: @@ -50,7 +51,8 @@ def run_tests(skip_net_tests, debug, verbose, args): result = test_util.run_test_suites( 'buildman', debug, verbose, False, False, args.threads, test_name, [], [test.TestBuildOutput, test.TestBuildBoards, test.TestBuild, - test.TestBuildConfig, test.TestBuildMisc, test.TestBuilderFuncs, + test.TestBuildMisc, test.TestBuilderFuncs, + test_cfgutil.TestAdjustCfg, test_cfgutil.TestProcessConfig, func_test.TestFunctional, test_boards.TestBoards, test_bsettings.TestBsettings, test_builder.TestPrintFuncSizeDetail, diff --git a/tools/buildman/test.py b/tools/buildman/test.py index bdf68ef841d..b70a0012ca5 100644 --- a/tools/buildman/test.py +++ b/tools/buildman/test.py @@ -758,144 +758,6 @@ class TestBuild(TestBuildBase): self.assertEqual(expected, result) -class TestBuildConfig(TestBuildBase): - """Tests for config adjustment functionality""" - - def test_adjust_cfg_nop(self): - """check various adjustments of config that are nops""" - # enable an enabled CONFIG - self.assertEqual( - 'CONFIG_FRED=y', - cfgutil.adjust_cfg_line('CONFIG_FRED=y', {'FRED':'FRED'})[0]) - - # disable a disabled CONFIG - self.assertEqual( - '# CONFIG_FRED is not set', - cfgutil.adjust_cfg_line( - '# CONFIG_FRED is not set', {'FRED':'~FRED'})[0]) - - # use the adjust_cfg_lines() function - self.assertEqual( - ['CONFIG_FRED=y'], - cfgutil.adjust_cfg_lines(['CONFIG_FRED=y'], {'FRED':'FRED'})) - self.assertEqual( - ['# CONFIG_FRED is not set'], - cfgutil.adjust_cfg_lines(['CONFIG_FRED=y'], {'FRED':'~FRED'})) - - # handling an empty line - self.assertEqual('#', cfgutil.adjust_cfg_line('#', {'FRED':'~FRED'})[0]) - - def test_adjust_cfg(self): - """check various adjustments of config""" - # disable a CONFIG - self.assertEqual( - '# CONFIG_FRED is not set', - cfgutil.adjust_cfg_line('CONFIG_FRED=1' , {'FRED':'~FRED'})[0]) - - # enable a disabled CONFIG - self.assertEqual( - 'CONFIG_FRED=y', - cfgutil.adjust_cfg_line( - '# CONFIG_FRED is not set', {'FRED':'FRED'})[0]) - - # enable a CONFIG that doesn't exist - self.assertEqual( - ['CONFIG_FRED=y'], - cfgutil.adjust_cfg_lines([], {'FRED':'FRED'})) - - # disable a CONFIG that doesn't exist - self.assertEqual( - ['# CONFIG_FRED is not set'], - cfgutil.adjust_cfg_lines([], {'FRED':'~FRED'})) - - # disable a value CONFIG - self.assertEqual( - '# CONFIG_FRED is not set', - cfgutil.adjust_cfg_line('CONFIG_FRED="fred"' , {'FRED':'~FRED'})[0]) - - # setting a value CONFIG - self.assertEqual( - 'CONFIG_FRED="fred"', - cfgutil.adjust_cfg_line('# CONFIG_FRED is not set' , - {'FRED':'FRED="fred"'})[0]) - - # changing a value CONFIG - self.assertEqual( - 'CONFIG_FRED="fred"', - cfgutil.adjust_cfg_line('CONFIG_FRED="ernie"' , - {'FRED':'FRED="fred"'})[0]) - - # setting a value for a CONFIG that doesn't exist - self.assertEqual( - ['CONFIG_FRED="fred"'], - cfgutil.adjust_cfg_lines([], {'FRED':'FRED="fred"'})) - - def test_convert_adjust_cfg_list(self): - """Check conversion of the list of changes into a dict""" - self.assertEqual({}, cfgutil.convert_list_to_dict(None)) - - expect = { - 'FRED':'FRED', - 'MARY':'~MARY', - 'JOHN':'JOHN=0x123', - 'ALICE':'ALICE="alice"', - 'AMY':'AMY', - 'ABE':'~ABE', - 'MARK':'MARK=0x456', - 'ANNA':'ANNA="anna"', - } - actual = cfgutil.convert_list_to_dict( - ['FRED', '~MARY', 'JOHN=0x123', 'ALICE="alice"', - 'CONFIG_AMY', '~CONFIG_ABE', 'CONFIG_MARK=0x456', - 'CONFIG_ANNA="anna"']) - self.assertEqual(expect, actual) - - # Test comma-separated values - actual = cfgutil.convert_list_to_dict( - ['FRED,~MARY,JOHN=0x123', 'ALICE="alice"', - 'CONFIG_AMY,~CONFIG_ABE', 'CONFIG_MARK=0x456,CONFIG_ANNA="anna"']) - self.assertEqual(expect, actual) - - # Test mixed comma-separated and individual values - actual = cfgutil.convert_list_to_dict( - ['FRED,~MARY', 'JOHN=0x123', 'ALICE="alice",CONFIG_AMY', - '~CONFIG_ABE,CONFIG_MARK=0x456', 'CONFIG_ANNA="anna"']) - self.assertEqual(expect, actual) - - def test_check_cfg_file(self): - """Test check_cfg_file detects conflicts as expected""" - # Check failure to disable CONFIG - result = cfgutil.check_cfg_lines(['CONFIG_FRED=1'], {'FRED':'~FRED'}) - self.assertEqual([['~FRED', 'CONFIG_FRED=1']], result) - - result = cfgutil.check_cfg_lines( - ['CONFIG_FRED=1', 'CONFIG_MARY="mary"'], {'FRED':'~FRED'}) - self.assertEqual([['~FRED', 'CONFIG_FRED=1']], result) - - result = cfgutil.check_cfg_lines( - ['CONFIG_FRED=1', 'CONFIG_MARY="mary"'], {'MARY':'~MARY'}) - self.assertEqual([['~MARY', 'CONFIG_MARY="mary"']], result) - - # Check failure to enable CONFIG - result = cfgutil.check_cfg_lines( - ['# CONFIG_FRED is not set'], {'FRED':'FRED'}) - self.assertEqual([['FRED', '# CONFIG_FRED is not set']], result) - - # Check failure to set CONFIG value - result = cfgutil.check_cfg_lines( - ['# CONFIG_FRED is not set', 'CONFIG_MARY="not"'], - {'MARY':'MARY="mary"', 'FRED':'FRED'}) - self.assertEqual([ - ['FRED', '# CONFIG_FRED is not set'], - ['MARY="mary"', 'CONFIG_MARY="not"']], result) - - # Check failure to add CONFIG value - result = cfgutil.check_cfg_lines([], {'MARY':'MARY="mary"'}) - self.assertEqual([ - ['MARY="mary"', 'Missing expected line: CONFIG_MARY="mary"']], - result) - - class TestBuildMisc(TestBuildBase): """Miscellaneous buildman tests""" @@ -1322,83 +1184,6 @@ class TestBuilderFuncs(TestBuildBase): finally: os.unlink(tmp_name) - def test_process_config_defconfig(self): - """Test process_config() with .config style file""" - config_data = '''# This is a comment -CONFIG_OPTION_A=y -CONFIG_OPTION_B="string" -CONFIG_OPTION_C=123 -# CONFIG_OPTION_D is not set -''' - with tempfile.NamedTemporaryFile(mode='w', delete=False, - suffix='.config') as tmp: - tmp.write(config_data) - tmp_name = tmp.name - - try: - config = cfgutil.process_config(tmp_name, squash_config_y=False) - - self.assertEqual('y', config['CONFIG_OPTION_A']) - self.assertEqual('"string"', config['CONFIG_OPTION_B']) - self.assertEqual('123', config['CONFIG_OPTION_C']) - # Comments should be ignored - self.assertNotIn('CONFIG_OPTION_D', config) - finally: - os.unlink(tmp_name) - - def test_process_config_autoconf_h(self): - """Test process_config() with autoconf.h style file""" - config_data = '''/* Auto-generated header */ -#define CONFIG_OPTION_A 1 -#define CONFIG_OPTION_B "value" -#define CONFIG_OPTION_C -#define NOT_CONFIG 1 -''' - with tempfile.NamedTemporaryFile(mode='w', delete=False, - suffix='.h') as tmp: - tmp.write(config_data) - tmp_name = tmp.name - - try: - config = cfgutil.process_config(tmp_name, squash_config_y=False) - - self.assertEqual('1', config['CONFIG_OPTION_A']) - self.assertEqual('"value"', config['CONFIG_OPTION_B']) - # #define without value gets empty string (squash_config_y=False) - self.assertEqual('', config['CONFIG_OPTION_C']) - # Non-CONFIG_ defines should be ignored - self.assertNotIn('NOT_CONFIG', config) - finally: - os.unlink(tmp_name) - - def test_process_config_nonexistent(self): - """Test process_config() with non-existent file""" - config = cfgutil.process_config('/nonexistent/path/config', - squash_config_y=False) - self.assertEqual({}, config) - - def test_process_config_squash_y(self): - """Test process_config() with squash_config_y enabled""" - config_data = '''CONFIG_OPTION_A=y -CONFIG_OPTION_B=n -#define CONFIG_OPTION_C -''' - with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp: - tmp.write(config_data) - tmp_name = tmp.name - - try: - config = cfgutil.process_config(tmp_name, squash_config_y=True) - - # y should be squashed to 1 - self.assertEqual('1', config['CONFIG_OPTION_A']) - # n should remain n - self.assertEqual('n', config['CONFIG_OPTION_B']) - # Empty #define should get '1' when squash_config_y is True - self.assertEqual('1', config['CONFIG_OPTION_C']) - finally: - os.unlink(tmp_name) - def test_process_environment(self): """Test _process_environment() function""" build = builder.Builder(self.toolchains, self.base_dir, None, 0, 2) diff --git a/tools/buildman/test_cfgutil.py b/tools/buildman/test_cfgutil.py new file mode 100644 index 00000000000..ba3f0468570 --- /dev/null +++ b/tools/buildman/test_cfgutil.py @@ -0,0 +1,234 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022 Google LLC +# + +"""Tests for cfgutil module""" + +import os +import tempfile +import unittest + +from buildman import cfgutil + + +class TestAdjustCfg(unittest.TestCase): + """Tests for config adjustment functions""" + + def test_adjust_cfg_nop(self): + """check various adjustments of config that are nops""" + # enable an enabled CONFIG + self.assertEqual( + 'CONFIG_FRED=y', + cfgutil.adjust_cfg_line('CONFIG_FRED=y', {'FRED':'FRED'})[0]) + + # disable a disabled CONFIG + self.assertEqual( + '# CONFIG_FRED is not set', + cfgutil.adjust_cfg_line( + '# CONFIG_FRED is not set', {'FRED':'~FRED'})[0]) + + # use the adjust_cfg_lines() function + self.assertEqual( + ['CONFIG_FRED=y'], + cfgutil.adjust_cfg_lines(['CONFIG_FRED=y'], {'FRED':'FRED'})) + self.assertEqual( + ['# CONFIG_FRED is not set'], + cfgutil.adjust_cfg_lines(['CONFIG_FRED=y'], {'FRED':'~FRED'})) + + # handling an empty line + self.assertEqual('#', cfgutil.adjust_cfg_line('#', {'FRED':'~FRED'})[0]) + + def test_adjust_cfg(self): + """check various adjustments of config""" + # disable a CONFIG + self.assertEqual( + '# CONFIG_FRED is not set', + cfgutil.adjust_cfg_line('CONFIG_FRED=1' , {'FRED':'~FRED'})[0]) + + # enable a disabled CONFIG + self.assertEqual( + 'CONFIG_FRED=y', + cfgutil.adjust_cfg_line( + '# CONFIG_FRED is not set', {'FRED':'FRED'})[0]) + + # enable a CONFIG that doesn't exist + self.assertEqual( + ['CONFIG_FRED=y'], + cfgutil.adjust_cfg_lines([], {'FRED':'FRED'})) + + # disable a CONFIG that doesn't exist + self.assertEqual( + ['# CONFIG_FRED is not set'], + cfgutil.adjust_cfg_lines([], {'FRED':'~FRED'})) + + # disable a value CONFIG + self.assertEqual( + '# CONFIG_FRED is not set', + cfgutil.adjust_cfg_line('CONFIG_FRED="fred"' , {'FRED':'~FRED'})[0]) + + # setting a value CONFIG + self.assertEqual( + 'CONFIG_FRED="fred"', + cfgutil.adjust_cfg_line('# CONFIG_FRED is not set' , + {'FRED':'FRED="fred"'})[0]) + + # changing a value CONFIG + self.assertEqual( + 'CONFIG_FRED="fred"', + cfgutil.adjust_cfg_line('CONFIG_FRED="ernie"' , + {'FRED':'FRED="fred"'})[0]) + + # setting a value for a CONFIG that doesn't exist + self.assertEqual( + ['CONFIG_FRED="fred"'], + cfgutil.adjust_cfg_lines([], {'FRED':'FRED="fred"'})) + + def test_convert_adjust_cfg_list(self): + """Check conversion of the list of changes into a dict""" + self.assertEqual({}, cfgutil.convert_list_to_dict(None)) + + expect = { + 'FRED':'FRED', + 'MARY':'~MARY', + 'JOHN':'JOHN=0x123', + 'ALICE':'ALICE="alice"', + 'AMY':'AMY', + 'ABE':'~ABE', + 'MARK':'MARK=0x456', + 'ANNA':'ANNA="anna"', + } + actual = cfgutil.convert_list_to_dict( + ['FRED', '~MARY', 'JOHN=0x123', 'ALICE="alice"', + 'CONFIG_AMY', '~CONFIG_ABE', 'CONFIG_MARK=0x456', + 'CONFIG_ANNA="anna"']) + self.assertEqual(expect, actual) + + # Test comma-separated values + actual = cfgutil.convert_list_to_dict( + ['FRED,~MARY,JOHN=0x123', 'ALICE="alice"', + 'CONFIG_AMY,~CONFIG_ABE', 'CONFIG_MARK=0x456,CONFIG_ANNA="anna"']) + self.assertEqual(expect, actual) + + # Test mixed comma-separated and individual values + actual = cfgutil.convert_list_to_dict( + ['FRED,~MARY', 'JOHN=0x123', 'ALICE="alice",CONFIG_AMY', + '~CONFIG_ABE,CONFIG_MARK=0x456', 'CONFIG_ANNA="anna"']) + self.assertEqual(expect, actual) + + def test_check_cfg_file(self): + """Test check_cfg_file detects conflicts as expected""" + # Check failure to disable CONFIG + result = cfgutil.check_cfg_lines(['CONFIG_FRED=1'], {'FRED':'~FRED'}) + self.assertEqual([['~FRED', 'CONFIG_FRED=1']], result) + + result = cfgutil.check_cfg_lines( + ['CONFIG_FRED=1', 'CONFIG_MARY="mary"'], {'FRED':'~FRED'}) + self.assertEqual([['~FRED', 'CONFIG_FRED=1']], result) + + result = cfgutil.check_cfg_lines( + ['CONFIG_FRED=1', 'CONFIG_MARY="mary"'], {'MARY':'~MARY'}) + self.assertEqual([['~MARY', 'CONFIG_MARY="mary"']], result) + + # Check failure to enable CONFIG + result = cfgutil.check_cfg_lines( + ['# CONFIG_FRED is not set'], {'FRED':'FRED'}) + self.assertEqual([['FRED', '# CONFIG_FRED is not set']], result) + + # Check failure to set CONFIG value + result = cfgutil.check_cfg_lines( + ['# CONFIG_FRED is not set', 'CONFIG_MARY="not"'], + {'MARY':'MARY="mary"', 'FRED':'FRED'}) + self.assertEqual([ + ['FRED', '# CONFIG_FRED is not set'], + ['MARY="mary"', 'CONFIG_MARY="not"']], result) + + # Check failure to add CONFIG value + result = cfgutil.check_cfg_lines([], {'MARY':'MARY="mary"'}) + self.assertEqual([ + ['MARY="mary"', 'Missing expected line: CONFIG_MARY="mary"']], + result) + + +class TestProcessConfig(unittest.TestCase): + """Tests for process_config() function""" + + def test_process_config_defconfig(self): + """Test process_config() with .config style file""" + config_data = '''# This is a comment +CONFIG_OPTION_A=y +CONFIG_OPTION_B="string" +CONFIG_OPTION_C=123 +# CONFIG_OPTION_D is not set +''' + with tempfile.NamedTemporaryFile(mode='w', delete=False, + suffix='.config') as tmp: + tmp.write(config_data) + tmp_name = tmp.name + + try: + config = cfgutil.process_config(tmp_name, squash_config_y=False) + + self.assertEqual('y', config['CONFIG_OPTION_A']) + self.assertEqual('"string"', config['CONFIG_OPTION_B']) + self.assertEqual('123', config['CONFIG_OPTION_C']) + # Comments should be ignored + self.assertNotIn('CONFIG_OPTION_D', config) + finally: + os.unlink(tmp_name) + + def test_process_config_autoconf_h(self): + """Test process_config() with autoconf.h style file""" + config_data = '''/* Auto-generated header */ +#define CONFIG_OPTION_A 1 +#define CONFIG_OPTION_B "value" +#define CONFIG_OPTION_C +#define NOT_CONFIG 1 +''' + with tempfile.NamedTemporaryFile(mode='w', delete=False, + suffix='.h') as tmp: + tmp.write(config_data) + tmp_name = tmp.name + + try: + config = cfgutil.process_config(tmp_name, squash_config_y=False) + + self.assertEqual('1', config['CONFIG_OPTION_A']) + self.assertEqual('"value"', config['CONFIG_OPTION_B']) + # #define without value gets empty string (squash_config_y=False) + self.assertEqual('', config['CONFIG_OPTION_C']) + # Non-CONFIG_ defines should be ignored + self.assertNotIn('NOT_CONFIG', config) + finally: + os.unlink(tmp_name) + + def test_process_config_nonexistent(self): + """Test process_config() with non-existent file""" + config = cfgutil.process_config('/nonexistent/path/config', + squash_config_y=False) + self.assertEqual({}, config) + + def test_process_config_squash_y(self): + """Test process_config() with squash_config_y enabled""" + config_data = '''CONFIG_OPTION_A=y +CONFIG_OPTION_B=n +#define CONFIG_OPTION_C +''' + with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp: + tmp.write(config_data) + tmp_name = tmp.name + + try: + config = cfgutil.process_config(tmp_name, squash_config_y=True) + + # y should be squashed to 1 + self.assertEqual('1', config['CONFIG_OPTION_A']) + # n should remain n + self.assertEqual('n', config['CONFIG_OPTION_B']) + # Empty #define should get '1' when squash_config_y is True + self.assertEqual('1', config['CONFIG_OPTION_C']) + finally: + os.unlink(tmp_name) + + +if __name__ == "__main__": + unittest.main()