CONFIGURATION!

This commit is contained in:
2026-08-05 18:25:17 +02:00
parent 770c7ca631
commit ffaf8f258e
155 changed files with 28575 additions and 19 deletions
@@ -0,0 +1,52 @@
# SPDX-License-Identifier: GPL-2.0
config A
bool "A"
default y
config A0
bool "A0"
depends on A
default y
help
This depends on A, so should be a submenu of A.
config A0_0
bool "A1_0"
depends on A0
help
Submenus are created recursively.
This should be a submenu of A0.
config A1
bool "A1"
depends on A
default y
help
This should line up with A0.
choice
prompt "choice"
depends on A1
help
Choice should become a submenu as well.
config A1_0
bool "A1_0"
config A1_1
bool "A1_1"
endchoice
config B
bool "B"
help
This is independent of A.
config C
bool "C"
depends on A
help
This depends on A, but not a consecutive item, so can/should not
be a submenu.
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
"""
Create submenu for symbols that depend on the preceding one.
If a symbols has dependency on the preceding symbol, the menu entry
should become the submenu of the preceding one, and displayed with
deeper indentation.
"""
def test(conf):
assert conf.oldaskconfig() == 0
assert conf.stdout_contains('expected_stdout')
@@ -0,0 +1,10 @@
A (A) [Y/n/?] (NEW)
A0 (A0) [Y/n/?] (NEW)
A1_0 (A0_0) [N/y/?] (NEW)
A1 (A1) [Y/n/?] (NEW)
choice
> 1. A1_0 (A1_0) (NEW)
2. A1_1 (A1_1) (NEW)
choice[1-2?]:
B (B) [N/y/?] (NEW)
C (C) [N/y/?] (NEW)
+13
View File
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
choice
prompt "boolean choice"
default BOOL_CHOICE1
config BOOL_CHOICE0
bool "choice 0"
config BOOL_CHOICE1
bool "choice 1"
endchoice
+29
View File
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: GPL-2.0
"""
Basic choice tests.
"""
def test_oldask0(conf):
assert conf.oldaskconfig() == 0
assert conf.stdout_contains('oldask0_expected_stdout')
def test_allyes(conf):
assert conf.allyesconfig() == 0
assert conf.config_contains('allyes_expected_config')
def test_allmod(conf):
assert conf.allmodconfig() == 0
assert conf.config_contains('allmod_expected_config')
def test_allno(conf):
assert conf.allnoconfig() == 0
assert conf.config_contains('allno_expected_config')
def test_alldef(conf):
assert conf.alldefconfig() == 0
assert conf.config_contains('alldef_expected_config')
@@ -0,0 +1,2 @@
# CONFIG_BOOL_CHOICE0 is not set
CONFIG_BOOL_CHOICE1=y
@@ -0,0 +1,2 @@
# CONFIG_BOOL_CHOICE0 is not set
CONFIG_BOOL_CHOICE1=y
@@ -0,0 +1,2 @@
# CONFIG_BOOL_CHOICE0 is not set
CONFIG_BOOL_CHOICE1=y
@@ -0,0 +1,2 @@
# CONFIG_BOOL_CHOICE0 is not set
CONFIG_BOOL_CHOICE1=y
@@ -0,0 +1,4 @@
boolean choice
1. choice 0 (BOOL_CHOICE0) (NEW)
> 2. choice 1 (BOOL_CHOICE1) (NEW)
choice[1-2?]:
@@ -0,0 +1,22 @@
choice
prompt "choose A or B"
config A
bool "A"
config B
bool "B"
endchoice
choice
prompt "choose X or Y"
depends on B
config X
bool "X"
config Y
bool "Y"
endchoice
@@ -0,0 +1,34 @@
# SPDX-License-Identifier: GPL-2.0-only
"""
Randomize all dependent choices
This is a somewhat tricky case for randconfig; the visibility of one choice is
determined by a member of another choice. Randconfig should be able to generate
all possible patterns.
"""
def test(conf):
expected0 = False
expected1 = False
expected2 = False
for i in range(100):
assert conf.randconfig(seed=i) == 0
if conf.config_matches('expected_config0'):
expected0 = True
elif conf.config_matches('expected_config1'):
expected1 = True
elif conf.config_matches('expected_config2'):
expected2 = True
else:
assert False
if expected0 and expected1 and expected2:
break
assert expected0
assert expected1
assert expected2
@@ -0,0 +1,6 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
CONFIG_A=y
# CONFIG_B is not set
@@ -0,0 +1,8 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
# CONFIG_A is not set
CONFIG_B=y
CONFIG_X=y
# CONFIG_Y is not set
@@ -0,0 +1,8 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
# CONFIG_A is not set
CONFIG_B=y
# CONFIG_X is not set
CONFIG_Y=y
@@ -0,0 +1,32 @@
choice
prompt "This is always invisible"
depends on n
config DUMMY
bool "DUMMY"
endchoice
choice
prompt "Choose A or B"
config A
bool "A"
config B
bool "B"
endchoice
config FOO
bool "FOO"
depends on A
choice
prompt "Choose X"
depends on FOO
config X
bool "X"
endchoice
@@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-only
"""
Randomize choices with correct dependencies
When shuffling a choice may potentially disrupt certain dependencies, symbol
values must be recalculated.
Related Linux commits:
- c8fb7d7e48d11520ad24808cfce7afb7b9c9f798
"""
def test(conf):
for i in range(20):
assert conf.randconfig(seed=i) == 0
assert (conf.config_matches('expected_config0') or
conf.config_matches('expected_config1') or
conf.config_matches('expected_config2'))
@@ -0,0 +1,8 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
CONFIG_A=y
# CONFIG_B is not set
CONFIG_FOO=y
CONFIG_X=y
@@ -0,0 +1,7 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
CONFIG_A=y
# CONFIG_B is not set
# CONFIG_FOO is not set
@@ -0,0 +1,6 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
# CONFIG_A is not set
CONFIG_B=y
@@ -0,0 +1,32 @@
# SPDX-License-Identifier: GPL-2.0
# Test Kconfig file for conditional dependencies.
# Enable module support for tristate testing
config MODULES
bool "Enable loadable module support"
modules
default y
config FOO
bool "FOO symbol"
config BAR
bool "BAR symbol"
config TEST_BASIC
bool "Test basic conditional dependency"
depends on FOO if BAR
default y
config TEST_COMPLEX
bool "Test complex conditional dependency"
depends on (FOO && BAR) if (FOO || BAR)
default y
config BAZ
tristate "BAZ symbol"
config TEST_OPTIONAL
tristate "Test simple optional dependency"
depends on BAZ if BAZ
default y
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
"""
Correctly handle conditional dependencies.
"""
def test(conf):
assert conf.oldconfig('test_config1') == 0
assert conf.config_matches('expected_config1')
assert conf.oldconfig('test_config2') == 0
assert conf.config_matches('expected_config2')
assert conf.oldconfig('test_config3') == 0
assert conf.config_matches('expected_config3')
@@ -0,0 +1,11 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
CONFIG_MODULES=y
CONFIG_FOO=y
CONFIG_BAR=y
CONFIG_TEST_BASIC=y
CONFIG_TEST_COMPLEX=y
CONFIG_BAZ=m
CONFIG_TEST_OPTIONAL=m
@@ -0,0 +1,9 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
CONFIG_MODULES=y
# CONFIG_FOO is not set
CONFIG_BAR=y
CONFIG_BAZ=y
CONFIG_TEST_OPTIONAL=y
@@ -0,0 +1,11 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
CONFIG_MODULES=y
# CONFIG_FOO is not set
# CONFIG_BAR is not set
CONFIG_TEST_BASIC=y
CONFIG_TEST_COMPLEX=y
# CONFIG_BAZ is not set
CONFIG_TEST_OPTIONAL=y
@@ -0,0 +1,6 @@
# Basic check that everything can be configured if selected.
CONFIG_FOO=y
CONFIG_BAR=y
CONFIG_BAZ=m
# Ensure that TEST_OPTIONAL=y with BAZ=m is converted to TEST_OPTIONAL=m
CONFIG_TEST_OPTIONAL=y
@@ -0,0 +1,7 @@
# If FOO is not selected, then TEST_BASIC should fail the conditional
# dependency since BAR is set.
# TEST_COMPLEX will fail dependency as it depends on both FOO and BAR
# if either of those is selected.
CONFIG_FOO=n
CONFIG_BAR=y
CONFIG_BAZ=y
@@ -0,0 +1,6 @@
# If FOO is not selected, but BAR is also not selected, then TEST_BASIC
# should pass since the dependency on FOO is conditional on BAR.
# TEST_COMPLEX should be also set since neither FOO nor BAR are selected
# so it has no dependencies.
CONFIG_FOO=n
CONFIG_BAR=n
+314
View File
@@ -0,0 +1,314 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com>
#
"""
Kconfig unit testing framework.
This provides fixture functions commonly used from test files.
"""
import os
import pytest
import shutil
import subprocess
import tempfile
CONF_PATH = os.path.abspath(os.path.join('scripts', 'kconfig', 'conf'))
class Conf:
"""Kconfig runner and result checker.
This class provides methods to run text-based interface of Kconfig
(scripts/kconfig/conf) and retrieve the resulted configuration,
stdout, and stderr. It also provides methods to compare those
results with expectations.
"""
def __init__(self, request):
"""Create a new Conf instance.
request: object to introspect the requesting test module
"""
# the directory of the test being run
self._test_dir = os.path.dirname(str(request.fspath))
# runners
def _run_conf(self, mode, dot_config=None, out_file='.config',
interactive=False, in_keys=None, extra_env={}):
"""Run text-based Kconfig executable and save the result.
mode: input mode option (--oldaskconfig, --defconfig=<file> etc.)
dot_config: .config file to use for configuration base
out_file: file name to contain the output config data
interactive: flag to specify the interactive mode
in_keys: key inputs for interactive modes
extra_env: additional environments
returncode: exit status of the Kconfig executable
"""
command = [CONF_PATH, mode, 'Kconfig']
# Override 'srctree' environment to make the test as the top directory
extra_env['srctree'] = self._test_dir
# Clear KCONFIG_DEFCONFIG_LIST to keep unit tests from being affected
# by the user's environment.
extra_env['KCONFIG_DEFCONFIG_LIST'] = ''
# Run Kconfig in a temporary directory.
# This directory is automatically removed when done.
with tempfile.TemporaryDirectory() as temp_dir:
# if .config is given, copy it to the working directory
if dot_config:
shutil.copyfile(os.path.join(self._test_dir, dot_config),
os.path.join(temp_dir, '.config'))
ps = subprocess.Popen(command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=temp_dir,
env=dict(os.environ, **extra_env))
# If input key sequence is given, feed it to stdin.
if in_keys:
ps.stdin.write(in_keys.encode('utf-8'))
while ps.poll() is None:
# For interactive modes such as oldaskconfig, oldconfig,
# send 'Enter' key until the program finishes.
if interactive:
try:
ps.stdin.write(b'\n')
ps.stdin.flush()
except (BrokenPipeError, OSError):
# Process has exited, stop sending input
break
# Close stdin gracefully
try:
ps.stdin.close()
except (BrokenPipeError, OSError):
# Ignore broken pipe on close
pass
# Wait for process to complete
ps.wait()
self.retcode = ps.returncode
self.stdout = ps.stdout.read().decode()
self.stderr = ps.stderr.read().decode()
# Retrieve the resulted config data only when .config is supposed
# to exist. If the command fails, the .config does not exist.
# 'listnewconfig' does not produce .config in the first place.
if self.retcode == 0 and out_file:
with open(os.path.join(temp_dir, out_file)) as f:
self.config = f.read()
else:
self.config = None
# Logging:
# Pytest captures the following information by default. In failure
# of tests, the captured log will be displayed. This will be useful to
# figure out what has happened.
print("[command]\n{}\n".format(' '.join(command)))
print("[retcode]\n{}\n".format(self.retcode))
print("[stdout]")
print(self.stdout)
print("[stderr]")
print(self.stderr)
if self.config is not None:
print("[output for '{}']".format(out_file))
print(self.config)
return self.retcode
def oldaskconfig(self, dot_config=None, in_keys=None):
"""Run oldaskconfig.
dot_config: .config file to use for configuration base (optional)
in_key: key inputs (optional)
returncode: exit status of the Kconfig executable
"""
return self._run_conf('--oldaskconfig', dot_config=dot_config,
interactive=True, in_keys=in_keys)
def oldconfig(self, dot_config=None, in_keys=None):
"""Run oldconfig.
dot_config: .config file to use for configuration base (optional)
in_key: key inputs (optional)
returncode: exit status of the Kconfig executable
"""
return self._run_conf('--oldconfig', dot_config=dot_config,
interactive=True, in_keys=in_keys)
def olddefconfig(self, dot_config=None):
"""Run olddefconfig.
dot_config: .config file to use for configuration base (optional)
returncode: exit status of the Kconfig executable
"""
return self._run_conf('--olddefconfig', dot_config=dot_config)
def defconfig(self, defconfig):
"""Run defconfig.
defconfig: defconfig file for input
returncode: exit status of the Kconfig executable
"""
defconfig_path = os.path.join(self._test_dir, defconfig)
return self._run_conf('--defconfig={}'.format(defconfig_path))
def _allconfig(self, mode, all_config, extra_env={}):
if all_config:
all_config_path = os.path.join(self._test_dir, all_config)
extra_env['KCONFIG_ALLCONFIG'] = all_config_path
return self._run_conf('--{}config'.format(mode), extra_env=extra_env)
def allyesconfig(self, all_config=None):
"""Run allyesconfig.
all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
returncode: exit status of the Kconfig executable
"""
return self._allconfig('allyes', all_config)
def allmodconfig(self, all_config=None):
"""Run allmodconfig.
all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
returncode: exit status of the Kconfig executable
"""
return self._allconfig('allmod', all_config)
def allnoconfig(self, all_config=None):
"""Run allnoconfig.
all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
returncode: exit status of the Kconfig executable
"""
return self._allconfig('allno', all_config)
def alldefconfig(self, all_config=None):
"""Run alldefconfig.
all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
returncode: exit status of the Kconfig executable
"""
return self._allconfig('alldef', all_config)
def randconfig(self, all_config=None, seed=None):
"""Run randconfig.
all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
seed: the seed for randconfig (optional)
returncode: exit status of the Kconfig executable
"""
if seed is not None:
extra_env = {'KCONFIG_SEED': hex(seed)}
else:
extra_env = {}
return self._allconfig('rand', all_config, extra_env=extra_env)
def savedefconfig(self, dot_config):
"""Run savedefconfig.
dot_config: .config file for input
returncode: exit status of the Kconfig executable
"""
return self._run_conf('--savedefconfig', out_file='defconfig')
def listnewconfig(self, dot_config=None):
"""Run listnewconfig.
dot_config: .config file to use for configuration base (optional)
returncode: exit status of the Kconfig executable
"""
return self._run_conf('--listnewconfig', dot_config=dot_config,
out_file=None)
# checkers
def _read_and_compare(self, compare, expected):
"""Compare the result with expectation.
compare: function to compare the result with expectation
expected: file that contains the expected data
"""
with open(os.path.join(self._test_dir, expected)) as f:
expected_data = f.read()
return compare(self, expected_data)
def _contains(self, attr, expected):
return self._read_and_compare(
lambda s, e: getattr(s, attr).find(e) >= 0,
expected)
def _matches(self, attr, expected):
return self._read_and_compare(lambda s, e: getattr(s, attr) == e,
expected)
def config_contains(self, expected):
"""Check if resulted configuration contains expected data.
expected: file that contains the expected data
returncode: True if result contains the expected data, False otherwise
"""
return self._contains('config', expected)
def config_matches(self, expected):
"""Check if resulted configuration exactly matches expected data.
expected: file that contains the expected data
returncode: True if result matches the expected data, False otherwise
"""
return self._matches('config', expected)
def stdout_contains(self, expected):
"""Check if resulted stdout contains expected data.
expected: file that contains the expected data
returncode: True if result contains the expected data, False otherwise
"""
return self._contains('stdout', expected)
def stdout_matches(self, expected):
"""Check if resulted stdout exactly matches expected data.
expected: file that contains the expected data
returncode: True if result matches the expected data, False otherwise
"""
return self._matches('stdout', expected)
def stderr_contains(self, expected):
"""Check if resulted stderr contains expected data.
expected: file that contains the expected data
returncode: True if result contains the expected data, False otherwise
"""
return self._contains('stderr', expected)
def stderr_matches(self, expected):
"""Check if resulted stderr exactly matches expected data.
expected: file that contains the expected data
returncode: True if result matches the expected data, False otherwise
"""
return self._matches('stderr', expected)
@pytest.fixture(scope="module")
def conf(request):
"""Create a Conf instance and provide it to test functions."""
return Conf(request)
@@ -0,0 +1,63 @@
# SPDX-License-Identifier: GPL-2.0
# depends on itself
config A
bool "A"
depends on A
# select itself
config B
bool
select B
# depends on each other
config C1
bool "C1"
depends on C2
config C2
bool "C2"
depends on C1
# depends on and select
config D1
bool "D1"
depends on D2
select D2
config D2
bool
# depends on and imply
config E1
bool "E1"
depends on E2
imply E2
config E2
bool "E2"
# property
config F1
bool "F1"
default F2
config F2
bool "F2"
depends on F1
# menu
menu "menu depending on its content"
depends on G
config G
bool "G"
endmenu
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
"""
Detect recursive dependency error.
Recursive dependency should be treated as an error.
"""
def test(conf):
assert conf.oldaskconfig() == 1
assert conf.stderr_contains('expected_stderr')
@@ -0,0 +1,38 @@
error: recursive dependency detected!
symbol A depends on A
For a resolution refer to Documentation/kbuild/kconfig-language.rst
subsection "Kconfig recursive dependency limitations"
error: recursive dependency detected!
symbol B is selected by B
For a resolution refer to Documentation/kbuild/kconfig-language.rst
subsection "Kconfig recursive dependency limitations"
error: recursive dependency detected!
symbol C1 depends on C2
symbol C2 depends on C1
For a resolution refer to Documentation/kbuild/kconfig-language.rst
subsection "Kconfig recursive dependency limitations"
error: recursive dependency detected!
symbol D1 depends on D2
symbol D2 is selected by D1
For a resolution refer to Documentation/kbuild/kconfig-language.rst
subsection "Kconfig recursive dependency limitations"
error: recursive dependency detected!
symbol E1 depends on E2
symbol E2 is implied by E1
For a resolution refer to Documentation/kbuild/kconfig-language.rst
subsection "Kconfig recursive dependency limitations"
error: recursive dependency detected!
symbol F1 default value contains F2
symbol F2 depends on F1
For a resolution refer to Documentation/kbuild/kconfig-language.rst
subsection "Kconfig recursive dependency limitations"
error: recursive dependency detected!
symbol G depends on G
For a resolution refer to Documentation/kbuild/kconfig-language.rst
subsection "Kconfig recursive dependency limitations"
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc1"
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc2"
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc3"
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc1"
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
"""
Detect recursive inclusion error.
If recursive inclusion is detected, it should fail with error messages.
"""
def test(conf):
assert conf.oldaskconfig() != 0
assert conf.stderr_contains('expected_stderr')
@@ -0,0 +1,6 @@
Recursive inclusion detected.
Inclusion path:
current file : Kconfig.inc1
included from: Kconfig.inc3:2
included from: Kconfig.inc2:4
included from: Kconfig.inc1:5
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc1"
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc2"
source "Kconfig.inc3"
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc3"
@@ -0,0 +1 @@
# SPDX-License-Identifier: GPL-2.0-only
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
"""
Detect repeated inclusion error.
If repeated inclusion is detected, it should fail with error message.
"""
def test(conf):
assert conf.oldaskconfig() != 0
assert conf.stderr_contains('expected_stderr')
@@ -0,0 +1,2 @@
Kconfig.inc1:4: error: repeated inclusion of Kconfig.inc3
Kconfig.inc2:3: note: location of first inclusion of Kconfig.inc3
@@ -0,0 +1,52 @@
# SPDX-License-Identifier: GPL-2.0
# Test that transitional symbols cannot have properties other than help
config BAD_DEFAULT
bool
transitional
default y
help
This transitional symbol illegally has a default property.
config BAD_PROMPT
bool
transitional
prompt "Bad prompt"
help
This transitional symbol illegally has a prompt.
config BAD_SELECT
bool
transitional
select OTHER_SYMBOL
help
This transitional symbol illegally has a select.
config BAD_IMPLY
bool
transitional
imply OTHER_SYMBOL
help
This transitional symbol illegally has an imply.
config BAD_DEPENDS
bool
transitional
depends on OTHER_SYMBOL
help
This transitional symbol illegally has a depends.
config BAD_RANGE
int
transitional
range 1 10
help
This transitional symbol illegally has a range.
config BAD_NO_TYPE
transitional
help
This transitional symbol illegally has no type specified.
config OTHER_SYMBOL
bool
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
"""
Test that transitional symbols with invalid properties are rejected.
Transitional symbols can only have help sections. Any other properties
(default, select, depends, etc.) should cause a parser error.
"""
def test(conf):
# This should fail with exit code 1 due to invalid transitional symbol
assert conf.olddefconfig() == 1
# Check that the error message is about transitional symbols
assert conf.stderr_contains('expected_stderr')
@@ -0,0 +1,7 @@
Kconfig:46:warning: config symbol defined without type
Kconfig:7: error: transitional symbols can only have help sections
Kconfig:14: error: transitional symbols can only have help sections
Kconfig:21: error: transitional symbols can only have help sections
Kconfig:28: error: transitional symbols can only have help sections
Kconfig:32: error: transitional symbols can only have help sections
Kconfig:42: error: transitional symbols can only have help sections
@@ -0,0 +1,39 @@
# SPDX-License-Identifier: GPL-2.0
config A
bool "A"
help
This is a new symbol.
choice
prompt "Choice ?"
depends on A
help
"depends on A" has been newly added.
config CHOICE_B
bool "Choice B"
config CHOICE_C
bool "Choice C"
help
This is a new symbol, so should be asked.
endchoice
choice
prompt "Choice2 ?"
config CHOICE_D
bool "Choice D"
config CHOICE_E
bool "Choice E"
config CHOICE_F
bool "Choice F"
depends on A
help
This is a new symbol, so should be asked.
endchoice
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
"""
Ask new choice values when they become visible.
If new choice values are added with new dependency, and they become
visible during user configuration, oldconfig should recognize them
as (NEW), and ask the user for choice.
Related Linux commit: 5d09598d488f081e3be23f885ed65cbbe2d073b5
"""
def test(conf):
assert conf.oldconfig('config', 'y') == 0
assert conf.stdout_contains('expected_stdout')
@@ -0,0 +1,3 @@
CONFIG_CHOICE_B=y
# CONFIG_CHOICE_D is not set
CONFIG_CHOICE_E=y
@@ -0,0 +1,10 @@
A (A) [N/y/?] (NEW) y
Choice ?
> 1. Choice B (CHOICE_B)
2. Choice C (CHOICE_C) (NEW)
choice[1-2?]:
Choice2 ?
1. Choice D (CHOICE_D)
> 2. Choice E (CHOICE_E)
3. Choice F (CHOICE_F) (NEW)
choice[1-3?]:
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
config A
bool "A"
choice
prompt "Choice ?"
depends on A
config CHOICE_B
bool "Choice B"
config CHOICE_C
bool "Choice C"
endchoice
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0
"""
Do not write choice values to .config if the dependency is unmet.
"# CONFIG_... is not set" should not be written into the .config file
for symbols with unmet dependency.
This was not working correctly for choice values because choice needs
a bit different symbol computation.
This checks that no unneeded "# COFIG_... is not set" is contained in
the .config file.
Related Linux commit: cb67ab2cd2b8abd9650292c986c79901e3073a59
"""
def test(conf):
assert conf.oldaskconfig('config', 'n') == 0
assert conf.config_matches('expected_config')
@@ -0,0 +1 @@
CONFIG_A=y
@@ -0,0 +1,5 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
# CONFIG_A is not set
@@ -0,0 +1,27 @@
# SPDX-License-Identifier: GPL-2.0
# 'info' prints the argument to stdout.
$(info,hello world 0)
# 'warning-if', if the first argument is y, sends the second argument to stderr,
# and the message is prefixed with the current file name and line number.
$(warning-if,y,hello world 1)
# 'error-if' is similar, but it terminates the parsing immediately.
# The following is just no-op since the first argument is not y.
$(error-if,n,this should not be printed)
# Shorthand
warning = $(warning-if,y,$(1))
# 'shell' executes a command, and returns its stdout.
$(warning,$(shell,echo hello world 3))
# Every newline in the output is replaced with a space,
# but any trailing newlines are deleted.
$(warning,$(shell,printf 'hello\nworld\n\n4\n\n\n'))
# 'filename' is expanded to the currently parsed file name,
# 'lineno' to the line number.
$(warning,filename=$(filename))
$(warning,lineno=$(lineno))
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
"""
Built-in function tests.
"""
def test(conf):
assert conf.oldaskconfig() == 0
assert conf.stdout_contains('expected_stdout')
assert conf.stderr_matches('expected_stderr')
@@ -0,0 +1,5 @@
Kconfig:8: hello world 1
Kconfig:18: hello world 3
Kconfig:22: hello world 4
Kconfig:26: filename=Kconfig
Kconfig:27: lineno=27
@@ -0,0 +1 @@
hello world 0
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
X = $(Y)
Y = $(X)
$(info $(X))
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
"""
Detect circular variable expansion.
If a recursively expanded variable references itself (eventually),
it should fail with an error message.
"""
def test(conf):
assert conf.oldaskconfig() != 0
assert conf.stderr_matches('expected_stderr')
@@ -0,0 +1 @@
Kconfig:5: Recursive variable 'X' references itself (eventually)
@@ -0,0 +1,44 @@
# SPDX-License-Identifier: GPL-2.0
# Shorthand
warning = $(warning-if,y,$(1))
# You can not pass commas directly to a function since they are treated as
# delimiters. You can use the following trick to do so.
comma := ,
$(warning,hello$(comma) world)
# Like Make, single quotes, double quotes, spaces are treated verbatim.
# The following prints the text as-is.
$(warning, ' " '" ' ''' "'")
# Unlike Make, '$' has special meaning only when it is followed by '('.
# No need to escape '$' itself.
$(warning,$)
$(warning,$$)
$ := 1
$(warning,$($))
# You need a trick to escape '$' followed by '('
# The following should print "$(X)". It should not be expanded further.
dollar := $
$(warning,$(dollar)(X))
# You need a trick to treat unbalanced parentheses.
# The following should print "(".
left_paren := (
$(warning,$(left_paren))
# A simple expanded should not be expanded multiple times.
# The following should print "$(X)". It should not be expanded further.
Y := $(dollar)(X)
$(warning,$(Y))
# The following should print "$(X)" as well.
Y = $(dollar)(X)
$(warning,$(Y))
# The following should print "$(".
# It should not be emit "unterminated reference" error.
unterminated := $(dollar)(
$(warning,$(unterminated))
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
"""
Escape sequence tests.
"""
def test(conf):
assert conf.oldaskconfig() == 0
assert conf.stderr_matches('expected_stderr')
@@ -0,0 +1,10 @@
Kconfig:9: hello, world
Kconfig:13: ' " '" ' ''' "'"
Kconfig:17: $
Kconfig:18: $$
Kconfig:20: 1
Kconfig:25: $(X)
Kconfig:30: (
Kconfig:35: $(X)
Kconfig:39: $(X)
Kconfig:44: $(
@@ -0,0 +1,53 @@
# SPDX-License-Identifier: GPL-2.0
# Shorthand
warning = $(warning-if,y,$(1))
# Simply expanded variable.
X := 1
SIMPLE := $(X)
X := 2
$(warning,SIMPLE = $(SIMPLE))
# Recursively expanded variable.
X := 1
RECURSIVE = $(X)
X := 2
$(warning,RECURSIVE = $(RECURSIVE))
# Append something to a simply expanded variable.
Y := 3
SIMPLE += $(Y)
Y := 4
$(warning,SIMPLE = $(SIMPLE))
# Append something to a recursively expanded variable.
Y := 3
RECURSIVE += $(Y)
Y := 4
$(warning,RECURSIVE = $(RECURSIVE))
# Use += operator to an undefined variable.
# This works as a recursively expanded variable.
Y := 3
UNDEFINED_VARIABLE += $(Y)
Y := 4
$(warning,UNDEFINED_VARIABLE = $(UNDEFINED_VARIABLE))
# You can use variable references for the lefthand side of assignment statement.
X := A
Y := B
$(X)$(Y) := 5
$(warning,AB = $(AB))
# User-defined function.
greeting = $(1), my name is $(2).
$(warning,$(greeting,Hello,John))
# The number of arguments is not checked for user-defined functions.
# If some arguments are optional, it is useful to pass fewer parameters.
# $(2) will be blank in this case.
$(warning,$(greeting,Hello))
# Unreferenced parameters are just ignored.
$(warning,$(greeting,Hello,John,ignored,ignored))
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
"""
Variable and user-defined function tests.
"""
def test(conf):
assert conf.oldaskconfig() == 0
assert conf.stderr_matches('expected_stderr')
@@ -0,0 +1,9 @@
Kconfig:10: SIMPLE = 1
Kconfig:16: RECURSIVE = 2
Kconfig:22: SIMPLE = 1 3
Kconfig:28: RECURSIVE = 2 4
Kconfig:35: UNDEFINED_VARIABLE = 4
Kconfig:41: AB = 5
Kconfig:45: Hello, my name is John.
Kconfig:50: Hello, my name is .
Kconfig:53: Hello, my name is John.
+7
View File
@@ -0,0 +1,7 @@
[pytest]
addopts = --verbose
# Pytest requires that test files have unique names, because pytest imports
# them as top-level modules. It is silly to prefix or suffix a test file with
# the directory name that contains it. Use __init__.py for all test files.
python_files = __init__.py
+132
View File
@@ -0,0 +1,132 @@
# SPDX-License-Identifier: GPL-2.0
# Test transitional symbols for config migration with all Kconfig types
# Enable module support for tristate testing
config MODULES
bool "Enable loadable module support"
modules
default y
# Basic migration tests for all types
config NEW_BOOL
bool "New bool option"
default OLD_BOOL
config OLD_BOOL
bool
transitional
config NEW_TRISTATE
tristate "New tristate option"
default OLD_TRISTATE
config OLD_TRISTATE
tristate
transitional
config NEW_STRING
string "New string option"
default OLD_STRING
config OLD_STRING
string
transitional
config NEW_HEX
hex "New hex option"
default OLD_HEX
config OLD_HEX
hex
transitional
config NEW_INT
int "New int option"
default OLD_INT
config OLD_INT
int
transitional
# Precedence tests for all types
config NEW_BOOL_PRECEDENCE
bool "New bool option with precedence"
default OLD_BOOL_PRECEDENCE
config OLD_BOOL_PRECEDENCE
bool
transitional
config NEW_STRING_PRECEDENCE
string "New string option with precedence"
default OLD_STRING_PRECEDENCE
config OLD_STRING_PRECEDENCE
string
transitional
config NEW_TRISTATE_PRECEDENCE
tristate "New tristate option with precedence"
default OLD_TRISTATE_PRECEDENCE
config OLD_TRISTATE_PRECEDENCE
tristate
transitional
config NEW_HEX_PRECEDENCE
hex "New hex option with precedence"
default OLD_HEX_PRECEDENCE
config OLD_HEX_PRECEDENCE
hex
transitional
config NEW_INT_PRECEDENCE
int "New int option with precedence"
default OLD_INT_PRECEDENCE
config OLD_INT_PRECEDENCE
int
transitional
# Test that help sections are allowed for transitional symbols
config OLD_WITH_HELP
bool
transitional
help
This transitional symbol has a help section to validate that help is allowed.
# Test that we can set something to =n via transitional symbol
config NEW_DISABLED
tristate "Check for setting to disabled"
default OLD_DISABLED
config OLD_DISABLED
tristate
transitional
# Test that a potential new value disappears if it lacks a prompt
config NEW_DISABLED_UNSAVED
tristate
default OLD_DISABLED
config OLD_DISABLED_UNSAVED
tristate
transitional
# Test conditional default: transitional value should not prevent prompting
# when default visibility makes the expression evaluate to 'no'
config DEPENDENCY_TEST
bool "Dependency for testing"
default n
config NEW_CONDITIONAL_DEFAULT
bool "New option with conditional default"
default OLD_CONDITIONAL_DEFAULT if DEPENDENCY_TEST
config OLD_CONDITIONAL_DEFAULT
bool
transitional
config REGULAR_OPTION
bool "Regular option"
@@ -0,0 +1,25 @@
# SPDX-License-Identifier: GPL-2.0
"""
Test transitional symbol migration functionality for all Kconfig types.
This tests that:
- OLD_* options in existing .config cause NEW_* options to be set
- OLD_* options are not written to the new .config file
- NEW_* options appear in the new .config file with correct values
- NEW_* options with defaults from transitional symbols are not prompted
- All Kconfig types work correctly: bool, tristate, string, hex, int
- User-set NEW values take precedence over conflicting OLD transitional values
"""
def test(conf):
# Run olddefconfig to process the migration with the initial config
assert conf.olddefconfig(dot_config='initial_config') == 0
# Check that the configuration matches expected output
assert conf.config_contains('expected_config')
# Test oldconfig to ensure symbols with transitional defaults are not prompted
assert conf.oldconfig(dot_config='initial_config', in_keys='n\n') == 0
# Except for when conditional default evaluates to 'no'
assert conf.stdout_contains('expected_stdout')
@@ -0,0 +1,15 @@
CONFIG_MODULES=y
CONFIG_NEW_BOOL=y
CONFIG_NEW_TRISTATE=m
CONFIG_NEW_STRING="test string"
CONFIG_NEW_HEX=0x1234
CONFIG_NEW_INT=42
# CONFIG_NEW_BOOL_PRECEDENCE is not set
CONFIG_NEW_STRING_PRECEDENCE="user value"
CONFIG_NEW_TRISTATE_PRECEDENCE=y
CONFIG_NEW_HEX_PRECEDENCE=0xABCD
CONFIG_NEW_INT_PRECEDENCE=100
# CONFIG_NEW_DISABLED is not set
# CONFIG_DEPENDENCY_TEST is not set
# CONFIG_NEW_CONDITIONAL_DEFAULT is not set
# CONFIG_REGULAR_OPTION is not set
@@ -0,0 +1 @@
New option with conditional default (NEW_CONDITIONAL_DEFAULT) [N/y/?] (NEW) n
@@ -0,0 +1,20 @@
CONFIG_MODULES=y
CONFIG_OLD_BOOL=y
CONFIG_OLD_TRISTATE=m
CONFIG_OLD_STRING="test string"
CONFIG_OLD_HEX=0x1234
CONFIG_OLD_INT=42
# CONFIG_NEW_BOOL_PRECEDENCE is not set
CONFIG_OLD_BOOL_PRECEDENCE=y
CONFIG_NEW_STRING_PRECEDENCE="user value"
CONFIG_OLD_STRING_PRECEDENCE="old value"
CONFIG_NEW_TRISTATE_PRECEDENCE=y
CONFIG_OLD_TRISTATE_PRECEDENCE=m
CONFIG_NEW_HEX_PRECEDENCE=0xABCD
CONFIG_OLD_HEX_PRECEDENCE=0x5678
CONFIG_NEW_INT_PRECEDENCE=100
CONFIG_OLD_INT_PRECEDENCE=200
# CONFIG_OLD_DISABLED is not set
# CONFIG_OLD_DISABLED_UNSAVED is not set
# CONFIG_DEPENDENCY_TEST is not set
CONFIG_OLD_CONDITIONAL_DEFAULT=y