summaryrefslogtreecommitdiffstats
path: root/scripts/kconfig/tests
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-02 12:17:24 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-02 12:17:24 -0700
commit3f1fe48a36b0b6722dc3fd421d93512bac138e9a (patch)
treeb767d7f6e26bc64334d3f14f8422d187239dc45d /scripts/kconfig/tests
downloadlinux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.tar.gz
linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.zip
Merge tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linuxgrafted
Pull io_uring fixes from Jens Axboe: - Fix a task_work add use-after-free with SQPOLL. The sqpoll thread could pop and complete the last request while io_req_normal_work_add() was still looking at them after the mpscq push. Use the same approach as DEFER_TASKRUN to protect from that, holding an RCU read lock across the add, and have exit wait for an RCU grace period for SQPOLL rings as well. - CQE32 ring fixes: correct the free entry check for 32b CQEs, zero the big_cqe for aux CQEs, and only post the dummy skip CQE on CQE_MIXED rings - Mark the source filter table as COW when cloning bpf filters, so registering another filter on the source doesn't modify the shared table in place - Initialize the task context before running the BPF loop - Requeue zcrx multishot receives stopped by a local resource - End a TX_TIMESTAMP multishot cmd when the CQ is full (lollipopkit) * tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux: io_uring: fix task_work add use-after-free with SQPOLL io_uring/cmd_net: end TX_TIMESTAMP multishot when the CQ is full io_uring/zcrx: requeue multishot receives stopped by a local resource io_uring: initialize task context before running the BPF loop io_uring: zero big_cqe for aux CQEs on CQE32 rings io_uring: fix free entry check for 32b CQEs on CQE32 rings io_uring: only post the dummy skip CQE on CQE_MIXED rings io_uring/bpf_filter: mark source as COW when cloning filters
Diffstat (limited to 'scripts/kconfig/tests')
-rw-r--r--scripts/kconfig/tests/auto_submenu/Kconfig52
-rw-r--r--scripts/kconfig/tests/auto_submenu/__init__.py13
-rw-r--r--scripts/kconfig/tests/auto_submenu/expected_stdout10
-rw-r--r--scripts/kconfig/tests/choice/Kconfig13
-rw-r--r--scripts/kconfig/tests/choice/__init__.py29
-rw-r--r--scripts/kconfig/tests/choice/alldef_expected_config2
-rw-r--r--scripts/kconfig/tests/choice/allmod_expected_config2
-rw-r--r--scripts/kconfig/tests/choice/allno_expected_config2
-rw-r--r--scripts/kconfig/tests/choice/allyes_expected_config2
-rw-r--r--scripts/kconfig/tests/choice/oldask0_expected_stdout4
-rw-r--r--scripts/kconfig/tests/choice_randomize/Kconfig22
-rw-r--r--scripts/kconfig/tests/choice_randomize/__init__.py34
-rw-r--r--scripts/kconfig/tests/choice_randomize/expected_config06
-rw-r--r--scripts/kconfig/tests/choice_randomize/expected_config18
-rw-r--r--scripts/kconfig/tests/choice_randomize/expected_config28
-rw-r--r--scripts/kconfig/tests/choice_randomize2/Kconfig32
-rw-r--r--scripts/kconfig/tests/choice_randomize2/__init__.py18
-rw-r--r--scripts/kconfig/tests/choice_randomize2/expected_config08
-rw-r--r--scripts/kconfig/tests/choice_randomize2/expected_config17
-rw-r--r--scripts/kconfig/tests/choice_randomize2/expected_config26
-rw-r--r--scripts/kconfig/tests/conditional_dep/Kconfig32
-rw-r--r--scripts/kconfig/tests/conditional_dep/__init__.py14
-rw-r--r--scripts/kconfig/tests/conditional_dep/expected_config111
-rw-r--r--scripts/kconfig/tests/conditional_dep/expected_config29
-rw-r--r--scripts/kconfig/tests/conditional_dep/expected_config311
-rw-r--r--scripts/kconfig/tests/conditional_dep/test_config16
-rw-r--r--scripts/kconfig/tests/conditional_dep/test_config27
-rw-r--r--scripts/kconfig/tests/conditional_dep/test_config36
-rw-r--r--scripts/kconfig/tests/conftest.py318
-rw-r--r--scripts/kconfig/tests/err_recursive_dep/Kconfig63
-rw-r--r--scripts/kconfig/tests/err_recursive_dep/__init__.py10
-rw-r--r--scripts/kconfig/tests/err_recursive_dep/expected_stderr38
-rw-r--r--scripts/kconfig/tests/err_recursive_inc/Kconfig2
-rw-r--r--scripts/kconfig/tests/err_recursive_inc/Kconfig.inc15
-rw-r--r--scripts/kconfig/tests/err_recursive_inc/Kconfig.inc24
-rw-r--r--scripts/kconfig/tests/err_recursive_inc/Kconfig.inc32
-rw-r--r--scripts/kconfig/tests/err_recursive_inc/__init__.py11
-rw-r--r--scripts/kconfig/tests/err_recursive_inc/expected_stderr6
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/Kconfig3
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/Kconfig.inc14
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/Kconfig.inc23
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/Kconfig.inc31
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/__init__.py10
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/expected_stderr2
-rw-r--r--scripts/kconfig/tests/err_transitional/Kconfig52
-rw-r--r--scripts/kconfig/tests/err_transitional/__init__.py14
-rw-r--r--scripts/kconfig/tests/err_transitional/expected_stderr7
-rw-r--r--scripts/kconfig/tests/new_choice_with_dep/Kconfig39
-rw-r--r--scripts/kconfig/tests/new_choice_with_dep/__init__.py15
-rw-r--r--scripts/kconfig/tests/new_choice_with_dep/config3
-rw-r--r--scripts/kconfig/tests/new_choice_with_dep/expected_stdout10
-rw-r--r--scripts/kconfig/tests/no_write_if_dep_unmet/Kconfig16
-rw-r--r--scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py20
-rw-r--r--scripts/kconfig/tests/no_write_if_dep_unmet/config1
-rw-r--r--scripts/kconfig/tests/no_write_if_dep_unmet/expected_config5
-rw-r--r--scripts/kconfig/tests/preprocess/builtin_func/Kconfig27
-rw-r--r--scripts/kconfig/tests/preprocess/builtin_func/__init__.py9
-rw-r--r--scripts/kconfig/tests/preprocess/builtin_func/expected_stderr5
-rw-r--r--scripts/kconfig/tests/preprocess/builtin_func/expected_stdout1
-rw-r--r--scripts/kconfig/tests/preprocess/circular_expansion/Kconfig5
-rw-r--r--scripts/kconfig/tests/preprocess/circular_expansion/__init__.py11
-rw-r--r--scripts/kconfig/tests/preprocess/circular_expansion/expected_stderr1
-rw-r--r--scripts/kconfig/tests/preprocess/escape/Kconfig44
-rw-r--r--scripts/kconfig/tests/preprocess/escape/__init__.py8
-rw-r--r--scripts/kconfig/tests/preprocess/escape/expected_stderr10
-rw-r--r--scripts/kconfig/tests/preprocess/variable/Kconfig53
-rw-r--r--scripts/kconfig/tests/preprocess/variable/__init__.py8
-rw-r--r--scripts/kconfig/tests/preprocess/variable/expected_stderr9
-rw-r--r--scripts/kconfig/tests/pytest.ini7
-rw-r--r--scripts/kconfig/tests/transitional/Kconfig132
-rw-r--r--scripts/kconfig/tests/transitional/__init__.py25
-rw-r--r--scripts/kconfig/tests/transitional/expected_config15
-rw-r--r--scripts/kconfig/tests/transitional/expected_stdout1
-rw-r--r--scripts/kconfig/tests/transitional/initial_config20
-rw-r--r--scripts/kconfig/tests/warn_changed_input/Kconfig40
-rw-r--r--scripts/kconfig/tests/warn_changed_input/__init__.py33
-rw-r--r--scripts/kconfig/tests/warn_changed_input/config3
-rw-r--r--scripts/kconfig/tests/warn_changed_input/expected_config6
-rw-r--r--scripts/kconfig/tests/warn_changed_input/expected_defconfig1
-rw-r--r--scripts/kconfig/tests/warn_changed_input/expected_stderr4
80 files changed, 1516 insertions, 0 deletions
diff --git a/scripts/kconfig/tests/auto_submenu/Kconfig b/scripts/kconfig/tests/auto_submenu/Kconfig
new file mode 100644
index 000000000..b20761edc
--- /dev/null
+++ b/scripts/kconfig/tests/auto_submenu/Kconfig
@@ -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.
diff --git a/scripts/kconfig/tests/auto_submenu/__init__.py b/scripts/kconfig/tests/auto_submenu/__init__.py
new file mode 100644
index 000000000..25abd927f
--- /dev/null
+++ b/scripts/kconfig/tests/auto_submenu/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/auto_submenu/expected_stdout b/scripts/kconfig/tests/auto_submenu/expected_stdout
new file mode 100644
index 000000000..bf5236f39
--- /dev/null
+++ b/scripts/kconfig/tests/auto_submenu/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)
diff --git a/scripts/kconfig/tests/choice/Kconfig b/scripts/kconfig/tests/choice/Kconfig
new file mode 100644
index 000000000..cd252579a
--- /dev/null
+++ b/scripts/kconfig/tests/choice/Kconfig
@@ -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
diff --git a/scripts/kconfig/tests/choice/__init__.py b/scripts/kconfig/tests/choice/__init__.py
new file mode 100644
index 000000000..0fc7bf9b5
--- /dev/null
+++ b/scripts/kconfig/tests/choice/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/choice/alldef_expected_config b/scripts/kconfig/tests/choice/alldef_expected_config
new file mode 100644
index 000000000..b359a2e64
--- /dev/null
+++ b/scripts/kconfig/tests/choice/alldef_expected_config
@@ -0,0 +1,2 @@
+# CONFIG_BOOL_CHOICE0 is not set
+CONFIG_BOOL_CHOICE1=y
diff --git a/scripts/kconfig/tests/choice/allmod_expected_config b/scripts/kconfig/tests/choice/allmod_expected_config
new file mode 100644
index 000000000..b359a2e64
--- /dev/null
+++ b/scripts/kconfig/tests/choice/allmod_expected_config
@@ -0,0 +1,2 @@
+# CONFIG_BOOL_CHOICE0 is not set
+CONFIG_BOOL_CHOICE1=y
diff --git a/scripts/kconfig/tests/choice/allno_expected_config b/scripts/kconfig/tests/choice/allno_expected_config
new file mode 100644
index 000000000..b359a2e64
--- /dev/null
+++ b/scripts/kconfig/tests/choice/allno_expected_config
@@ -0,0 +1,2 @@
+# CONFIG_BOOL_CHOICE0 is not set
+CONFIG_BOOL_CHOICE1=y
diff --git a/scripts/kconfig/tests/choice/allyes_expected_config b/scripts/kconfig/tests/choice/allyes_expected_config
new file mode 100644
index 000000000..b359a2e64
--- /dev/null
+++ b/scripts/kconfig/tests/choice/allyes_expected_config
@@ -0,0 +1,2 @@
+# CONFIG_BOOL_CHOICE0 is not set
+CONFIG_BOOL_CHOICE1=y
diff --git a/scripts/kconfig/tests/choice/oldask0_expected_stdout b/scripts/kconfig/tests/choice/oldask0_expected_stdout
new file mode 100644
index 000000000..80ec34c61
--- /dev/null
+++ b/scripts/kconfig/tests/choice/oldask0_expected_stdout
@@ -0,0 +1,4 @@
+boolean choice
+ 1. choice 0 (BOOL_CHOICE0) (NEW)
+> 2. choice 1 (BOOL_CHOICE1) (NEW)
+choice[1-2?]:
diff --git a/scripts/kconfig/tests/choice_randomize/Kconfig b/scripts/kconfig/tests/choice_randomize/Kconfig
new file mode 100644
index 000000000..93a1699ce
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize/Kconfig
@@ -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
diff --git a/scripts/kconfig/tests/choice_randomize/__init__.py b/scripts/kconfig/tests/choice_randomize/__init__.py
new file mode 100644
index 000000000..d380045be
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize/__init__.py
@@ -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
diff --git a/scripts/kconfig/tests/choice_randomize/expected_config0 b/scripts/kconfig/tests/choice_randomize/expected_config0
new file mode 100644
index 000000000..f69227323
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize/expected_config0
@@ -0,0 +1,6 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Main menu
+#
+CONFIG_A=y
+# CONFIG_B is not set
diff --git a/scripts/kconfig/tests/choice_randomize/expected_config1 b/scripts/kconfig/tests/choice_randomize/expected_config1
new file mode 100644
index 000000000..bf83784c9
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize/expected_config1
@@ -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
diff --git a/scripts/kconfig/tests/choice_randomize/expected_config2 b/scripts/kconfig/tests/choice_randomize/expected_config2
new file mode 100644
index 000000000..38f93a8f3
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize/expected_config2
@@ -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
diff --git a/scripts/kconfig/tests/choice_randomize2/Kconfig b/scripts/kconfig/tests/choice_randomize2/Kconfig
new file mode 100644
index 000000000..530cf2ef7
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize2/Kconfig
@@ -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
diff --git a/scripts/kconfig/tests/choice_randomize2/__init__.py b/scripts/kconfig/tests/choice_randomize2/__init__.py
new file mode 100644
index 000000000..2066757b8
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize2/__init__.py
@@ -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'))
diff --git a/scripts/kconfig/tests/choice_randomize2/expected_config0 b/scripts/kconfig/tests/choice_randomize2/expected_config0
new file mode 100644
index 000000000..5c9e1c172
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize2/expected_config0
@@ -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
diff --git a/scripts/kconfig/tests/choice_randomize2/expected_config1 b/scripts/kconfig/tests/choice_randomize2/expected_config1
new file mode 100644
index 000000000..5b975d91b
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize2/expected_config1
@@ -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
diff --git a/scripts/kconfig/tests/choice_randomize2/expected_config2 b/scripts/kconfig/tests/choice_randomize2/expected_config2
new file mode 100644
index 000000000..5a5ebb90d
--- /dev/null
+++ b/scripts/kconfig/tests/choice_randomize2/expected_config2
@@ -0,0 +1,6 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Main menu
+#
+# CONFIG_A is not set
+CONFIG_B=y
diff --git a/scripts/kconfig/tests/conditional_dep/Kconfig b/scripts/kconfig/tests/conditional_dep/Kconfig
new file mode 100644
index 000000000..2015dfbce
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/Kconfig
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/__init__.py b/scripts/kconfig/tests/conditional_dep/__init__.py
new file mode 100644
index 000000000..ab16df648
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/conditional_dep/expected_config1 b/scripts/kconfig/tests/conditional_dep/expected_config1
new file mode 100644
index 000000000..826ed7f54
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/expected_config1
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/expected_config2 b/scripts/kconfig/tests/conditional_dep/expected_config2
new file mode 100644
index 000000000..10d2354f6
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/expected_config2
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/expected_config3 b/scripts/kconfig/tests/conditional_dep/expected_config3
new file mode 100644
index 000000000..b04fa6fdf
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/expected_config3
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/test_config1 b/scripts/kconfig/tests/conditional_dep/test_config1
new file mode 100644
index 000000000..9b05f3ce8
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/test_config1
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/test_config2 b/scripts/kconfig/tests/conditional_dep/test_config2
new file mode 100644
index 000000000..5e66d230a
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/test_config2
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/test_config3 b/scripts/kconfig/tests/conditional_dep/test_config3
new file mode 100644
index 000000000..86304f3aa
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/test_config3
@@ -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
diff --git a/scripts/kconfig/tests/conftest.py b/scripts/kconfig/tests/conftest.py
new file mode 100644
index 000000000..66f95e4ed
--- /dev/null
+++ b/scripts/kconfig/tests/conftest.py
@@ -0,0 +1,318 @@
+# 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={},
+ silent=False):
+ """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]
+ if silent:
+ command.append('-s')
+ command += [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)
diff --git a/scripts/kconfig/tests/err_recursive_dep/Kconfig b/scripts/kconfig/tests/err_recursive_dep/Kconfig
new file mode 100644
index 000000000..ebdb3ffd8
--- /dev/null
+++ b/scripts/kconfig/tests/err_recursive_dep/Kconfig
@@ -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
diff --git a/scripts/kconfig/tests/err_recursive_dep/__init__.py b/scripts/kconfig/tests/err_recursive_dep/__init__.py
new file mode 100644
index 000000000..5f3821b43
--- /dev/null
+++ b/scripts/kconfig/tests/err_recursive_dep/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/err_recursive_dep/expected_stderr b/scripts/kconfig/tests/err_recursive_dep/expected_stderr
new file mode 100644
index 000000000..fc2e860af
--- /dev/null
+++ b/scripts/kconfig/tests/err_recursive_dep/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"
diff --git a/scripts/kconfig/tests/err_recursive_inc/Kconfig b/scripts/kconfig/tests/err_recursive_inc/Kconfig
new file mode 100644
index 000000000..c6f4adec7
--- /dev/null
+++ b/scripts/kconfig/tests/err_recursive_inc/Kconfig
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+source "Kconfig.inc1"
diff --git a/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc1 b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc1
new file mode 100644
index 000000000..01cbf0d69
--- /dev/null
+++ b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc1
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+
+
+source "Kconfig.inc2"
diff --git a/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc2 b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc2
new file mode 100644
index 000000000..82351075a
--- /dev/null
+++ b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc2
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+
+source "Kconfig.inc3"
diff --git a/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc3 b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc3
new file mode 100644
index 000000000..c6f4adec7
--- /dev/null
+++ b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc3
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+source "Kconfig.inc1"
diff --git a/scripts/kconfig/tests/err_recursive_inc/__init__.py b/scripts/kconfig/tests/err_recursive_inc/__init__.py
new file mode 100644
index 000000000..27aa1895a
--- /dev/null
+++ b/scripts/kconfig/tests/err_recursive_inc/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/err_recursive_inc/expected_stderr b/scripts/kconfig/tests/err_recursive_inc/expected_stderr
new file mode 100644
index 000000000..b070a31fd
--- /dev/null
+++ b/scripts/kconfig/tests/err_recursive_inc/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
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig b/scripts/kconfig/tests/err_repeated_inc/Kconfig
new file mode 100644
index 000000000..09a88fd29
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "Kconfig.inc1"
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc1 b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc1
new file mode 100644
index 000000000..495dc3831
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc1
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "Kconfig.inc2"
+source "Kconfig.inc3"
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc2 b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc2
new file mode 100644
index 000000000..2b630eec2
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc2
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "Kconfig.inc3"
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc3 b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc3
new file mode 100644
index 000000000..a4e40e534
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc3
@@ -0,0 +1 @@
+# SPDX-License-Identifier: GPL-2.0-only
diff --git a/scripts/kconfig/tests/err_repeated_inc/__init__.py b/scripts/kconfig/tests/err_repeated_inc/__init__.py
new file mode 100644
index 000000000..129d740a8
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/err_repeated_inc/expected_stderr b/scripts/kconfig/tests/err_repeated_inc/expected_stderr
new file mode 100644
index 000000000..53071430e
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/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
diff --git a/scripts/kconfig/tests/err_transitional/Kconfig b/scripts/kconfig/tests/err_transitional/Kconfig
new file mode 100644
index 000000000..a75ed3b2f
--- /dev/null
+++ b/scripts/kconfig/tests/err_transitional/Kconfig
@@ -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
diff --git a/scripts/kconfig/tests/err_transitional/__init__.py b/scripts/kconfig/tests/err_transitional/__init__.py
new file mode 100644
index 000000000..7dffb5b08
--- /dev/null
+++ b/scripts/kconfig/tests/err_transitional/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/err_transitional/expected_stderr b/scripts/kconfig/tests/err_transitional/expected_stderr
new file mode 100644
index 000000000..b52db4f68
--- /dev/null
+++ b/scripts/kconfig/tests/err_transitional/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
diff --git a/scripts/kconfig/tests/new_choice_with_dep/Kconfig b/scripts/kconfig/tests/new_choice_with_dep/Kconfig
new file mode 100644
index 000000000..127731c04
--- /dev/null
+++ b/scripts/kconfig/tests/new_choice_with_dep/Kconfig
@@ -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
diff --git a/scripts/kconfig/tests/new_choice_with_dep/__init__.py b/scripts/kconfig/tests/new_choice_with_dep/__init__.py
new file mode 100644
index 000000000..fe9d32253
--- /dev/null
+++ b/scripts/kconfig/tests/new_choice_with_dep/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/new_choice_with_dep/config b/scripts/kconfig/tests/new_choice_with_dep/config
new file mode 100644
index 000000000..47ef95d56
--- /dev/null
+++ b/scripts/kconfig/tests/new_choice_with_dep/config
@@ -0,0 +1,3 @@
+CONFIG_CHOICE_B=y
+# CONFIG_CHOICE_D is not set
+CONFIG_CHOICE_E=y
diff --git a/scripts/kconfig/tests/new_choice_with_dep/expected_stdout b/scripts/kconfig/tests/new_choice_with_dep/expected_stdout
new file mode 100644
index 000000000..74dc0bcb2
--- /dev/null
+++ b/scripts/kconfig/tests/new_choice_with_dep/expected_stdout
@@ -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?]:
diff --git a/scripts/kconfig/tests/no_write_if_dep_unmet/Kconfig b/scripts/kconfig/tests/no_write_if_dep_unmet/Kconfig
new file mode 100644
index 000000000..4767aab6d
--- /dev/null
+++ b/scripts/kconfig/tests/no_write_if_dep_unmet/Kconfig
@@ -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
diff --git a/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py b/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py
new file mode 100644
index 000000000..791ed659c
--- /dev/null
+++ b/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py
@@ -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 "# CONFIG_... 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')
diff --git a/scripts/kconfig/tests/no_write_if_dep_unmet/config b/scripts/kconfig/tests/no_write_if_dep_unmet/config
new file mode 100644
index 000000000..abd280e2f
--- /dev/null
+++ b/scripts/kconfig/tests/no_write_if_dep_unmet/config
@@ -0,0 +1 @@
+CONFIG_A=y
diff --git a/scripts/kconfig/tests/no_write_if_dep_unmet/expected_config b/scripts/kconfig/tests/no_write_if_dep_unmet/expected_config
new file mode 100644
index 000000000..473228810
--- /dev/null
+++ b/scripts/kconfig/tests/no_write_if_dep_unmet/expected_config
@@ -0,0 +1,5 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Main menu
+#
+# CONFIG_A is not set
diff --git a/scripts/kconfig/tests/preprocess/builtin_func/Kconfig b/scripts/kconfig/tests/preprocess/builtin_func/Kconfig
new file mode 100644
index 000000000..baa328827
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/builtin_func/Kconfig
@@ -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))
diff --git a/scripts/kconfig/tests/preprocess/builtin_func/__init__.py b/scripts/kconfig/tests/preprocess/builtin_func/__init__.py
new file mode 100644
index 000000000..2e53ba08f
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/builtin_func/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/preprocess/builtin_func/expected_stderr b/scripts/kconfig/tests/preprocess/builtin_func/expected_stderr
new file mode 100644
index 000000000..33ea9ca38
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/builtin_func/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
diff --git a/scripts/kconfig/tests/preprocess/builtin_func/expected_stdout b/scripts/kconfig/tests/preprocess/builtin_func/expected_stdout
new file mode 100644
index 000000000..82de3a7e9
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/builtin_func/expected_stdout
@@ -0,0 +1 @@
+hello world 0
diff --git a/scripts/kconfig/tests/preprocess/circular_expansion/Kconfig b/scripts/kconfig/tests/preprocess/circular_expansion/Kconfig
new file mode 100644
index 000000000..6838997c2
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/circular_expansion/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+X = $(Y)
+Y = $(X)
+$(info $(X))
diff --git a/scripts/kconfig/tests/preprocess/circular_expansion/__init__.py b/scripts/kconfig/tests/preprocess/circular_expansion/__init__.py
new file mode 100644
index 000000000..419bda3e0
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/circular_expansion/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/preprocess/circular_expansion/expected_stderr b/scripts/kconfig/tests/preprocess/circular_expansion/expected_stderr
new file mode 100644
index 000000000..cde68fa98
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/circular_expansion/expected_stderr
@@ -0,0 +1 @@
+Kconfig:5: Recursive variable 'X' references itself (eventually)
diff --git a/scripts/kconfig/tests/preprocess/escape/Kconfig b/scripts/kconfig/tests/preprocess/escape/Kconfig
new file mode 100644
index 000000000..4e3f44445
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/escape/Kconfig
@@ -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))
diff --git a/scripts/kconfig/tests/preprocess/escape/__init__.py b/scripts/kconfig/tests/preprocess/escape/__init__.py
new file mode 100644
index 000000000..7ee8e747f
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/escape/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/preprocess/escape/expected_stderr b/scripts/kconfig/tests/preprocess/escape/expected_stderr
new file mode 100644
index 000000000..1c00957dd
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/escape/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: $(
diff --git a/scripts/kconfig/tests/preprocess/variable/Kconfig b/scripts/kconfig/tests/preprocess/variable/Kconfig
new file mode 100644
index 000000000..9ce2f95cb
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/variable/Kconfig
@@ -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))
diff --git a/scripts/kconfig/tests/preprocess/variable/__init__.py b/scripts/kconfig/tests/preprocess/variable/__init__.py
new file mode 100644
index 000000000..e88b1708d
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/variable/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/preprocess/variable/expected_stderr b/scripts/kconfig/tests/preprocess/variable/expected_stderr
new file mode 100644
index 000000000..a4841c3fd
--- /dev/null
+++ b/scripts/kconfig/tests/preprocess/variable/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.
diff --git a/scripts/kconfig/tests/pytest.ini b/scripts/kconfig/tests/pytest.ini
new file mode 100644
index 000000000..85d7ce8e4
--- /dev/null
+++ b/scripts/kconfig/tests/pytest.ini
@@ -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
diff --git a/scripts/kconfig/tests/transitional/Kconfig b/scripts/kconfig/tests/transitional/Kconfig
new file mode 100644
index 000000000..faa4d396f
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/Kconfig
@@ -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"
diff --git a/scripts/kconfig/tests/transitional/__init__.py b/scripts/kconfig/tests/transitional/__init__.py
new file mode 100644
index 000000000..b50ba2397
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/__init__.py
@@ -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')
diff --git a/scripts/kconfig/tests/transitional/expected_config b/scripts/kconfig/tests/transitional/expected_config
new file mode 100644
index 000000000..e01f5f070
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/expected_config
@@ -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
diff --git a/scripts/kconfig/tests/transitional/expected_stdout b/scripts/kconfig/tests/transitional/expected_stdout
new file mode 100644
index 000000000..6f0b285d6
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/expected_stdout
@@ -0,0 +1 @@
+New option with conditional default (NEW_CONDITIONAL_DEFAULT) [N/y/?] (NEW) n
diff --git a/scripts/kconfig/tests/transitional/initial_config b/scripts/kconfig/tests/transitional/initial_config
new file mode 100644
index 000000000..68b7da672
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/initial_config
@@ -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
diff --git a/scripts/kconfig/tests/warn_changed_input/Kconfig b/scripts/kconfig/tests/warn_changed_input/Kconfig
new file mode 100644
index 000000000..69845e2f3
--- /dev/null
+++ b/scripts/kconfig/tests/warn_changed_input/Kconfig
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DEP
+ bool "DEP"
+ help
+ Test dependency symbol for Kconfig warning coverage.
+ This is used by the warn_changed_input selftest.
+ It intentionally stays unset in the input fragment.
+ The test checks how dependent user input is adjusted.
+
+config A
+ bool "A"
+ depends on DEP
+ help
+ Test bool symbol for changed-input diagnostics.
+ The input fragment requests this symbol as built-in.
+ The unmet dependency on DEP forces the final value to n.
+ The warning should report that downgrade.
+
+config NUM
+ int "NUM"
+ range 10 20
+ help
+ Test integer symbol for changed-input diagnostics.
+ The input fragment requests a value outside the allowed range.
+ Kconfig resolves it to the constrained in-range value.
+ The warning should report that adjustment.
+
+config DUP
+ bool "DUP"
+ depends on DEP
+ help
+ Test duplicate-definition handling for changed-input diagnostics.
+ The input fragment requests this symbol as built-in.
+ The duplicate definition below must not produce a duplicate warning.
+ This keeps the warning output stable for repeated menu entries.
+
+config DUP
+ bool
+ depends on DEP
diff --git a/scripts/kconfig/tests/warn_changed_input/__init__.py b/scripts/kconfig/tests/warn_changed_input/__init__.py
new file mode 100644
index 000000000..4c3bca6af
--- /dev/null
+++ b/scripts/kconfig/tests/warn_changed_input/__init__.py
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Test optional warnings for user-provided values changed by Kconfig.
+
+Warnings should stay disabled by default, and should only appear when
+KCONFIG_WARN_CHANGED_INPUT is enabled.
+"""
+
+
+def test(conf):
+ assert conf.olddefconfig('config') == 0
+ assert 'user-provided values changed by Kconfig' not in conf.stderr
+
+ assert conf._run_conf('--olddefconfig', dot_config='config',
+ extra_env={
+ 'KCONFIG_WARN_CHANGED_INPUT': '1',
+ }) == 0
+ assert conf.stderr_contains('expected_stderr')
+ assert conf.config_matches('expected_config')
+
+ assert conf._run_conf('--olddefconfig', dot_config='config',
+ extra_env={
+ 'KCONFIG_WARN_CHANGED_INPUT': '1',
+ }, silent=True) == 0
+ assert conf.stderr_contains('expected_stderr')
+
+ assert conf._run_conf('--savedefconfig=defconfig', dot_config='config',
+ out_file='defconfig',
+ extra_env={
+ 'KCONFIG_WARN_CHANGED_INPUT': '1',
+ }) == 0
+ assert conf.stderr_contains('expected_stderr')
+ assert conf.config_matches('expected_defconfig')
diff --git a/scripts/kconfig/tests/warn_changed_input/config b/scripts/kconfig/tests/warn_changed_input/config
new file mode 100644
index 000000000..dbe93ff26
--- /dev/null
+++ b/scripts/kconfig/tests/warn_changed_input/config
@@ -0,0 +1,3 @@
+CONFIG_A=y
+CONFIG_NUM=30
+CONFIG_DUP=y
diff --git a/scripts/kconfig/tests/warn_changed_input/expected_config b/scripts/kconfig/tests/warn_changed_input/expected_config
new file mode 100644
index 000000000..fe8bbec66
--- /dev/null
+++ b/scripts/kconfig/tests/warn_changed_input/expected_config
@@ -0,0 +1,6 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Main menu
+#
+# CONFIG_DEP is not set
+CONFIG_NUM=20
diff --git a/scripts/kconfig/tests/warn_changed_input/expected_defconfig b/scripts/kconfig/tests/warn_changed_input/expected_defconfig
new file mode 100644
index 000000000..af9e34851
--- /dev/null
+++ b/scripts/kconfig/tests/warn_changed_input/expected_defconfig
@@ -0,0 +1 @@
+CONFIG_NUM=20
diff --git a/scripts/kconfig/tests/warn_changed_input/expected_stderr b/scripts/kconfig/tests/warn_changed_input/expected_stderr
new file mode 100644
index 000000000..9ec8446b4
--- /dev/null
+++ b/scripts/kconfig/tests/warn_changed_input/expected_stderr
@@ -0,0 +1,4 @@
+warning: user-provided values changed by Kconfig:
+ CONFIG_A: y -> n
+ CONFIG_NUM: 30 -> 20
+ CONFIG_DUP: y -> n