|
@@ -0,0 +1,3203 @@
|
|
|
+# Copyright (c) 2011-2019, Ulf Magnusson
|
|
|
+# SPDX-License-Identifier: ISC
|
|
|
+
|
|
|
+# This is the Kconfiglib test suite. It runs selftests on Kconfigs provided by
|
|
|
+# us and tests compatibility with the C Kconfig implementation by comparing the
|
|
|
+# output of Kconfiglib with the output of the scripts/kconfig/*conf utilities
|
|
|
+# for different targets and defconfigs. It should be run from the top-level
|
|
|
+# kernel directory with
|
|
|
+#
|
|
|
+# $ python Kconfiglib/testsuite.py
|
|
|
+#
|
|
|
+# Some additional options can be turned on by passing them as arguments. They
|
|
|
+# default to off.
|
|
|
+#
|
|
|
+# - obsessive:
|
|
|
+# By default, only valid arch/defconfig pairs are tested. In obsessive mode,
|
|
|
+# every arch will be tested with every defconfig. Increases the testing time
|
|
|
+# by an order of magnitude. Occasionally finds (usually obscure) bugs, and I
|
|
|
+# make sure everything passes with it.
|
|
|
+#
|
|
|
+# - obsessive-min-config:
|
|
|
+# Like obsessive, for the minimal configuation (defconfig) tests.
|
|
|
+#
|
|
|
+# - log:
|
|
|
+# Log timestamped defconfig test failures to the file test_defconfig_fails.
|
|
|
+# Handy in obsessive mode.
|
|
|
+#
|
|
|
+# For example, this commands runs the test suite in obsessive mode with logging
|
|
|
+# enabled:
|
|
|
+#
|
|
|
+# $ python(3) Kconfiglib/testsuite.py obsessive log
|
|
|
+#
|
|
|
+# pypy works too, and runs most tests much faster than CPython.
|
|
|
+#
|
|
|
+# All tests should pass. Report regressions to ulfalizer a.t Google's email
|
|
|
+# service.
|
|
|
+
|
|
|
+import difflib
|
|
|
+import errno
|
|
|
+import os
|
|
|
+import re
|
|
|
+import shutil
|
|
|
+import subprocess
|
|
|
+import sys
|
|
|
+import tempfile
|
|
|
+import textwrap
|
|
|
+
|
|
|
+from kconfiglib import Kconfig, Symbol, Choice, COMMENT, MENU, MenuNode, \
|
|
|
+ BOOL, TRISTATE, HEX, \
|
|
|
+ TRI_TO_STR, \
|
|
|
+ escape, unescape, \
|
|
|
+ expr_str, expr_items, split_expr, \
|
|
|
+ _ordered_unique, \
|
|
|
+ OR, AND, \
|
|
|
+ KconfigError
|
|
|
+
|
|
|
+
|
|
|
+def shell(cmd):
|
|
|
+ with open(os.devnull, "w") as devnull:
|
|
|
+ subprocess.call(cmd, shell=True, stdout=devnull, stderr=devnull)
|
|
|
+
|
|
|
+
|
|
|
+all_passed = True
|
|
|
+
|
|
|
+
|
|
|
+def fail(msg=None):
|
|
|
+ global all_passed
|
|
|
+ all_passed = False
|
|
|
+ if msg is not None:
|
|
|
+ print("fail: " + msg)
|
|
|
+
|
|
|
+
|
|
|
+def verify(cond, msg):
|
|
|
+ if not cond:
|
|
|
+ fail(msg)
|
|
|
+
|
|
|
+
|
|
|
+def verify_equal(x, y):
|
|
|
+ if x != y:
|
|
|
+ fail("'{}' does not equal '{}'".format(x, y))
|
|
|
+
|
|
|
+
|
|
|
+# Prevent accidental loading of configuration files by removing
|
|
|
+# KCONFIG_ALLCONFIG from the environment
|
|
|
+os.environ.pop("KCONFIG_ALLCONFIG", None)
|
|
|
+
|
|
|
+obsessive = False
|
|
|
+obsessive_min_config = False
|
|
|
+log = False
|
|
|
+
|
|
|
+
|
|
|
+def run_tests():
|
|
|
+ global obsessive, log
|
|
|
+ for s in sys.argv[1:]:
|
|
|
+ if s == "obsessive":
|
|
|
+ obsessive = True
|
|
|
+ print("Obsessive mode enabled")
|
|
|
+ elif s == "obsessive-min-config":
|
|
|
+ obsessive_min_config = True
|
|
|
+ print("Obsessive minimal config mode enabled")
|
|
|
+ elif s == "log":
|
|
|
+ log = True
|
|
|
+ print("Log mode enabled")
|
|
|
+ else:
|
|
|
+ print("Unrecognized option '{}'".format(s))
|
|
|
+ return
|
|
|
+
|
|
|
+ run_selftests()
|
|
|
+ run_compatibility_tests()
|
|
|
+
|
|
|
+
|
|
|
+def run_selftests():
|
|
|
+ #
|
|
|
+ # Common helper functions. These all expect 'c' to hold the current
|
|
|
+ # configuration.
|
|
|
+ #
|
|
|
+
|
|
|
+ def verify_value(sym_name, val):
|
|
|
+ # Verifies that a symbol has a particular value.
|
|
|
+
|
|
|
+ if isinstance(val, int):
|
|
|
+ val = TRI_TO_STR[val]
|
|
|
+
|
|
|
+ sym = c.syms[sym_name]
|
|
|
+ verify(sym.str_value == val,
|
|
|
+ 'expected {} to have the value "{}", had the value "{}"'
|
|
|
+ .format(sym_name, val, sym.str_value))
|
|
|
+
|
|
|
+ def assign_and_verify_value(sym_name, val, new_val):
|
|
|
+ # Assigns 'val' to a symbol and verifies that its value becomes
|
|
|
+ # 'new_val'. Assumes (and tests) that 'val' is valid for the
|
|
|
+ # symbol type.
|
|
|
+
|
|
|
+ if isinstance(new_val, int):
|
|
|
+ new_val = TRI_TO_STR[new_val]
|
|
|
+
|
|
|
+ sym = c.syms[sym_name]
|
|
|
+ old_val = sym.str_value
|
|
|
+ verify(sym.set_value(val),
|
|
|
+ "assigning '{}' to {} unexpectedly failed"
|
|
|
+ .format(val, sym_name))
|
|
|
+ verify(sym.str_value == new_val,
|
|
|
+ "expected {} to have the value '{}' after being assigned the "
|
|
|
+ "value '{}'. Instead, the value is '{}'. The old value was "
|
|
|
+ "'{}'."
|
|
|
+ .format(sym_name, new_val, val, sym.str_value, old_val))
|
|
|
+
|
|
|
+ def assign_and_verify(sym_name, user_val):
|
|
|
+ # Like assign_and_verify_value(), with the expected value being the
|
|
|
+ # value just set.
|
|
|
+
|
|
|
+ assign_and_verify_value(sym_name, user_val, user_val)
|
|
|
+
|
|
|
+ def assign_and_verify_user_value(sym_name, val, user_val, valid):
|
|
|
+ # Assigns a user value to the symbol and verifies the new user value.
|
|
|
+ # If valid is True, the user value is valid for the type, otherwise
|
|
|
+ # not. This is used to test the set_value() return value.
|
|
|
+
|
|
|
+ sym = c.syms[sym_name]
|
|
|
+ sym_old_user_val = sym.user_value
|
|
|
+
|
|
|
+ verify(sym.set_value(val) == valid,
|
|
|
+ "expected the user value '{}' to be {} for {}, was not"
|
|
|
+ .format(val, "valid" if valid else "invalid", sym_name))
|
|
|
+ verify(sym.user_value == user_val,
|
|
|
+ "the assigned user value '{}' wasn't reflected in user_value "
|
|
|
+ "on the symbol {}. Instead, the new user_value was '{}'. The "
|
|
|
+ "old user value was '{}'."
|
|
|
+ .format(user_val, sym_name, sym.user_value, sym_old_user_val))
|
|
|
+
|
|
|
+ #
|
|
|
+ # Selftests
|
|
|
+ #
|
|
|
+
|
|
|
+ print("Testing string literal lexing")
|
|
|
+
|
|
|
+ # Dummy empty configuration just to get a Kconfig object
|
|
|
+ c = Kconfig("Kconfiglib/tests/empty")
|
|
|
+
|
|
|
+ def verify_string_lex(s, expected):
|
|
|
+ # Verifies that a constant symbol with the name 'res' is produced from
|
|
|
+ # lexing 's'
|
|
|
+
|
|
|
+ res = c._tokenize("if " + s)[1].name
|
|
|
+ verify(res == expected,
|
|
|
+ "expected <{}> to produced the constant symbol <{}>, "
|
|
|
+ 'produced <{}>'.format(s[1:-1], expected, res))
|
|
|
+
|
|
|
+ verify_string_lex(r""" "" """, "")
|
|
|
+ verify_string_lex(r""" '' """, "")
|
|
|
+
|
|
|
+ verify_string_lex(r""" "a" """, "a")
|
|
|
+ verify_string_lex(r""" 'a' """, "a")
|
|
|
+ verify_string_lex(r""" "ab" """, "ab")
|
|
|
+ verify_string_lex(r""" 'ab' """, "ab")
|
|
|
+ verify_string_lex(r""" "abc" """, "abc")
|
|
|
+ verify_string_lex(r""" 'abc' """, "abc")
|
|
|
+
|
|
|
+ verify_string_lex(r""" "'" """, "'")
|
|
|
+ verify_string_lex(r""" '"' """, '"')
|
|
|
+
|
|
|
+ verify_string_lex(r""" "\"" """, '"')
|
|
|
+ verify_string_lex(r""" '\'' """, "'")
|
|
|
+
|
|
|
+ verify_string_lex(r""" "\"\"" """, '""')
|
|
|
+ verify_string_lex(r""" '\'\'' """, "''")
|
|
|
+
|
|
|
+ verify_string_lex(r""" "\'" """, "'")
|
|
|
+ verify_string_lex(r""" '\"' """, '"')
|
|
|
+
|
|
|
+ verify_string_lex(r""" "\\" """, "\\")
|
|
|
+ verify_string_lex(r""" '\\' """, "\\")
|
|
|
+
|
|
|
+ verify_string_lex(r""" "\a\\'\b\c\"'d" """, 'a\\\'bc"\'d')
|
|
|
+ verify_string_lex(r""" '\a\\"\b\c\'"d' """, "a\\\"bc'\"d")
|
|
|
+
|
|
|
+ def verify_string_bad(s):
|
|
|
+ # Verifies that tokenizing 's' throws a KconfigError. Strips the first
|
|
|
+ # and last characters from 's' so we can use readable raw strings as
|
|
|
+ # input.
|
|
|
+
|
|
|
+ try:
|
|
|
+ c.eval_string(s)
|
|
|
+ except KconfigError:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ fail("expected tokenization of {} to fail, didn't".format(s[1:-1]))
|
|
|
+
|
|
|
+ verify_string_bad(r""" " """)
|
|
|
+ verify_string_bad(r""" ' """)
|
|
|
+ verify_string_bad(r""" "' """)
|
|
|
+ verify_string_bad(r""" '" """)
|
|
|
+ verify_string_bad(r""" "\" """)
|
|
|
+ verify_string_bad(r""" '\' """)
|
|
|
+ verify_string_bad(r""" "foo """)
|
|
|
+ verify_string_bad(r""" 'foo """)
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing escape() and unescape()")
|
|
|
+
|
|
|
+ def verify_escape_unescape(s, sesc):
|
|
|
+ # Verify that 's' escapes to 'sesc' and that 'sesc' unescapes to 's'
|
|
|
+ verify_equal(escape(s), sesc)
|
|
|
+ verify_equal(unescape(sesc), s)
|
|
|
+
|
|
|
+ verify_escape_unescape(r'' , r'' )
|
|
|
+ verify_escape_unescape(r'foo' , r'foo' )
|
|
|
+ verify_escape_unescape(r'"' , r'\"' )
|
|
|
+ verify_escape_unescape(r'""' , r'\"\"' )
|
|
|
+ verify_escape_unescape('\\' , r'\\' )
|
|
|
+ verify_escape_unescape(r'\\' , r'\\\\' )
|
|
|
+ verify_escape_unescape(r'\"' , r'\\\"' )
|
|
|
+ verify_escape_unescape(r'"ab\cd"ef"', r'\"ab\\cd\"ef\"')
|
|
|
+
|
|
|
+ # Backslashes before any character should be unescaped, not just before "
|
|
|
+ # and \
|
|
|
+ verify_equal(unescape(r"\afoo\b\c\\d\\\e\\\\f"), r"afoobc\d\e\\f")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing _ordered_unique()")
|
|
|
+
|
|
|
+ verify_equal(_ordered_unique([]), [])
|
|
|
+ verify_equal(_ordered_unique([1]), [1])
|
|
|
+ verify_equal(_ordered_unique([1, 2]), [1, 2])
|
|
|
+ verify_equal(_ordered_unique([1, 1]), [1])
|
|
|
+ verify_equal(_ordered_unique([1, 1, 2]), [1, 2])
|
|
|
+ verify_equal(_ordered_unique([1, 2, 1]), [1, 2])
|
|
|
+ verify_equal(_ordered_unique([1, 2, 2]), [1, 2])
|
|
|
+ verify_equal(_ordered_unique([1, 2, 3, 2, 1, 2, 3, 4, 3, 2, 1, 0]),
|
|
|
+ [1, 2, 3, 4, 0])
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing expression evaluation")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Keval", warn=False)
|
|
|
+
|
|
|
+ def verify_eval(expr, val):
|
|
|
+ res = c.eval_string(expr)
|
|
|
+ verify(res == val,
|
|
|
+ "'{}' evaluated to {}, expected {}".format(expr, res, val))
|
|
|
+
|
|
|
+ # No modules
|
|
|
+ verify_eval("n", 0)
|
|
|
+ verify_eval("m", 0)
|
|
|
+ verify_eval("y", 2)
|
|
|
+ verify_eval("'n'", 0)
|
|
|
+ verify_eval("'m'", 0)
|
|
|
+ verify_eval("'y'", 2)
|
|
|
+ verify_eval("M", 2)
|
|
|
+
|
|
|
+ # Modules
|
|
|
+ c.modules.set_value(2)
|
|
|
+ verify_eval("n", 0)
|
|
|
+ verify_eval("m", 1)
|
|
|
+ verify_eval("y", 2)
|
|
|
+ verify_eval("'n'", 0)
|
|
|
+ verify_eval("'m'", 1)
|
|
|
+ verify_eval("'y'", 2)
|
|
|
+ verify_eval("M", 1)
|
|
|
+ verify_eval("(Y || N) && (m && y)", 1)
|
|
|
+
|
|
|
+ # Non-bool/non-tristate symbols are always n in a tristate sense
|
|
|
+ verify_eval("Y_STRING", 0)
|
|
|
+ verify_eval("Y_STRING || m", 1)
|
|
|
+
|
|
|
+ # As are all constants besides y and m
|
|
|
+ verify_eval('"foo"', 0)
|
|
|
+ verify_eval('"foo" || "bar"', 0)
|
|
|
+ verify_eval('"foo" || m', 1)
|
|
|
+
|
|
|
+ # Test equality for symbols
|
|
|
+
|
|
|
+ verify_eval("N = N", 2)
|
|
|
+ verify_eval("N = n", 2)
|
|
|
+ verify_eval("N = 'n'", 2)
|
|
|
+ verify_eval("N != N", 0)
|
|
|
+ verify_eval("N != n", 0)
|
|
|
+ verify_eval("N != 'n'", 0)
|
|
|
+
|
|
|
+ verify_eval("M = M", 2)
|
|
|
+ verify_eval("M = m", 2)
|
|
|
+ verify_eval("M = 'm'", 2)
|
|
|
+ verify_eval("M != M", 0)
|
|
|
+ verify_eval("M != m", 0)
|
|
|
+ verify_eval("M != 'm'", 0)
|
|
|
+
|
|
|
+ verify_eval("Y = Y", 2)
|
|
|
+ verify_eval("Y = y", 2)
|
|
|
+ verify_eval("Y = 'y'", 2)
|
|
|
+ verify_eval("Y != Y", 0)
|
|
|
+ verify_eval("Y != y", 0)
|
|
|
+ verify_eval("Y != 'y'", 0)
|
|
|
+
|
|
|
+ verify_eval("N != M", 2)
|
|
|
+ verify_eval("N != Y", 2)
|
|
|
+ verify_eval("M != Y", 2)
|
|
|
+
|
|
|
+ verify_eval("Y_STRING = y", 2)
|
|
|
+ verify_eval("Y_STRING = 'y'", 2)
|
|
|
+ verify_eval('FOO_BAR_STRING = "foo bar"', 2)
|
|
|
+ verify_eval('FOO_BAR_STRING != "foo bar baz"', 2)
|
|
|
+ verify_eval('INT_37 = 37', 2)
|
|
|
+ verify_eval("INT_37 = '37'", 2)
|
|
|
+ verify_eval('HEX_0X37 = 0x37', 2)
|
|
|
+ verify_eval("HEX_0X37 = '0x37'", 2)
|
|
|
+
|
|
|
+ # These should also hold after 31847b67 (kconfig: allow use of relations
|
|
|
+ # other than (in)equality)
|
|
|
+ verify_eval("HEX_0X37 = '0x037'", 2)
|
|
|
+ verify_eval("HEX_0X37 = '0x0037'", 2)
|
|
|
+
|
|
|
+ # Constant symbol comparisons
|
|
|
+ verify_eval('"foo" != "bar"', 2)
|
|
|
+ verify_eval('"foo" = "bar"', 0)
|
|
|
+ verify_eval('"foo" = "foo"', 2)
|
|
|
+
|
|
|
+ # Undefined symbols get their name as their value
|
|
|
+ c.warn = False
|
|
|
+ verify_eval("'not_defined' = not_defined", 2)
|
|
|
+ verify_eval("not_defined_2 = not_defined_2", 2)
|
|
|
+ verify_eval("not_defined_1 != not_defined_2", 2)
|
|
|
+
|
|
|
+ # Test less than/greater than
|
|
|
+
|
|
|
+ # Basic evaluation
|
|
|
+ verify_eval("INT_37 < 38", 2)
|
|
|
+ verify_eval("38 < INT_37", 0)
|
|
|
+ verify_eval("INT_37 < '38'", 2)
|
|
|
+ verify_eval("'38' < INT_37", 0)
|
|
|
+ verify_eval("INT_37 < 138", 2)
|
|
|
+ verify_eval("138 < INT_37", 0)
|
|
|
+ verify_eval("INT_37 < '138'", 2)
|
|
|
+ verify_eval("'138' < INT_37", 0)
|
|
|
+ verify_eval("INT_37 < -138", 0)
|
|
|
+ verify_eval("-138 < INT_37", 2)
|
|
|
+ verify_eval("INT_37 < '-138'", 0)
|
|
|
+ verify_eval("'-138' < INT_37", 2)
|
|
|
+ verify_eval("INT_37 < 37", 0)
|
|
|
+ verify_eval("37 < INT_37", 0)
|
|
|
+ verify_eval("INT_37 < 36", 0)
|
|
|
+ verify_eval("36 < INT_37", 2)
|
|
|
+
|
|
|
+ # Different formats in comparison
|
|
|
+ verify_eval("INT_37 < 0x26", 2) # 38
|
|
|
+ verify_eval("INT_37 < 0x25", 0) # 37
|
|
|
+ verify_eval("INT_37 < 0x24", 0) # 36
|
|
|
+ verify_eval("HEX_0X37 < 56", 2) # 0x38
|
|
|
+ verify_eval("HEX_0X37 < 55", 0) # 0x37
|
|
|
+ verify_eval("HEX_0X37 < 54", 0) # 0x36
|
|
|
+
|
|
|
+ # Other int comparisons
|
|
|
+ verify_eval("INT_37 <= 38", 2)
|
|
|
+ verify_eval("INT_37 <= 37", 2)
|
|
|
+ verify_eval("INT_37 <= 36", 0)
|
|
|
+ verify_eval("INT_37 > 38", 0)
|
|
|
+ verify_eval("INT_37 > 37", 0)
|
|
|
+ verify_eval("INT_37 > 36", 2)
|
|
|
+ verify_eval("INT_37 >= 38", 0)
|
|
|
+ verify_eval("INT_37 >= 37", 2)
|
|
|
+ verify_eval("INT_37 >= 36", 2)
|
|
|
+
|
|
|
+ # Other hex comparisons
|
|
|
+ verify_eval("HEX_0X37 <= 0x38", 2)
|
|
|
+ verify_eval("HEX_0X37 <= 0x37", 2)
|
|
|
+ verify_eval("HEX_0X37 <= 0x36", 0)
|
|
|
+ verify_eval("HEX_0X37 > 0x38", 0)
|
|
|
+ verify_eval("HEX_0X37 > 0x37", 0)
|
|
|
+ verify_eval("HEX_0X37 > 0x36", 2)
|
|
|
+ verify_eval("HEX_0X37 >= 0x38", 0)
|
|
|
+ verify_eval("HEX_0X37 >= 0x37", 2)
|
|
|
+ verify_eval("HEX_0X37 >= 0x36", 2)
|
|
|
+
|
|
|
+ # A hex holding a value without a "0x" prefix should still be treated as
|
|
|
+ # hexadecimal
|
|
|
+ verify_eval("HEX_37 < 0x38", 2)
|
|
|
+ verify_eval("HEX_37 < 0x37", 0)
|
|
|
+ verify_eval("HEX_37 < 0x36", 0)
|
|
|
+
|
|
|
+ # Symbol comparisons
|
|
|
+ verify_eval("INT_37 < HEX_0X37", 2)
|
|
|
+ verify_eval("INT_37 > HEX_0X37", 0)
|
|
|
+ verify_eval("HEX_0X37 < INT_37 ", 0)
|
|
|
+ verify_eval("HEX_0X37 > INT_37 ", 2)
|
|
|
+ verify_eval("INT_37 < INT_37 ", 0)
|
|
|
+ verify_eval("INT_37 <= INT_37 ", 2)
|
|
|
+ verify_eval("INT_37 > INT_37 ", 0)
|
|
|
+ verify_eval("INT_37 <= INT_37 ", 2)
|
|
|
+
|
|
|
+ # Tristate value comparisons
|
|
|
+ verify_eval("n < n", 0)
|
|
|
+ verify_eval("n < m", 2)
|
|
|
+ verify_eval("n < y", 2)
|
|
|
+ verify_eval("n < N", 0)
|
|
|
+ verify_eval("n < M", 2)
|
|
|
+ verify_eval("n < Y", 2)
|
|
|
+ verify_eval("0 > n", 0)
|
|
|
+ verify_eval("1 > n", 2)
|
|
|
+ verify_eval("2 > n", 2)
|
|
|
+ verify_eval("m < n", 0)
|
|
|
+ verify_eval("m < m", 0)
|
|
|
+ verify_eval("m < y", 2)
|
|
|
+
|
|
|
+ # Strings compare lexicographically
|
|
|
+ verify_eval("'aa' < 'ab'", 2)
|
|
|
+ verify_eval("'aa' > 'ab'", 0)
|
|
|
+ verify_eval("'ab' < 'aa'", 0)
|
|
|
+ verify_eval("'ab' > 'aa'", 2)
|
|
|
+
|
|
|
+ # Comparisons where one of the operands doesn't parse as a number also give
|
|
|
+ # a lexicographic comparison
|
|
|
+ verify_eval("INT_37 < '37a' ", 2)
|
|
|
+ verify_eval("'37a' > INT_37", 2)
|
|
|
+ verify_eval("INT_37 <= '37a' ", 2)
|
|
|
+ verify_eval("'37a' >= INT_37", 2)
|
|
|
+ verify_eval("INT_37 >= '37a' ", 0)
|
|
|
+ verify_eval("INT_37 > '37a' ", 0)
|
|
|
+ verify_eval("'37a' < INT_37", 0)
|
|
|
+ verify_eval("'37a' <= INT_37", 0)
|
|
|
+
|
|
|
+ def verify_eval_bad(expr):
|
|
|
+ try:
|
|
|
+ c.eval_string(expr)
|
|
|
+ except KconfigError:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ fail('expected eval_string("{}") to throw KconfigError, '
|
|
|
+ "didn't".format(expr))
|
|
|
+
|
|
|
+ # Verify that some bad stuff throws KconfigError's
|
|
|
+ verify_eval_bad("")
|
|
|
+ verify_eval_bad("&")
|
|
|
+ verify_eval_bad("|")
|
|
|
+ verify_eval_bad("!")
|
|
|
+ verify_eval_bad("(")
|
|
|
+ verify_eval_bad(")")
|
|
|
+ verify_eval_bad("=")
|
|
|
+ verify_eval_bad("(X")
|
|
|
+ verify_eval_bad("X)")
|
|
|
+ verify_eval_bad("X X")
|
|
|
+ verify_eval_bad("!X X")
|
|
|
+ verify_eval_bad("X !X")
|
|
|
+ verify_eval_bad("(X) X")
|
|
|
+ verify_eval_bad("X &&")
|
|
|
+ verify_eval_bad("&& X")
|
|
|
+ verify_eval_bad("X && && X")
|
|
|
+ verify_eval_bad("X && !&&")
|
|
|
+ verify_eval_bad("X ||")
|
|
|
+ verify_eval_bad("|| X")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Symbol.__str__()/custom_str() and def_{int,hex,string}")
|
|
|
+
|
|
|
+ def verify_str(item, s):
|
|
|
+ verify_equal(str(item), s[1:-1])
|
|
|
+
|
|
|
+ def verify_custom_str(item, s):
|
|
|
+ verify_equal(item.custom_str(lambda sc: "[{}]".format(sc.name)),
|
|
|
+ s[1:-1])
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kstr", warn=False)
|
|
|
+
|
|
|
+ c.modules.set_value(2)
|
|
|
+
|
|
|
+ verify_str(c.syms["UNDEFINED"], """
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["BASIC_NO_PROMPT"], """
|
|
|
+config BASIC_NO_PROMPT
|
|
|
+ bool
|
|
|
+ help
|
|
|
+ blah blah
|
|
|
+
|
|
|
+ blah blah blah
|
|
|
+
|
|
|
+ blah
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["BASIC_PROMPT"], """
|
|
|
+config BASIC_PROMPT
|
|
|
+ bool "basic"
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["ADVANCED"], """
|
|
|
+config ADVANCED
|
|
|
+ tristate "prompt" if DEP
|
|
|
+ default DEFAULT_1
|
|
|
+ default DEFAULT_2 if DEP
|
|
|
+ select SELECTED_1
|
|
|
+ select SELECTED_2 if DEP
|
|
|
+ imply IMPLIED_1
|
|
|
+ imply IMPLIED_2 if DEP
|
|
|
+ help
|
|
|
+ first help text
|
|
|
+
|
|
|
+config ADVANCED
|
|
|
+ tristate "prompt 2"
|
|
|
+
|
|
|
+menuconfig ADVANCED
|
|
|
+ tristate "prompt 3"
|
|
|
+
|
|
|
+config ADVANCED
|
|
|
+ tristate
|
|
|
+ depends on (A || !B || (C && D) || !(E && F) || G = H || (I && !J && (K || L) && !(M || N) && O = P)) && DEP4 && DEP3
|
|
|
+ help
|
|
|
+ second help text
|
|
|
+
|
|
|
+config ADVANCED
|
|
|
+ tristate "prompt 4" if VIS
|
|
|
+ depends on DEP4 && DEP3
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_custom_str(c.syms["ADVANCED"], """
|
|
|
+config ADVANCED
|
|
|
+ tristate "prompt" if [DEP]
|
|
|
+ default [DEFAULT_1]
|
|
|
+ default [DEFAULT_2] if [DEP]
|
|
|
+ select [SELECTED_1]
|
|
|
+ select [SELECTED_2] if [DEP]
|
|
|
+ imply [IMPLIED_1]
|
|
|
+ imply [IMPLIED_2] if [DEP]
|
|
|
+ help
|
|
|
+ first help text
|
|
|
+
|
|
|
+config ADVANCED
|
|
|
+ tristate "prompt 2"
|
|
|
+
|
|
|
+menuconfig ADVANCED
|
|
|
+ tristate "prompt 3"
|
|
|
+
|
|
|
+config ADVANCED
|
|
|
+ tristate
|
|
|
+ depends on ([A] || ![B] || ([C] && [D]) || !([E] && [F]) || [G] = [H] || ([I] && ![J] && ([K] || [L]) && !([M] || [N]) && [O] = [P])) && [DEP4] && [DEP3]
|
|
|
+ help
|
|
|
+ second help text
|
|
|
+
|
|
|
+config ADVANCED
|
|
|
+ tristate "prompt 4" if [VIS]
|
|
|
+ depends on [DEP4] && [DEP3]
|
|
|
+""")
|
|
|
+
|
|
|
+
|
|
|
+ verify_str(c.syms["ONLY_DIRECT_DEPS"], """
|
|
|
+config ONLY_DIRECT_DEPS
|
|
|
+ int
|
|
|
+ depends on DEP1 && DEP2
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["STRING"], """
|
|
|
+config STRING
|
|
|
+ string
|
|
|
+ default "foo"
|
|
|
+ default "bar" if DEP
|
|
|
+ default STRING2
|
|
|
+ default STRING3 if DEP
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["INT"], """
|
|
|
+config INT
|
|
|
+ int
|
|
|
+ range 1 2
|
|
|
+ range FOO BAR
|
|
|
+ range BAZ QAZ if DEP
|
|
|
+ default 7 if DEP
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["HEX"], """
|
|
|
+config HEX
|
|
|
+ hex
|
|
|
+ range 0x100 0x200
|
|
|
+ range FOO BAR
|
|
|
+ range BAZ QAZ if DEP
|
|
|
+ default 0x123
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.modules, """
|
|
|
+config MODULES
|
|
|
+ bool "MODULES"
|
|
|
+ option modules
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["OPTIONS"], """
|
|
|
+config OPTIONS
|
|
|
+ option allnoconfig_y
|
|
|
+ option defconfig_list
|
|
|
+ option env="ENV"
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["CORRECT_PROP_LOCS_BOOL"], """
|
|
|
+config CORRECT_PROP_LOCS_BOOL
|
|
|
+ bool "prompt 1"
|
|
|
+ default DEFAULT_1
|
|
|
+ default DEFAULT_2
|
|
|
+ select SELECT_1
|
|
|
+ select SELECT_2
|
|
|
+ imply IMPLY_1
|
|
|
+ imply IMPLY_2
|
|
|
+ depends on LOC_1
|
|
|
+ help
|
|
|
+ help 1
|
|
|
+
|
|
|
+menuconfig CORRECT_PROP_LOCS_BOOL
|
|
|
+ bool "prompt 2"
|
|
|
+ default DEFAULT_3
|
|
|
+ default DEFAULT_4
|
|
|
+ select SELECT_3
|
|
|
+ select SELECT_4
|
|
|
+ imply IMPLY_3
|
|
|
+ imply IMPLY_4
|
|
|
+ depends on LOC_2
|
|
|
+ help
|
|
|
+ help 2
|
|
|
+
|
|
|
+config CORRECT_PROP_LOCS_BOOL
|
|
|
+ bool "prompt 3"
|
|
|
+ default DEFAULT_5
|
|
|
+ default DEFAULT_6
|
|
|
+ select SELECT_5
|
|
|
+ select SELECT_6
|
|
|
+ imply IMPLY_5
|
|
|
+ imply IMPLY_6
|
|
|
+ depends on LOC_3
|
|
|
+ help
|
|
|
+ help 2
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["CORRECT_PROP_LOCS_INT"], """
|
|
|
+config CORRECT_PROP_LOCS_INT
|
|
|
+ int
|
|
|
+ range 1 2
|
|
|
+ range 3 4
|
|
|
+ depends on LOC_1
|
|
|
+
|
|
|
+config CORRECT_PROP_LOCS_INT
|
|
|
+ int
|
|
|
+ range 5 6
|
|
|
+ range 7 8
|
|
|
+ depends on LOC_2
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["PROMPT_ONLY"], """
|
|
|
+config PROMPT_ONLY
|
|
|
+ prompt "prompt only"
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_custom_str(c.syms["CORRECT_PROP_LOCS_INT"], """
|
|
|
+config CORRECT_PROP_LOCS_INT
|
|
|
+ int
|
|
|
+ range [1] [2]
|
|
|
+ range [3] [4]
|
|
|
+ depends on [LOC_1]
|
|
|
+
|
|
|
+config CORRECT_PROP_LOCS_INT
|
|
|
+ int
|
|
|
+ range [5] [6]
|
|
|
+ range [7] [8]
|
|
|
+ depends on [LOC_2]
|
|
|
+""")
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Choice.__str__()/custom_str()")
|
|
|
+
|
|
|
+ verify_str(c.named_choices["CHOICE"], """
|
|
|
+choice CHOICE
|
|
|
+ tristate "foo"
|
|
|
+ default CHOICE_1
|
|
|
+ default CHOICE_2 if dep
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.named_choices["CHOICE"].nodes[0].next.item, """
|
|
|
+choice
|
|
|
+ tristate "no name"
|
|
|
+ optional
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.named_choices["CORRECT_PROP_LOCS_CHOICE"], """
|
|
|
+choice CORRECT_PROP_LOCS_CHOICE
|
|
|
+ bool
|
|
|
+ default CHOICE_3
|
|
|
+ depends on LOC_1
|
|
|
+
|
|
|
+choice CORRECT_PROP_LOCS_CHOICE
|
|
|
+ bool
|
|
|
+ default CHOICE_4
|
|
|
+ depends on LOC_2
|
|
|
+
|
|
|
+choice CORRECT_PROP_LOCS_CHOICE
|
|
|
+ bool
|
|
|
+ default CHOICE_5
|
|
|
+ depends on LOC_3
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_custom_str(c.named_choices["CORRECT_PROP_LOCS_CHOICE"], """
|
|
|
+choice CORRECT_PROP_LOCS_CHOICE
|
|
|
+ bool
|
|
|
+ default [CHOICE_3]
|
|
|
+ depends on [LOC_1]
|
|
|
+
|
|
|
+choice CORRECT_PROP_LOCS_CHOICE
|
|
|
+ bool
|
|
|
+ default [CHOICE_4]
|
|
|
+ depends on [LOC_2]
|
|
|
+
|
|
|
+choice CORRECT_PROP_LOCS_CHOICE
|
|
|
+ bool
|
|
|
+ default [CHOICE_5]
|
|
|
+ depends on [LOC_3]
|
|
|
+""")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing MenuNode.__str__()/custom_str() for menus and comments")
|
|
|
+
|
|
|
+ verify_str(c.syms["SIMPLE_MENU_HOOK"].nodes[0].next, """
|
|
|
+menu "simple menu"
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["ADVANCED_MENU_HOOK"].nodes[0].next, """
|
|
|
+menu "advanced menu"
|
|
|
+ depends on A
|
|
|
+ visible if B && (C || D)
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_custom_str(c.syms["ADVANCED_MENU_HOOK"].nodes[0].next, """
|
|
|
+menu "advanced menu"
|
|
|
+ depends on [A]
|
|
|
+ visible if [B] && ([C] || [D])
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["SIMPLE_COMMENT_HOOK"].nodes[0].next, """
|
|
|
+comment "simple comment"
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["ADVANCED_COMMENT_HOOK"].nodes[0].next, """
|
|
|
+comment "advanced comment"
|
|
|
+ depends on A && B
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_custom_str(c.syms["ADVANCED_COMMENT_HOOK"].nodes[0].next, """
|
|
|
+comment "advanced comment"
|
|
|
+ depends on [A] && [B]
|
|
|
+""")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing {MenuNode,Symbol,Choice}.orig_*")
|
|
|
+
|
|
|
+ # Just test some corner cases here re. MenuNode.orig_*. They are already
|
|
|
+ # indirectly tested above. Use MenuNode.__str__() as a proxy.
|
|
|
+
|
|
|
+ verify_str(c.syms["DEP_REM_CORNER_CASES"], """
|
|
|
+config DEP_REM_CORNER_CASES
|
|
|
+ bool
|
|
|
+ default A
|
|
|
+ depends on n
|
|
|
+
|
|
|
+config DEP_REM_CORNER_CASES
|
|
|
+ bool
|
|
|
+ default B if n
|
|
|
+
|
|
|
+config DEP_REM_CORNER_CASES
|
|
|
+ bool
|
|
|
+ default C
|
|
|
+ depends on m && MODULES
|
|
|
+
|
|
|
+config DEP_REM_CORNER_CASES
|
|
|
+ bool
|
|
|
+ default D if A
|
|
|
+
|
|
|
+config DEP_REM_CORNER_CASES
|
|
|
+ bool
|
|
|
+ default E if !E1
|
|
|
+ default F if F1 = F2
|
|
|
+ default G if G1 || H1
|
|
|
+ depends on !H
|
|
|
+
|
|
|
+config DEP_REM_CORNER_CASES
|
|
|
+ bool
|
|
|
+ default H
|
|
|
+ depends on "foo" = "bar"
|
|
|
+
|
|
|
+config DEP_REM_CORNER_CASES
|
|
|
+ bool "prompt" if FOO || BAR
|
|
|
+ depends on BAZ && QAZ
|
|
|
+""")
|
|
|
+
|
|
|
+ # Test {Symbol,Choice}.orig_*
|
|
|
+
|
|
|
+ def verify_deps(elms, dep_index, expected):
|
|
|
+ verify_equal(" ".join(expr_str(elm[dep_index]) for elm in elms),
|
|
|
+ expected)
|
|
|
+
|
|
|
+ verify_deps(c.syms["BOOL_SYM_ORIG"].orig_defaults, 1, "DEP y y")
|
|
|
+ verify_deps(c.syms["BOOL_SYM_ORIG"].orig_selects, 1, "y DEP y")
|
|
|
+ verify_deps(c.syms["BOOL_SYM_ORIG"].orig_implies, 1, "y y DEP")
|
|
|
+ verify_deps(c.syms["INT_SYM_ORIG"].orig_ranges, 2, "DEP y DEP")
|
|
|
+ verify_deps(c.named_choices["CHOICE_ORIG"].orig_defaults, 1, "y DEP DEP")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Symbol.__repr__()")
|
|
|
+
|
|
|
+ def verify_repr(item, s):
|
|
|
+ verify_equal(repr(item) + "\n", s[1:])
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Krepr", warn=False)
|
|
|
+
|
|
|
+ verify_repr(c.n, """
|
|
|
+<symbol n, tristate, value n, constant>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.m, """
|
|
|
+<symbol m, tristate, value m, constant>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.y, """
|
|
|
+<symbol y, tristate, value y, constant>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["UNDEFINED"], """
|
|
|
+<symbol UNDEFINED, unknown, value "UNDEFINED", visibility n, direct deps n, undefined>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["BASIC"], """
|
|
|
+<symbol BASIC, bool, value y, visibility n, direct deps y, Kconfiglib/tests/Krepr:9>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["VISIBLE"], """
|
|
|
+<symbol VISIBLE, bool, "visible", value n, visibility y, direct deps y, Kconfiglib/tests/Krepr:14>
|
|
|
+""")
|
|
|
+
|
|
|
+ c.syms["VISIBLE"].set_value(2)
|
|
|
+ c.syms["STRING"].set_value("foo")
|
|
|
+
|
|
|
+ verify_repr(c.syms["VISIBLE"], """
|
|
|
+<symbol VISIBLE, bool, "visible", value y, user value y, visibility y, direct deps y, Kconfiglib/tests/Krepr:14>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["STRING"], """
|
|
|
+<symbol STRING, string, "visible", value "foo", user value "foo", visibility y, direct deps y, Kconfiglib/tests/Krepr:17>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["DIR_DEP_N"], """
|
|
|
+<symbol DIR_DEP_N, unknown, value "DIR_DEP_N", visibility n, direct deps n, Kconfiglib/tests/Krepr:20>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["OPTIONS"], """
|
|
|
+<symbol OPTIONS, unknown, value "OPTIONS", visibility n, allnoconfig_y, is the defconfig_list symbol, from environment variable ENV, direct deps y, Kconfiglib/tests/Krepr:23>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["MULTI_DEF"], """
|
|
|
+<symbol MULTI_DEF, unknown, value "MULTI_DEF", visibility n, direct deps y, Kconfiglib/tests/Krepr:28, Kconfiglib/tests/Krepr:29>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["CHOICE_1"], """
|
|
|
+<symbol CHOICE_1, tristate, "choice sym", value n, visibility m, choice symbol, direct deps m, Kconfiglib/tests/Krepr:36>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.modules, """
|
|
|
+<symbol MODULES, bool, value y, visibility n, is the modules symbol, direct deps y, Kconfiglib/tests/Krepr:1>
|
|
|
+""")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Choice.__repr__()")
|
|
|
+
|
|
|
+ verify_repr(c.named_choices["CHOICE"], """
|
|
|
+<choice CHOICE, tristate, "choice", mode m, visibility y, Kconfiglib/tests/Krepr:33>
|
|
|
+""")
|
|
|
+
|
|
|
+ c.named_choices["CHOICE"].set_value(2)
|
|
|
+
|
|
|
+ verify_repr(c.named_choices["CHOICE"], """
|
|
|
+<choice CHOICE, tristate, "choice", mode y, user mode y, CHOICE_1 selected, visibility y, Kconfiglib/tests/Krepr:33>
|
|
|
+""")
|
|
|
+
|
|
|
+ c.syms["CHOICE_2"].set_value(2)
|
|
|
+
|
|
|
+ verify_repr(c.named_choices["CHOICE"], """
|
|
|
+<choice CHOICE, tristate, "choice", mode y, user mode y, CHOICE_2 selected, CHOICE_2 selected by user, visibility y, Kconfiglib/tests/Krepr:33>
|
|
|
+""")
|
|
|
+
|
|
|
+ c.named_choices["CHOICE"].set_value(1)
|
|
|
+
|
|
|
+ verify_repr(c.named_choices["CHOICE"], """
|
|
|
+<choice CHOICE, tristate, "choice", mode m, user mode m, CHOICE_2 selected by user (overridden), visibility y, Kconfiglib/tests/Krepr:33>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["CHOICE_HOOK"].nodes[0].next.item, """
|
|
|
+<choice, tristate, "optional choice", mode n, visibility n, optional, Kconfiglib/tests/Krepr:46>
|
|
|
+""")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing MenuNode.__repr__()")
|
|
|
+
|
|
|
+ verify_repr(c.syms["BASIC"].nodes[0], """
|
|
|
+<menu node for symbol BASIC, deps y, has help, has next, Kconfiglib/tests/Krepr:9>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["DIR_DEP_N"].nodes[0], """
|
|
|
+<menu node for symbol DIR_DEP_N, deps n, has next, Kconfiglib/tests/Krepr:20>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["MULTI_DEF"].nodes[0], """
|
|
|
+<menu node for symbol MULTI_DEF, deps y, has next, Kconfiglib/tests/Krepr:28>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["MULTI_DEF"].nodes[1], """
|
|
|
+<menu node for symbol MULTI_DEF, deps y, has next, Kconfiglib/tests/Krepr:29>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["MENUCONFIG"].nodes[0], """
|
|
|
+<menu node for symbol MENUCONFIG, is menuconfig, deps y, has next, Kconfiglib/tests/Krepr:31>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.named_choices["CHOICE"].nodes[0], """
|
|
|
+<menu node for choice CHOICE, prompt "choice" (visibility y), deps y, has child, has next, Kconfiglib/tests/Krepr:33>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["CHOICE_HOOK"].nodes[0].next, """
|
|
|
+<menu node for choice, prompt "optional choice" (visibility n), deps y, has next, Kconfiglib/tests/Krepr:46>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["NO_VISIBLE_IF_HOOK"].nodes[0].next, """
|
|
|
+<menu node for menu, prompt "no visible if" (visibility y), deps y, 'visible if' deps y, has next, Kconfiglib/tests/Krepr:53>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["VISIBLE_IF_HOOK"].nodes[0].next, """
|
|
|
+<menu node for menu, prompt "visible if" (visibility y), deps y, 'visible if' deps m, has next, Kconfiglib/tests/Krepr:58>
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_repr(c.syms["COMMENT_HOOK"].nodes[0].next, """
|
|
|
+<menu node for comment, prompt "comment" (visibility y), deps y, Kconfiglib/tests/Krepr:64>
|
|
|
+""")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Kconfig.__repr__()")
|
|
|
+
|
|
|
+ verify_repr(c, """
|
|
|
+<configuration with 15 symbols, main menu prompt "Main menu", srctree is current directory, config symbol prefix "CONFIG_", warnings disabled, printing of warnings to stderr enabled, undef. symbol assignment warnings disabled, overriding symbol assignment warnings enabled, redundant symbol assignment warnings enabled>
|
|
|
+""")
|
|
|
+
|
|
|
+ os.environ["srctree"] = "Kconfiglib"
|
|
|
+ os.environ["CONFIG_"] = "CONFIG_ value"
|
|
|
+
|
|
|
+ c = Kconfig("tests/Krepr", warn=False)
|
|
|
+ c.warn = True
|
|
|
+ c.warn_to_stderr = False
|
|
|
+ c.warn_assign_override = False
|
|
|
+ c.warn_assign_redun = False
|
|
|
+ c.warn_assign_undef = True
|
|
|
+
|
|
|
+ verify_repr(c, """
|
|
|
+<configuration with 15 symbols, main menu prompt "Main menu", srctree "Kconfiglib", config symbol prefix "CONFIG_ value", warnings enabled, printing of warnings to stderr disabled, undef. symbol assignment warnings enabled, overriding symbol assignment warnings disabled, redundant symbol assignment warnings disabled>
|
|
|
+""")
|
|
|
+
|
|
|
+ os.environ.pop("srctree", None)
|
|
|
+ os.environ.pop("CONFIG_", None)
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing tricky help strings")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Khelp")
|
|
|
+
|
|
|
+ def verify_help(node, s):
|
|
|
+ verify_equal(node.help, s[1:-1])
|
|
|
+
|
|
|
+ verify_help(c.syms["TWO_HELP_STRINGS"].nodes[0], """
|
|
|
+first help string
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_help(c.syms["TWO_HELP_STRINGS"].nodes[1], """
|
|
|
+second help string
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_help(c.syms["NO_BLANK_AFTER_HELP"].nodes[0], """
|
|
|
+help for
|
|
|
+NO_BLANK_AFTER_HELP
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_help(c.named_choices["CHOICE_HELP"].nodes[0], """
|
|
|
+help for
|
|
|
+CHOICE_HELP
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_help(c.syms["HELP_TERMINATED_BY_COMMENT"].nodes[0], """
|
|
|
+a
|
|
|
+b
|
|
|
+c
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_help(c.syms["TRICKY_HELP"].nodes[0], """
|
|
|
+a
|
|
|
+ b
|
|
|
+ c
|
|
|
+
|
|
|
+ d
|
|
|
+ e
|
|
|
+ f
|
|
|
+
|
|
|
+
|
|
|
+g
|
|
|
+ h
|
|
|
+ i
|
|
|
+""")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing locations, source/rsource/gsource/grsource, and "
|
|
|
+ "Kconfig.kconfig_filenames")
|
|
|
+
|
|
|
+ def verify_locations(nodes, *expected_locs):
|
|
|
+ verify(len(nodes) == len(expected_locs),
|
|
|
+ "Wrong number of locations for " + repr(nodes))
|
|
|
+
|
|
|
+ for node, expected_loc in zip(nodes, expected_locs):
|
|
|
+ node_loc = "{}:{}".format(node.filename, node.linenr)
|
|
|
+ verify(node_loc == expected_loc,
|
|
|
+ "expected {} to have the location {}, had the location {}"
|
|
|
+ .format(repr(node), expected_loc, node_loc))
|
|
|
+
|
|
|
+ # Expanded in the 'source' statement in Klocation
|
|
|
+
|
|
|
+ os.environ["TESTS_DIR_FROM_ENV"] = "tests"
|
|
|
+ os.environ["SUB_DIR_FROM_ENV"] = "sub"
|
|
|
+
|
|
|
+ os.environ["_SOURCED"] = "_sourced"
|
|
|
+ os.environ["_RSOURCED"] = "_rsourced"
|
|
|
+ os.environ["_GSOURCED"] = "_gsourced"
|
|
|
+ os.environ["_GRSOURCED"] = "_grsourced"
|
|
|
+
|
|
|
+ # Test twice, with $srctree as a relative and an absolute path,
|
|
|
+ # respectively
|
|
|
+ for srctree in "Kconfiglib", os.path.abspath("Kconfiglib"):
|
|
|
+ os.environ["srctree"] = srctree
|
|
|
+
|
|
|
+ # Has symbol with empty help text, so disable warnings
|
|
|
+ c = Kconfig("tests/Klocation", warn=False)
|
|
|
+
|
|
|
+ verify_locations(c.syms["UNDEFINED"].nodes)
|
|
|
+ verify_equal(c.syms["UNDEFINED"].name_and_loc, "UNDEFINED (undefined)")
|
|
|
+
|
|
|
+ verify_locations(c.syms["ONE_DEF"].nodes, "tests/Klocation:4")
|
|
|
+ verify_equal(c.syms["ONE_DEF"].name_and_loc,
|
|
|
+ "ONE_DEF (defined at tests/Klocation:4)")
|
|
|
+
|
|
|
+ verify_locations(c.syms["TWO_DEF"].nodes,
|
|
|
+ "tests/Klocation:7",
|
|
|
+ "tests/Klocation:10")
|
|
|
+ verify_equal(c.syms["TWO_DEF"].name_and_loc,
|
|
|
+ "TWO_DEF (defined at tests/Klocation:7, tests/Klocation:10)")
|
|
|
+
|
|
|
+ verify_locations(c.syms["MANY_DEF"].nodes,
|
|
|
+ "tests/Klocation:13",
|
|
|
+ "tests/Klocation:43",
|
|
|
+ "tests/Klocation:45",
|
|
|
+ "tests/Klocation_sourced:3",
|
|
|
+ "tests/sub/Klocation_rsourced:2",
|
|
|
+ "tests/sub/Klocation_gsourced1:1",
|
|
|
+ "tests/sub/Klocation_gsourced2:1",
|
|
|
+ "tests/sub/Klocation_gsourced1:1",
|
|
|
+ "tests/sub/Klocation_gsourced2:1",
|
|
|
+ "tests/sub/Klocation_grsourced1:1",
|
|
|
+ "tests/sub/Klocation_grsourced2:1",
|
|
|
+ "tests/sub/Klocation_grsourced1:1",
|
|
|
+ "tests/sub/Klocation_grsourced2:1",
|
|
|
+ "tests/Klocation:78")
|
|
|
+
|
|
|
+ verify_locations(c.named_choices["CHOICE_ONE_DEF"].nodes,
|
|
|
+ "tests/Klocation_sourced:5")
|
|
|
+ verify_equal(c.named_choices["CHOICE_ONE_DEF"].name_and_loc,
|
|
|
+ "<choice CHOICE_ONE_DEF> (defined at tests/Klocation_sourced:5)")
|
|
|
+
|
|
|
+ verify_locations(c.named_choices["CHOICE_TWO_DEF"].nodes,
|
|
|
+ "tests/Klocation_sourced:9",
|
|
|
+ "tests/Klocation_sourced:13")
|
|
|
+ verify_equal(c.named_choices["CHOICE_TWO_DEF"].name_and_loc,
|
|
|
+ "<choice CHOICE_TWO_DEF> (defined at tests/Klocation_sourced:9, tests/Klocation_sourced:13)")
|
|
|
+
|
|
|
+ verify_locations([c.syms["MENU_HOOK"].nodes[0].next],
|
|
|
+ "tests/Klocation_sourced:20")
|
|
|
+
|
|
|
+ verify_locations([c.syms["COMMENT_HOOK"].nodes[0].next],
|
|
|
+ "tests/Klocation_sourced:26")
|
|
|
+
|
|
|
+ # Test Kconfig.kconfig_filenames
|
|
|
+
|
|
|
+ verify_equal(c.kconfig_filenames, [
|
|
|
+ "tests/Klocation",
|
|
|
+ "tests/Klocation_sourced",
|
|
|
+ "tests/sub/Klocation_rsourced",
|
|
|
+ "tests/sub/Klocation_gsourced1",
|
|
|
+ "tests/sub/Klocation_gsourced2",
|
|
|
+ "tests/sub/Klocation_gsourced1",
|
|
|
+ "tests/sub/Klocation_gsourced2",
|
|
|
+ "tests/sub/Klocation_grsourced1",
|
|
|
+ "tests/sub/Klocation_grsourced2",
|
|
|
+ "tests/sub/Klocation_grsourced1",
|
|
|
+ "tests/sub/Klocation_grsourced2"
|
|
|
+ ])
|
|
|
+
|
|
|
+ # Test recursive 'source' detection
|
|
|
+
|
|
|
+ try:
|
|
|
+ Kconfig("tests/Krecursive1")
|
|
|
+ except KconfigError as e:
|
|
|
+ verify_equal(str(e), """
|
|
|
+tests/Krecursive2:1: recursive 'source' of 'tests/Krecursive1' detected. Check that environment variables are set correctly.
|
|
|
+Include path:
|
|
|
+tests/Krecursive1:1
|
|
|
+tests/Krecursive2:1
|
|
|
+"""[:-1])
|
|
|
+ except:
|
|
|
+ fail("recursive 'source' raised wrong exception")
|
|
|
+ else:
|
|
|
+ fail("recursive 'source' did not raise exception")
|
|
|
+
|
|
|
+ # Verify that source and rsource throw exceptions for missing files
|
|
|
+
|
|
|
+ # TODO: Make an exception test helper
|
|
|
+
|
|
|
+ try:
|
|
|
+ Kconfig("tests/Kmissingsource")
|
|
|
+ except KconfigError as e:
|
|
|
+ if "not found" not in str(e):
|
|
|
+ fail("'source' with missing file raised wrong KconfigError")
|
|
|
+ except:
|
|
|
+ fail("'source' with missing file raised wrong exception")
|
|
|
+ else:
|
|
|
+ fail("'source' with missing file did not raise exception")
|
|
|
+
|
|
|
+ try:
|
|
|
+ Kconfig("tests/Kmissingrsource")
|
|
|
+ except KconfigError as e:
|
|
|
+ if "not found" not in str(e):
|
|
|
+ fail("'rsource' with missing file raised wrong KconfigError")
|
|
|
+ except:
|
|
|
+ fail("'rsource' with missing file raised wrong exception")
|
|
|
+ else:
|
|
|
+ fail("'rsource' with missing file did not raise exception")
|
|
|
+
|
|
|
+ # Test a tricky case involving symlinks. $srctree is tests/symlink, which
|
|
|
+ # points to tests/sub/sub, meaning tests/symlink/.. != tests/. Previously,
|
|
|
+ # using 'rsource' from a file sourced with an absolute path triggered an
|
|
|
+ # unsafe relpath() with tests/symlink/.. in it, crashing.
|
|
|
+
|
|
|
+ os.environ["srctree"] = "Kconfiglib/tests/symlink"
|
|
|
+ os.environ["KCONFIG_SYMLINK_2"] = os.path.abspath(
|
|
|
+ "Kconfiglib/tests/sub/Kconfig_symlink_2")
|
|
|
+ if not os.path.isabs(
|
|
|
+ Kconfig("Kconfig_symlink_1").syms["FOUNDME"].nodes[0].filename):
|
|
|
+
|
|
|
+ fail("Symlink + rsource issues")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Kconfig.node_iter()")
|
|
|
+
|
|
|
+ # Reuse tests/Klocation. The node_iter(unique_syms=True) case already gets
|
|
|
+ # plenty of testing from write_config() as well.
|
|
|
+
|
|
|
+ os.environ["srctree"] = "Kconfiglib"
|
|
|
+ c = Kconfig("tests/Klocation", warn=False)
|
|
|
+
|
|
|
+ verify_equal(
|
|
|
+ [node.item.name for node in c.node_iter()
|
|
|
+ if isinstance(node.item, Symbol)],
|
|
|
+ ["ONE_DEF", "TWO_DEF", "TWO_DEF", "MANY_DEF", "HELP_1", "HELP_2",
|
|
|
+ "HELP_3", "MANY_DEF", "MANY_DEF", "MANY_DEF", "MENU_HOOK",
|
|
|
+ "COMMENT_HOOK"] + 10*["MANY_DEF"])
|
|
|
+
|
|
|
+ verify_equal(
|
|
|
+ [node.item.name for node in c.node_iter(True)
|
|
|
+ if isinstance(node.item, Symbol)],
|
|
|
+ ["ONE_DEF", "TWO_DEF", "MANY_DEF", "HELP_1", "HELP_2", "HELP_3",
|
|
|
+ "MENU_HOOK", "COMMENT_HOOK"])
|
|
|
+
|
|
|
+ verify_equal(
|
|
|
+ [node.prompt[0] for node in c.node_iter()
|
|
|
+ if not isinstance(node.item, Symbol)],
|
|
|
+ ["one-def choice", "two-def choice 1", "two-def choice 2",
|
|
|
+ "menu", "comment"])
|
|
|
+
|
|
|
+ verify_equal(
|
|
|
+ [node.prompt[0] for node in c.node_iter(True)
|
|
|
+ if not isinstance(node.item, Symbol)],
|
|
|
+ ["one-def choice", "two-def choice 1", "two-def choice 2",
|
|
|
+ "menu", "comment"])
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing MenuNode.include_path")
|
|
|
+
|
|
|
+ os.environ["srctree"] = "Kconfiglib/tests"
|
|
|
+
|
|
|
+ c = Kconfig("Kinclude_path")
|
|
|
+
|
|
|
+ def verify_node_path(node, *expected):
|
|
|
+ if node.include_path != expected:
|
|
|
+ fail("Wrong include path for node {!r}. Got {}, expected {}."
|
|
|
+ .format(node, node.include_path, expected))
|
|
|
+
|
|
|
+ def verify_sym_path(sym_name, node_i, *expected):
|
|
|
+ verify_node_path(c.syms[sym_name].nodes[node_i], *expected)
|
|
|
+
|
|
|
+ verify_sym_path("TOP", 0)
|
|
|
+ verify_sym_path("TOP", 1)
|
|
|
+ verify_sym_path("TOP", 2)
|
|
|
+
|
|
|
+ verify_sym_path("ONE_DOWN", 0, ("Kinclude_path", 4))
|
|
|
+ verify_sym_path("ONE_DOWN", 1, ("Kinclude_path", 4))
|
|
|
+ verify_sym_path("ONE_DOWN", 2, ("Kinclude_path", 4))
|
|
|
+ verify_sym_path("ONE_DOWN", 3, ("Kinclude_path", 9))
|
|
|
+ verify_sym_path("ONE_DOWN", 4, ("Kinclude_path", 9))
|
|
|
+ verify_sym_path("ONE_DOWN", 5, ("Kinclude_path", 9))
|
|
|
+
|
|
|
+ verify_sym_path("TWO_DOWN", 0,
|
|
|
+ ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
|
|
|
+ verify_sym_path("TWO_DOWN", 1,
|
|
|
+ ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 9))
|
|
|
+ verify_sym_path("TWO_DOWN", 2,
|
|
|
+ ("Kinclude_path", 9), ("Kinclude_path_sourced_1", 4))
|
|
|
+ verify_sym_path("TWO_DOWN", 3,
|
|
|
+ ("Kinclude_path", 9), ("Kinclude_path_sourced_1", 9))
|
|
|
+
|
|
|
+ verify_node_path(c.top_node)
|
|
|
+ verify_node_path(c.menus[0], ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
|
|
|
+ verify_node_path(c.comments[0], ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
|
|
|
+ verify_node_path(c.choices[0].nodes[0], ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
|
|
|
+
|
|
|
+ os.environ.pop("srctree", None)
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Kconfig.choices/menus/comments")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kitemlists")
|
|
|
+
|
|
|
+ def verify_prompts(items, *expected_prompts):
|
|
|
+ verify(len(items) == len(expected_prompts),
|
|
|
+ "Wrong number of prompts for {}".format(items))
|
|
|
+
|
|
|
+ for item, expected_prompt in zip(items, expected_prompts):
|
|
|
+ if not isinstance(item, MenuNode):
|
|
|
+ item = item.nodes[0]
|
|
|
+
|
|
|
+ verify(item.prompt[0] == expected_prompt,
|
|
|
+ "Wrong prompt for {}, expected '{}'"
|
|
|
+ .format(repr(item), expected_prompt))
|
|
|
+
|
|
|
+ verify_prompts(c.choices, "choice 1", "choice 2", "choice 3", "choice 2")
|
|
|
+ verify_prompts(c.menus, "menu 1", "menu 2", "menu 3", "menu 4", "menu 5")
|
|
|
+ verify_prompts(c.comments, "comment 1", "comment 2", "comment 3")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Symbol/Choice.direct_dep")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kdirdep")
|
|
|
+
|
|
|
+ verify_equal(expr_str(c.syms["NO_DEP_SYM"].direct_dep), 'y')
|
|
|
+ verify_equal(expr_str(c.syms["DEP_SYM"].direct_dep), "A || (B && C) || !D")
|
|
|
+
|
|
|
+ verify_equal(expr_str(c.named_choices["NO_DEP_CHOICE"].direct_dep), 'y')
|
|
|
+ verify_equal(expr_str(c.named_choices["DEP_CHOICE"].direct_dep),
|
|
|
+ "A || B || C")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing expr_items()")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kexpr_items")
|
|
|
+
|
|
|
+ def verify_expr_items(expr, *sym_names):
|
|
|
+ verify_equal(tuple(sorted(item.name for item in expr_items(expr))),
|
|
|
+ sym_names)
|
|
|
+
|
|
|
+ verify_expr_items(
|
|
|
+ c.syms["TEST"].defaults[0][0],
|
|
|
+ "A", "B", "C", "D", "E", "F", "G", "H"
|
|
|
+ )
|
|
|
+
|
|
|
+ verify_expr_items(
|
|
|
+ c.syms["TEST_CHOICE"].nodes[0].prompt[1],
|
|
|
+ "A", "CHOICE"
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing MenuNode/Symbol/Choice.referenced")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kreferenced", warn=False)
|
|
|
+
|
|
|
+ def verify_deps(item, *dep_names):
|
|
|
+ verify_equal(tuple(sorted(item.name for item in item.referenced)),
|
|
|
+ dep_names)
|
|
|
+
|
|
|
+ verify_deps(c.top_node, "y")
|
|
|
+
|
|
|
+ verify_deps(c.syms["NO_REFS"].nodes[0], "y")
|
|
|
+
|
|
|
+ verify_deps(c.syms["JUST_DEPENDS_ON_REFS"].nodes[0], "A", "B")
|
|
|
+
|
|
|
+ verify_deps(c.syms["LOTS_OF_REFS"].nodes[0],
|
|
|
+ *(chr(n) for n in range(ord("A"), ord("Z") + 1)))
|
|
|
+
|
|
|
+ verify_deps(c.syms["INT_REFS"].nodes[0],
|
|
|
+ "A", "B", "C", "D", "E", "F", "G", "H", "y")
|
|
|
+
|
|
|
+ verify_deps(c.syms["CHOICE_REF"].nodes[0], "CHOICE")
|
|
|
+
|
|
|
+ verify_deps(c.menus[0], "A", "B", "C", "D")
|
|
|
+
|
|
|
+ verify_deps(c.comments[0], "A", "B")
|
|
|
+
|
|
|
+ verify_deps(c.syms["MULTI_DEF_SYM"], "A", "B", "C", "y")
|
|
|
+ verify_deps(c.named_choices["MULTI_DEF_CHOICE"], "A", "B", "C")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing split_expr()")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/empty")
|
|
|
+ c.warn = False
|
|
|
+
|
|
|
+ def verify_split(to_split, op, operand_strs):
|
|
|
+ # The same hackage as in Kconfig.eval_string()
|
|
|
+ c._tokens = c._tokenize("if " + to_split)[1:]
|
|
|
+ c._tokens_i = 0
|
|
|
+
|
|
|
+ operands = split_expr(c._parse_expr(False), op)
|
|
|
+
|
|
|
+ verify(len(operands) == len(operand_strs),
|
|
|
+ "Wrong number of operands when {} was split by {}"
|
|
|
+ .format(to_split, "OR" if op == OR else "AND"))
|
|
|
+
|
|
|
+ for operand, operand_str in zip(operands, operand_strs):
|
|
|
+ verify_equal(expr_str(operand), operand_str)
|
|
|
+
|
|
|
+ verify_split("A", OR, ("A", ))
|
|
|
+ verify_split("!A", OR, ("!A", ))
|
|
|
+ verify_split("A = B", OR, ("A = B", ))
|
|
|
+ verify_split("A && B", OR, ("A && B", ))
|
|
|
+ verify_split("A || B", OR, ("A", "B" ))
|
|
|
+ verify_split("(A || B) || C", OR, ("A", "B", "C" ))
|
|
|
+ verify_split("A || (B || C)", OR, ("A", "B", "C" ))
|
|
|
+ verify_split("A || !(B || C)", OR, ("A", "!(B || C)" ))
|
|
|
+ verify_split("A || (B && (C || D))", OR, ("A", "B && (C || D)"))
|
|
|
+ verify_split("(A && (B || C)) || D", OR, ("A && (B || C)", "D"))
|
|
|
+
|
|
|
+ verify_split("A", AND, ("A", ))
|
|
|
+ verify_split("!A", AND, ("!A", ))
|
|
|
+ verify_split("A = B", AND, ("A = B", ))
|
|
|
+ verify_split("A || B", AND, ("A || B", ))
|
|
|
+ verify_split("A && B", AND, ("A", "B" ))
|
|
|
+ verify_split("(A && B) && C", AND, ("A", "B", "C" ))
|
|
|
+ verify_split("A && (B && C)", AND, ("A", "B", "C" ))
|
|
|
+ verify_split("A && !(B && C)", AND, ("A", "!(B && C)" ))
|
|
|
+ verify_split("A && (B || (C && D))", AND, ("A", "B || (C && D)"))
|
|
|
+ verify_split("(A || (B && C)) && D", AND, ("A || (B && C)", "D"))
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing visibility")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kvisibility")
|
|
|
+
|
|
|
+ def verify_visibility(item, no_module_vis, module_vis):
|
|
|
+ c.modules.set_value(0)
|
|
|
+ verify(item.visibility == no_module_vis,
|
|
|
+ "expected {} to have visibility {} without modules, had "
|
|
|
+ "visibility {}".
|
|
|
+ format(repr(item), no_module_vis, item.visibility))
|
|
|
+
|
|
|
+ c.modules.set_value(2)
|
|
|
+ verify(item.visibility == module_vis,
|
|
|
+ "expected {} to have visibility {} with modules, had "
|
|
|
+ "visibility {}".
|
|
|
+ format(repr(item), module_vis, item.visibility))
|
|
|
+
|
|
|
+ # Symbol visibility
|
|
|
+
|
|
|
+ verify_visibility(c.syms["NO_PROMPT"], 0, 0)
|
|
|
+ verify_visibility(c.syms["BOOL_N"], 0, 0)
|
|
|
+ verify_visibility(c.syms["BOOL_M"], 0, 2)
|
|
|
+ verify_visibility(c.syms["BOOL_MOD"], 2, 2)
|
|
|
+ verify_visibility(c.syms["BOOL_Y"], 2, 2)
|
|
|
+ verify_visibility(c.syms["TRISTATE_M"], 0, 1)
|
|
|
+ verify_visibility(c.syms["TRISTATE_MOD"], 2, 1)
|
|
|
+ verify_visibility(c.syms["TRISTATE_Y"], 2, 2)
|
|
|
+ verify_visibility(c.syms["BOOL_IF_N"], 0, 0)
|
|
|
+ verify_visibility(c.syms["BOOL_IF_M"], 0, 2)
|
|
|
+ verify_visibility(c.syms["BOOL_IF_Y"], 2, 2)
|
|
|
+ verify_visibility(c.syms["BOOL_MENU_N"], 0, 0)
|
|
|
+ verify_visibility(c.syms["BOOL_MENU_M"], 0, 2)
|
|
|
+ verify_visibility(c.syms["BOOL_MENU_Y"], 2, 2)
|
|
|
+ verify_visibility(c.syms["BOOL_CHOICE_N"], 0, 0)
|
|
|
+
|
|
|
+ # Non-tristate symbols in tristate choices are only visible if the choice
|
|
|
+ # is in y mode
|
|
|
+
|
|
|
+ # The choice can't be brought to y mode because of the 'if m'
|
|
|
+ verify_visibility(c.syms["BOOL_CHOICE_M"], 0, 0)
|
|
|
+ c.syms["BOOL_CHOICE_M"].choice.set_value(2)
|
|
|
+ verify_visibility(c.syms["BOOL_CHOICE_M"], 0, 0)
|
|
|
+
|
|
|
+ # The choice gets y mode only when running without modules, because it
|
|
|
+ # defaults to m mode
|
|
|
+ verify_visibility(c.syms["BOOL_CHOICE_Y"], 2, 0)
|
|
|
+ c.syms["BOOL_CHOICE_Y"].choice.set_value(2)
|
|
|
+ # When set to y mode, the choice symbol becomes visible both with and
|
|
|
+ # without modules
|
|
|
+ verify_visibility(c.syms["BOOL_CHOICE_Y"], 2, 2)
|
|
|
+
|
|
|
+ verify_visibility(c.syms["TRISTATE_IF_N"], 0, 0)
|
|
|
+ verify_visibility(c.syms["TRISTATE_IF_M"], 0, 1)
|
|
|
+ verify_visibility(c.syms["TRISTATE_IF_Y"], 2, 2)
|
|
|
+ verify_visibility(c.syms["TRISTATE_MENU_N"], 0, 0)
|
|
|
+ verify_visibility(c.syms["TRISTATE_MENU_M"], 0, 1)
|
|
|
+ verify_visibility(c.syms["TRISTATE_MENU_Y"], 2, 2)
|
|
|
+ verify_visibility(c.syms["TRISTATE_CHOICE_N"], 0, 0)
|
|
|
+ verify_visibility(c.syms["TRISTATE_CHOICE_M"], 0, 1)
|
|
|
+ verify_visibility(c.syms["TRISTATE_CHOICE_Y"], 2, 2)
|
|
|
+
|
|
|
+ verify_visibility(c.named_choices["BOOL_CHOICE_N"], 0, 0)
|
|
|
+ verify_visibility(c.named_choices["BOOL_CHOICE_M"], 0, 2)
|
|
|
+ verify_visibility(c.named_choices["BOOL_CHOICE_Y"], 2, 2)
|
|
|
+ verify_visibility(c.named_choices["TRISTATE_CHOICE_N"], 0, 0)
|
|
|
+ verify_visibility(c.named_choices["TRISTATE_CHOICE_M"], 0, 1)
|
|
|
+ verify_visibility(c.named_choices["TRISTATE_CHOICE_Y"], 2, 2)
|
|
|
+
|
|
|
+ verify_visibility(c.named_choices["TRISTATE_CHOICE_IF_M_AND_Y"], 0, 1)
|
|
|
+ verify_visibility(c.named_choices["TRISTATE_CHOICE_MENU_N_AND_Y"], 0, 0)
|
|
|
+
|
|
|
+ # Verify that 'visible if' visibility gets propagated to prompts
|
|
|
+
|
|
|
+ verify_visibility(c.syms["VISIBLE_IF_N"], 0, 0)
|
|
|
+ verify_visibility(c.syms["VISIBLE_IF_M"], 0, 1)
|
|
|
+ verify_visibility(c.syms["VISIBLE_IF_Y"], 2, 2)
|
|
|
+ verify_visibility(c.syms["VISIBLE_IF_M_2"], 0, 1)
|
|
|
+
|
|
|
+ # Verify that string/int/hex symbols with m visibility accept a user value
|
|
|
+
|
|
|
+ assign_and_verify("STRING_m", "foo bar")
|
|
|
+ assign_and_verify("INT_m", "123")
|
|
|
+ assign_and_verify("HEX_m", "0x123")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing .assignable")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kassignable")
|
|
|
+
|
|
|
+ def verify_assignable_imp(item, assignable_no_modules, assignable_modules):
|
|
|
+ # Verifies the assignable values for 'item', with and without modules.
|
|
|
+
|
|
|
+ for modules_val, assignable in (0, assignable_no_modules), \
|
|
|
+ (2, assignable_modules):
|
|
|
+
|
|
|
+ c.modules.set_value(modules_val)
|
|
|
+ module_msg = "without modules" if modules_val == 0 else \
|
|
|
+ "with modules"
|
|
|
+
|
|
|
+ verify(item.assignable == assignable,
|
|
|
+ "Incorrect assignable values for {} {}. Should be {}, "
|
|
|
+ "was {}."
|
|
|
+ .format(item.name, module_msg, assignable, item.assignable))
|
|
|
+
|
|
|
+ # Verify that the values can actually be assigned too
|
|
|
+
|
|
|
+ for val in item.assignable:
|
|
|
+ item.set_value(val)
|
|
|
+ verify(item.tri_value == val,
|
|
|
+ "Unable to set {} to {} {}, even though it was in "
|
|
|
+ ".assignable".format(item.name, val, module_msg))
|
|
|
+
|
|
|
+ def verify_assignable(sym_name, assignable_no_modules, assignable_modules):
|
|
|
+ verify_assignable_imp(c.syms[sym_name],
|
|
|
+ assignable_no_modules,
|
|
|
+ assignable_modules)
|
|
|
+
|
|
|
+ def verify_const_unassignable(sym_name):
|
|
|
+ verify_assignable_imp(c.const_syms[sym_name], (), ())
|
|
|
+
|
|
|
+ # Things that shouldn't be .assignable
|
|
|
+ verify_const_unassignable("n")
|
|
|
+ verify_const_unassignable("m")
|
|
|
+ verify_const_unassignable("y")
|
|
|
+ verify_const_unassignable("const")
|
|
|
+ verify_assignable("UNDEFINED", (), ())
|
|
|
+ verify_assignable("NO_PROMPT", (), ())
|
|
|
+ verify_assignable("STRING", (), ())
|
|
|
+ verify_assignable("INT", (), ())
|
|
|
+ verify_assignable("HEX", (), ())
|
|
|
+
|
|
|
+ # Non-selected symbols
|
|
|
+ verify_assignable("Y_VIS_BOOL", (0, 2), (0, 2))
|
|
|
+ verify_assignable("M_VIS_BOOL", ( ), (0, 2)) # Vis. promoted
|
|
|
+ verify_assignable("N_VIS_BOOL", ( ), ( ))
|
|
|
+ verify_assignable("Y_VIS_TRI", (0, 2), (0, 1, 2))
|
|
|
+ verify_assignable("M_VIS_TRI", ( ), (0, 1 ))
|
|
|
+ verify_assignable("N_VIS_TRI", ( ), ( ))
|
|
|
+
|
|
|
+ # Symbols selected to y
|
|
|
+ verify_assignable("Y_SEL_Y_VIS_BOOL", (2,), (2,))
|
|
|
+ verify_assignable("Y_SEL_M_VIS_BOOL", ( ), (2,)) # Vis. promoted
|
|
|
+ verify_assignable("Y_SEL_N_VIS_BOOL", ( ), ( ))
|
|
|
+ verify_assignable("Y_SEL_Y_VIS_TRI", (2,), (2,))
|
|
|
+ verify_assignable("Y_SEL_M_VIS_TRI", ( ), (2,))
|
|
|
+ verify_assignable("Y_SEL_N_VIS_TRI", ( ), ( ))
|
|
|
+
|
|
|
+ # Symbols selected to m
|
|
|
+ verify_assignable("M_SEL_Y_VIS_BOOL", (2,), ( 2,)) # Value promoted
|
|
|
+ verify_assignable("M_SEL_M_VIS_BOOL", ( ), ( 2,)) # Vis./value promoted
|
|
|
+ verify_assignable("M_SEL_N_VIS_BOOL", ( ), ( ))
|
|
|
+ verify_assignable("M_SEL_Y_VIS_TRI", (2,), (1, 2 ))
|
|
|
+ verify_assignable("M_SEL_M_VIS_TRI", ( ), (1, ))
|
|
|
+ verify_assignable("M_SEL_N_VIS_TRI", ( ), ( ))
|
|
|
+
|
|
|
+ # Symbols implied to y
|
|
|
+ verify_assignable("Y_IMP_Y_VIS_BOOL", (0, 2), (0, 2))
|
|
|
+ verify_assignable("Y_IMP_M_VIS_BOOL", ( ), (0, 2)) # Vis. promoted
|
|
|
+ verify_assignable("Y_IMP_N_VIS_BOOL", ( ), ( ))
|
|
|
+ verify_assignable("Y_IMP_Y_VIS_TRI", (0, 2), (0, 2)) # m removed by imply
|
|
|
+ verify_assignable("Y_IMP_M_VIS_TRI", ( ), (0, 2)) # m promoted to y by imply
|
|
|
+ verify_assignable("Y_IMP_N_VIS_TRI", ( ), ( ))
|
|
|
+
|
|
|
+ # Symbols implied to m (never affects assignable values)
|
|
|
+ verify_assignable("M_IMP_Y_VIS_BOOL", (0, 2), (0, 2))
|
|
|
+ verify_assignable("M_IMP_M_VIS_BOOL", ( ), (0, 2)) # Vis. promoted
|
|
|
+ verify_assignable("M_IMP_N_VIS_BOOL", ( ), ( ))
|
|
|
+ verify_assignable("M_IMP_Y_VIS_TRI", (0, 2), (0, 1, 2))
|
|
|
+ verify_assignable("M_IMP_M_VIS_TRI", ( ), (0, 1 ))
|
|
|
+ verify_assignable("M_IMP_N_VIS_TRI", ( ), ( ))
|
|
|
+
|
|
|
+ # Symbols in y-mode choice
|
|
|
+ verify_assignable("Y_CHOICE_BOOL", (2,), (2,))
|
|
|
+ verify_assignable("Y_CHOICE_TRISTATE", (2,), (2,))
|
|
|
+ verify_assignable("Y_CHOICE_N_VIS_TRISTATE", ( ), ( ))
|
|
|
+
|
|
|
+ # Symbols in m/y-mode choice, starting out in m mode, or y mode when
|
|
|
+ # running without modules
|
|
|
+ verify_assignable("MY_CHOICE_BOOL", (2,), ( ))
|
|
|
+ verify_assignable("MY_CHOICE_TRISTATE", (2,), (0, 1))
|
|
|
+ verify_assignable("MY_CHOICE_N_VIS_TRISTATE", ( ), ( ))
|
|
|
+
|
|
|
+ c.named_choices["MY_CHOICE"].set_value(2)
|
|
|
+
|
|
|
+ # Symbols in m/y-mode choice, now in y mode
|
|
|
+ verify_assignable("MY_CHOICE_BOOL", (2,), (2,))
|
|
|
+ verify_assignable("MY_CHOICE_TRISTATE", (2,), (2,))
|
|
|
+ verify_assignable("MY_CHOICE_N_VIS_TRISTATE", ( ), ( ))
|
|
|
+
|
|
|
+ def verify_choice_assignable(choice_name, assignable_no_modules,
|
|
|
+ assignable_modules):
|
|
|
+ verify_assignable_imp(c.named_choices[choice_name],
|
|
|
+ assignable_no_modules,
|
|
|
+ assignable_modules)
|
|
|
+
|
|
|
+ # Choices with various possible modes
|
|
|
+ verify_choice_assignable("Y_CHOICE", (2, ), ( 2,))
|
|
|
+ verify_choice_assignable("MY_CHOICE", (2, ), ( 1, 2 ))
|
|
|
+ verify_choice_assignable("NMY_CHOICE", (0, 2), (0, 1, 2 ))
|
|
|
+ verify_choice_assignable("NY_CHOICE", (0, 2), (0, 2 ))
|
|
|
+ verify_choice_assignable("NM_CHOICE", ( ), (0, 1 ))
|
|
|
+ verify_choice_assignable("M_CHOICE", ( ), ( 1, ))
|
|
|
+ verify_choice_assignable("N_CHOICE", ( ), ( ))
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing object relations")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Krelation")
|
|
|
+
|
|
|
+ verify(c.syms["A"].nodes[0].parent is c.top_node,
|
|
|
+ "A's parent should be the top node")
|
|
|
+
|
|
|
+ verify(c.syms["B"].nodes[0].parent.item is c.named_choices["CHOICE_1"],
|
|
|
+ "B's parent should be the first choice")
|
|
|
+
|
|
|
+ verify(c.syms["C"].nodes[0].parent.item is c.syms["B"],
|
|
|
+ "C's parent should be B (due to auto menus)")
|
|
|
+
|
|
|
+ verify(c.syms["E"].nodes[0].parent.item == MENU,
|
|
|
+ "E's parent should be a menu")
|
|
|
+
|
|
|
+ verify(c.syms["E"].nodes[0].parent.parent is c.top_node,
|
|
|
+ "E's grandparent should be the top node")
|
|
|
+
|
|
|
+ verify(c.syms["G"].nodes[0].parent.item is c.named_choices["CHOICE_2"],
|
|
|
+ "G's parent should be the second choice")
|
|
|
+
|
|
|
+ verify(c.syms["G"].nodes[0].parent.parent.item == MENU,
|
|
|
+ "G's grandparent should be a menu")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing hex/int ranges")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Krange", warn=False)
|
|
|
+
|
|
|
+ for sym_name in "HEX_NO_RANGE", "INT_NO_RANGE", "HEX_40", "INT_40":
|
|
|
+ sym = c.syms[sym_name]
|
|
|
+ verify(not sym.ranges,
|
|
|
+ "{} should not have ranges".format(sym_name))
|
|
|
+
|
|
|
+ for sym_name in "HEX_ALL_RANGES_DISABLED", "INT_ALL_RANGES_DISABLED", \
|
|
|
+ "HEX_RANGE_10_20_LOW_DEFAULT", \
|
|
|
+ "INT_RANGE_10_20_LOW_DEFAULT":
|
|
|
+ sym = c.syms[sym_name]
|
|
|
+ verify(sym.ranges, "{} should have ranges".format(sym_name))
|
|
|
+
|
|
|
+ # hex/int symbols without defaults should get no default value
|
|
|
+ verify_value("HEX_NO_RANGE", "")
|
|
|
+ verify_value("INT_NO_RANGE", "")
|
|
|
+ # And neither if all ranges are disabled
|
|
|
+ verify_value("HEX_ALL_RANGES_DISABLED", "")
|
|
|
+ verify_value("INT_ALL_RANGES_DISABLED", "")
|
|
|
+ # Make sure they are assignable though, and test that the form of the user
|
|
|
+ # value is reflected in the value for hex symbols
|
|
|
+ assign_and_verify("HEX_NO_RANGE", "0x123")
|
|
|
+ assign_and_verify("HEX_NO_RANGE", "123")
|
|
|
+ assign_and_verify("INT_NO_RANGE", "123")
|
|
|
+
|
|
|
+ # Defaults outside of the valid range should be clamped
|
|
|
+ verify_value("HEX_RANGE_10_20_LOW_DEFAULT", "0x10")
|
|
|
+ verify_value("HEX_RANGE_10_20_HIGH_DEFAULT", "0x20")
|
|
|
+ verify_value("INT_RANGE_10_20_LOW_DEFAULT", "10")
|
|
|
+ verify_value("INT_RANGE_10_20_HIGH_DEFAULT", "20")
|
|
|
+ # Defaults inside the valid range should be preserved. For hex symbols,
|
|
|
+ # they should additionally use the same form as in the assignment.
|
|
|
+ verify_value("HEX_RANGE_10_20_OK_DEFAULT", "0x15")
|
|
|
+ verify_value("HEX_RANGE_10_20_OK_DEFAULT_ALTERNATE", "15")
|
|
|
+ verify_value("INT_RANGE_10_20_OK_DEFAULT", "15")
|
|
|
+
|
|
|
+ # hex/int symbols with no defaults but valid ranges should default to the
|
|
|
+ # lower end of the range if it's > 0
|
|
|
+ verify_value("HEX_RANGE_10_20", "0x10")
|
|
|
+ verify_value("HEX_RANGE_0_10", "")
|
|
|
+ verify_value("INT_RANGE_10_20", "10")
|
|
|
+ verify_value("INT_RANGE_0_10", "")
|
|
|
+ verify_value("INT_RANGE_NEG_10_10", "")
|
|
|
+
|
|
|
+ # User values and dependent ranges
|
|
|
+
|
|
|
+ # Avoid warnings for assigning values outside the active range
|
|
|
+ c.warn = False
|
|
|
+
|
|
|
+ def verify_range(sym_name, low, high, default):
|
|
|
+ # Verifies that all values in the range 'low'-'high' can be assigned,
|
|
|
+ # and that assigning values outside the range reverts the value back to
|
|
|
+ # 'default' (None if it should revert back to "").
|
|
|
+
|
|
|
+ is_hex = (c.syms[sym_name].type == HEX)
|
|
|
+
|
|
|
+ for i in range(low, high + 1):
|
|
|
+ assign_and_verify_user_value(sym_name, str(i), str(i), True)
|
|
|
+ if is_hex:
|
|
|
+ # The form of the user value should be preserved for hex
|
|
|
+ # symbols
|
|
|
+ assign_and_verify_user_value(sym_name, hex(i), hex(i), True)
|
|
|
+
|
|
|
+ # Verify that assigning a user value just outside the range causes
|
|
|
+ # defaults to be used
|
|
|
+
|
|
|
+ if default is None:
|
|
|
+ default_str = ""
|
|
|
+ else:
|
|
|
+ default_str = hex(default) if is_hex else str(default)
|
|
|
+
|
|
|
+ if is_hex:
|
|
|
+ too_low_str = hex(low - 1)
|
|
|
+ too_high_str = hex(high + 1)
|
|
|
+ else:
|
|
|
+ too_low_str = str(low - 1)
|
|
|
+ too_high_str = str(high + 1)
|
|
|
+
|
|
|
+ assign_and_verify_value(sym_name, too_low_str, default_str)
|
|
|
+ assign_and_verify_value(sym_name, too_high_str, default_str)
|
|
|
+
|
|
|
+ verify_range("HEX_RANGE_10_20_LOW_DEFAULT", 0x10, 0x20, 0x10)
|
|
|
+ verify_range("HEX_RANGE_10_20_HIGH_DEFAULT", 0x10, 0x20, 0x20)
|
|
|
+ verify_range("HEX_RANGE_10_20_OK_DEFAULT", 0x10, 0x20, 0x15)
|
|
|
+
|
|
|
+ verify_range("INT_RANGE_10_20_LOW_DEFAULT", 10, 20, 10)
|
|
|
+ verify_range("INT_RANGE_10_20_HIGH_DEFAULT", 10, 20, 20)
|
|
|
+ verify_range("INT_RANGE_10_20_OK_DEFAULT", 10, 20, 15)
|
|
|
+
|
|
|
+ verify_range("HEX_RANGE_10_20", 0x10, 0x20, 0x10)
|
|
|
+
|
|
|
+ verify_range("INT_RANGE_10_20", 10, 20, 10)
|
|
|
+ verify_range("INT_RANGE_0_10", 0, 10, None)
|
|
|
+ verify_range("INT_RANGE_NEG_10_10", -10, 10, None)
|
|
|
+
|
|
|
+ # Dependent ranges
|
|
|
+
|
|
|
+ verify_value("HEX_40", "40")
|
|
|
+ verify_value("INT_40", "40")
|
|
|
+
|
|
|
+ c.syms["HEX_RANGE_10_20"].unset_value()
|
|
|
+ c.syms["INT_RANGE_10_20"].unset_value()
|
|
|
+ verify_value("HEX_RANGE_10_40_DEPENDENT", "0x10")
|
|
|
+ verify_value("INT_RANGE_10_40_DEPENDENT", "10")
|
|
|
+ c.syms["HEX_RANGE_10_20"].set_value("15")
|
|
|
+ c.syms["INT_RANGE_10_20"].set_value("15")
|
|
|
+ verify_value("HEX_RANGE_10_40_DEPENDENT", "0x15")
|
|
|
+ verify_value("INT_RANGE_10_40_DEPENDENT", "15")
|
|
|
+ c.unset_values()
|
|
|
+ verify_range("HEX_RANGE_10_40_DEPENDENT", 0x10, 0x40, 0x10)
|
|
|
+ verify_range("INT_RANGE_10_40_DEPENDENT", 10, 40, 10)
|
|
|
+
|
|
|
+ # Ranges and symbols defined in multiple locations
|
|
|
+
|
|
|
+ verify_value("INACTIVE_RANGE", "2")
|
|
|
+ verify_value("ACTIVE_RANGE", "1")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing defconfig_filename")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/empty")
|
|
|
+ verify(c.defconfig_filename is None,
|
|
|
+ "defconfig_filename should be None with no defconfig_list symbol")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kdefconfig_nonexistent")
|
|
|
+ verify(c.defconfig_filename is None,
|
|
|
+ "defconfig_filename should be None when none of the files in the "
|
|
|
+ "defconfig_list symbol exist")
|
|
|
+
|
|
|
+ # Referenced in Kdefconfig_existent(_but_n)
|
|
|
+ os.environ["FOO"] = "defconfig_2"
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kdefconfig_existent_but_n")
|
|
|
+ verify(c.defconfig_filename is None,
|
|
|
+ "defconfig_filename should be None when the condition is n for all "
|
|
|
+ "the defaults")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kdefconfig_existent")
|
|
|
+ verify(c.defconfig_filename == "Kconfiglib/tests/defconfig_2",
|
|
|
+ "defconfig_filename should return the existing file "
|
|
|
+ "Kconfiglib/tests/defconfig_2")
|
|
|
+
|
|
|
+ # Should also look relative to $srctree if the specified defconfig is a
|
|
|
+ # relative path and can't be opened
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kdefconfig_srctree")
|
|
|
+ verify(c.defconfig_filename == "Kconfiglib/tests/defconfig_2",
|
|
|
+ "defconfig_filename gave wrong file with $srctree unset")
|
|
|
+
|
|
|
+ os.environ["srctree"] = "Kconfiglib/tests"
|
|
|
+ c = Kconfig("Kdefconfig_srctree")
|
|
|
+ verify(c.defconfig_filename == "Kconfiglib/tests/sub/defconfig_in_sub",
|
|
|
+ "defconfig_filename gave wrong file with $srctree set")
|
|
|
+
|
|
|
+ os.environ.pop("srctree", None)
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing mainmenu_text")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/empty")
|
|
|
+ verify(c.mainmenu_text == "Main menu",
|
|
|
+ "An empty Kconfig should get a default main menu prompt")
|
|
|
+
|
|
|
+ # Expanded in the mainmenu text
|
|
|
+ os.environ["FOO"] = "bar baz"
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kmainmenu")
|
|
|
+ verify(c.mainmenu_text == "---bar baz---",
|
|
|
+ "Wrong mainmenu text")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing user_value")
|
|
|
+
|
|
|
+ # References undefined env. var. Disable warnings.
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kmisc", warn=False)
|
|
|
+
|
|
|
+ # Avoid warnings from assigning invalid user values and assigning user
|
|
|
+ # values to symbols without prompts
|
|
|
+ c.warn = False
|
|
|
+
|
|
|
+ syms = [c.syms[name] for name in
|
|
|
+ ("BOOL", "TRISTATE", "STRING", "INT", "HEX")]
|
|
|
+
|
|
|
+ for sym in syms:
|
|
|
+ verify(sym.user_value is None,
|
|
|
+ "{} should not have a user value to begin with")
|
|
|
+
|
|
|
+ # Assign valid values for the types
|
|
|
+
|
|
|
+ assign_and_verify_user_value("BOOL", 0, 0, True)
|
|
|
+ assign_and_verify_user_value("BOOL", 2, 2, True)
|
|
|
+ assign_and_verify_user_value("TRISTATE", 0, 0, True)
|
|
|
+ assign_and_verify_user_value("TRISTATE", 1, 1, True)
|
|
|
+ assign_and_verify_user_value("TRISTATE", 2, 2, True)
|
|
|
+ assign_and_verify_user_value("STRING", "foo bar", "foo bar", True)
|
|
|
+ assign_and_verify_user_value("INT", "123", "123", True)
|
|
|
+ assign_and_verify_user_value("HEX", "0x123", "0x123", True)
|
|
|
+
|
|
|
+ # Assign invalid values for the types. They should retain their old user
|
|
|
+ # value.
|
|
|
+
|
|
|
+ assign_and_verify_user_value("BOOL", 1, 2, False)
|
|
|
+ assign_and_verify_user_value("BOOL", "foo", 2, False)
|
|
|
+ assign_and_verify_user_value("BOOL", "1", 2, False)
|
|
|
+ assign_and_verify_user_value("TRISTATE", "foo", 2, False)
|
|
|
+ assign_and_verify_user_value("TRISTATE", "1", 2, False)
|
|
|
+ assign_and_verify_user_value("STRING", 0, "foo bar", False)
|
|
|
+ assign_and_verify_user_value("INT", "foo", "123", False)
|
|
|
+ assign_and_verify_user_value("INT", 0, "123", False)
|
|
|
+ assign_and_verify_user_value("HEX", "foo", "0x123", False)
|
|
|
+ assign_and_verify_user_value("HEX", 0, "0x123", False)
|
|
|
+ assign_and_verify_user_value("HEX", "-0x1", "0x123", False)
|
|
|
+
|
|
|
+ for s in syms:
|
|
|
+ s.unset_value()
|
|
|
+ verify(s.user_value is None,
|
|
|
+ "{} should not have a user value after being reset".
|
|
|
+ format(s.name))
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing is_menuconfig")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kmenuconfig")
|
|
|
+
|
|
|
+ for not_menuconfig in c.syms["NOT_MENUCONFIG_1"].nodes[0], \
|
|
|
+ c.syms["NOT_MENUCONFIG_2"].nodes[0], \
|
|
|
+ c.syms["MENUCONFIG_MULTI_DEF"].nodes[0], \
|
|
|
+ c.syms["COMMENT_HOOK"].nodes[0].next:
|
|
|
+
|
|
|
+ verify(not not_menuconfig.is_menuconfig,
|
|
|
+ "'{}' should have is_menuconfig False".format(not_menuconfig))
|
|
|
+
|
|
|
+ for menuconfig in c.top_node, \
|
|
|
+ c.syms["MENUCONFIG_1"].nodes[0], \
|
|
|
+ c.syms["MENUCONFIG_MULTI_DEF"].nodes[1], \
|
|
|
+ c.syms["MENU_HOOK"].nodes[0].next, \
|
|
|
+ c.syms["CHOICE_HOOK"].nodes[0].next:
|
|
|
+
|
|
|
+ verify(menuconfig.is_menuconfig,
|
|
|
+ "'{}' should have is_menuconfig True".format(menuconfig))
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing 'option env' semantics")
|
|
|
+
|
|
|
+ os.environ["ENV_VAR"] = "ENV_VAR value"
|
|
|
+
|
|
|
+ # References undefined env. var., so disable warnings
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kmisc", warn=False)
|
|
|
+
|
|
|
+ # Verify that 'option env' is treated like a default
|
|
|
+ verify_value("FROM_ENV", "ENV_VAR value")
|
|
|
+ verify_value("FROM_ENV_MISSING", "missing")
|
|
|
+
|
|
|
+ verify_value("FROM_ENV_WEIRD", "weird")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing defined vs undefined symbols")
|
|
|
+
|
|
|
+ for name in "A", "B", "C", "D", "BOOL", "TRISTATE", "STRING", "INT", "HEX":
|
|
|
+ verify(c.syms[name].nodes,
|
|
|
+ "{} should be defined".format(name))
|
|
|
+
|
|
|
+ for name in "NOT_DEFINED_1", "NOT_DEFINED_2", "NOT_DEFINED_3", \
|
|
|
+ "NOT_DEFINED_4":
|
|
|
+ sym = c.syms[name]
|
|
|
+ verify(not c.syms[name].nodes,
|
|
|
+ "{} should not be defined".format(name))
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Symbol.choice")
|
|
|
+
|
|
|
+ for name in "A", "B", "C", "D":
|
|
|
+ verify(c.syms[name].choice is not None,
|
|
|
+ "{} should be a choice symbol".format(name))
|
|
|
+
|
|
|
+ for name in "Q1", "Q2", "Q3", "BOOL", "TRISTATE", "STRING", "INT", "HEX", \
|
|
|
+ "FROM_ENV", "FROM_ENV_MISSING", "NOT_DEFINED_1", \
|
|
|
+ "NOT_DEFINED_2", "NOT_DEFINED_3", "NOT_DEFINED_4":
|
|
|
+ verify(c.syms[name].choice is None,
|
|
|
+ "{} should not be a choice symbol".format(name))
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing is_allnoconfig_y")
|
|
|
+
|
|
|
+ verify(not c.syms["NOT_ALLNOCONFIG_Y"].is_allnoconfig_y,
|
|
|
+ "NOT_ALLNOCONFIG_Y should not be allnoconfig_y")
|
|
|
+ verify(c.syms["ALLNOCONFIG_Y"].is_allnoconfig_y,
|
|
|
+ "ALLNOCONFIG_Y should be allnoconfig_y")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing .config reading and writing")
|
|
|
+
|
|
|
+ config_test_file = "Kconfiglib/tests/config_test"
|
|
|
+
|
|
|
+ def verify_file_contents(fname, contents):
|
|
|
+ with open(fname, "r") as f:
|
|
|
+ file_contents = f.read()
|
|
|
+ verify(file_contents == contents,
|
|
|
+ "{} contains '{}'. Expected '{}'."
|
|
|
+ .format(fname, file_contents, contents))
|
|
|
+
|
|
|
+ # Writing/reading strings with characters that need to be escaped
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kescape")
|
|
|
+
|
|
|
+ # Test the default value
|
|
|
+ c.write_config(config_test_file + "_from_def")
|
|
|
+ verify_file_contents(config_test_file + "_from_def",
|
|
|
+ r'''CONFIG_STRING="\"\\"''' "\n")
|
|
|
+ # Write our own value
|
|
|
+ c.syms["STRING"].set_value(r'''\"a'\\''')
|
|
|
+ c.write_config(config_test_file + "_from_user")
|
|
|
+ verify_file_contents(config_test_file + "_from_user",
|
|
|
+ r'''CONFIG_STRING="\\\"a'\\\\"''' "\n")
|
|
|
+
|
|
|
+ # Read back the two configs and verify the respective values
|
|
|
+ c.load_config(config_test_file + "_from_def")
|
|
|
+ verify_value("STRING", '"\\')
|
|
|
+ c.load_config(config_test_file + "_from_user")
|
|
|
+ verify_value("STRING", r'''\"a'\\''')
|
|
|
+
|
|
|
+ # Appending values from a .config
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kappend")
|
|
|
+
|
|
|
+ # Values before assigning
|
|
|
+ verify_value("BOOL", "n")
|
|
|
+ verify_value("STRING", "")
|
|
|
+
|
|
|
+ # Assign BOOL
|
|
|
+ c.load_config("Kconfiglib/tests/config_set_bool", replace=False)
|
|
|
+ verify_value("BOOL", "y")
|
|
|
+ verify_value("STRING", "")
|
|
|
+
|
|
|
+ # Assign STRING
|
|
|
+ c.load_config("Kconfiglib/tests/config_set_string", replace=False)
|
|
|
+ verify_value("BOOL", "y")
|
|
|
+ verify_value("STRING", "foo bar")
|
|
|
+
|
|
|
+ # Reset BOOL
|
|
|
+ c.load_config("Kconfiglib/tests/config_set_string")
|
|
|
+ verify_value("BOOL", "n")
|
|
|
+ verify_value("STRING", "foo bar")
|
|
|
+
|
|
|
+ # Loading a completely empty .config should reset values
|
|
|
+ c.load_config("Kconfiglib/tests/empty")
|
|
|
+ verify_value("STRING", "")
|
|
|
+
|
|
|
+ # An indented assignment in a .config should be ignored
|
|
|
+ c.load_config("Kconfiglib/tests/config_indented")
|
|
|
+ verify_value("IGNOREME", "y")
|
|
|
+
|
|
|
+ # Symbol order in headers and minimal configuration files should match
|
|
|
+ # definition order, like in .config files
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Korder")
|
|
|
+
|
|
|
+ c.write_autoconf(config_test_file)
|
|
|
+ verify_file_contents(config_test_file, """
|
|
|
+#define CONFIG_O 0
|
|
|
+#define CONFIG_R 1
|
|
|
+#define CONFIG_D 2
|
|
|
+#define CONFIG_E 3
|
|
|
+#define CONFIG_R2 4
|
|
|
+#define CONFIG_I 5
|
|
|
+#define CONFIG_N 6
|
|
|
+#define CONFIG_G 7
|
|
|
+"""[1:])
|
|
|
+
|
|
|
+ # Differs from defaults
|
|
|
+ c.syms["O"].set_value("-1")
|
|
|
+ c.syms["R"].set_value("-1")
|
|
|
+ c.syms["E"].set_value("-1")
|
|
|
+ c.syms["R2"].set_value("-1")
|
|
|
+ c.syms["N"].set_value("-1")
|
|
|
+ c.syms["G"].set_value("-1")
|
|
|
+ c.write_min_config(config_test_file)
|
|
|
+ verify_file_contents(config_test_file, """
|
|
|
+CONFIG_O=-1
|
|
|
+CONFIG_R=-1
|
|
|
+CONFIG_E=-1
|
|
|
+CONFIG_R2=-1
|
|
|
+CONFIG_N=-1
|
|
|
+CONFIG_G=-1
|
|
|
+"""[1:])
|
|
|
+
|
|
|
+ # Test header strings in configuration files and headers
|
|
|
+
|
|
|
+ os.environ["KCONFIG_CONFIG_HEADER"] = "config header from env.\n"
|
|
|
+ os.environ["KCONFIG_AUTOHEADER_HEADER"] = "header header from env.\n"
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kheader")
|
|
|
+ c.write_config(config_test_file, header="config header from param\n")
|
|
|
+ verify_file_contents(config_test_file, """\
|
|
|
+config header from param
|
|
|
+CONFIG_FOO=y
|
|
|
+""")
|
|
|
+ c.write_min_config(config_test_file, header="min. config header from param\n")
|
|
|
+ verify_file_contents(config_test_file, """\
|
|
|
+min. config header from param
|
|
|
+""")
|
|
|
+ c.write_config(config_test_file)
|
|
|
+ verify_file_contents(config_test_file, """\
|
|
|
+config header from env.
|
|
|
+CONFIG_FOO=y
|
|
|
+""")
|
|
|
+ c.write_min_config(config_test_file)
|
|
|
+ verify_file_contents(config_test_file, """\
|
|
|
+config header from env.
|
|
|
+""")
|
|
|
+ c.write_autoconf(config_test_file, header="header header from param\n")
|
|
|
+ verify_file_contents(config_test_file, """\
|
|
|
+header header from param
|
|
|
+#define CONFIG_FOO 1
|
|
|
+""")
|
|
|
+ c.write_autoconf(config_test_file)
|
|
|
+ verify_file_contents(config_test_file, """\
|
|
|
+header header from env.
|
|
|
+#define CONFIG_FOO 1
|
|
|
+""")
|
|
|
+
|
|
|
+ del os.environ["KCONFIG_CONFIG_HEADER"]
|
|
|
+ del os.environ["KCONFIG_AUTOHEADER_HEADER"]
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing Kconfig fetching and separation")
|
|
|
+
|
|
|
+ for c in Kconfig("Kconfiglib/tests/Kmisc", warn=False), \
|
|
|
+ Kconfig("Kconfiglib/tests/Kmisc", warn=False):
|
|
|
+ for item in c.syms["BOOL"], \
|
|
|
+ c.syms["BOOL"].nodes[0], \
|
|
|
+ c.named_choices["OPTIONAL"], \
|
|
|
+ c.named_choices["OPTIONAL"].nodes[0], \
|
|
|
+ c.syms["MENU_HOOK"].nodes[0].next, \
|
|
|
+ c.syms["COMMENT_HOOK"].nodes[0].next:
|
|
|
+ verify(item.kconfig is c,
|
|
|
+ ".kconfig not properly set for " + repr(item))
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing imply semantics")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kimply")
|
|
|
+
|
|
|
+ verify_value("IMPLY_DIRECT_DEPS", "y")
|
|
|
+ verify_value("UNMET_DIRECT_1", "n")
|
|
|
+ verify_value("UNMET_DIRECT_2", "n")
|
|
|
+ verify_value("UNMET_DIRECT_3", "n")
|
|
|
+ verify_value("MET_DIRECT_1", "y")
|
|
|
+ verify_value("MET_DIRECT_2", "y")
|
|
|
+ verify_value("MET_DIRECT_3", "y")
|
|
|
+ verify_value("MET_DIRECT_4", "y")
|
|
|
+
|
|
|
+ verify_value("IMPLY_COND", "y")
|
|
|
+ verify_value("IMPLIED_N_COND", "n")
|
|
|
+ verify_value("IMPLIED_M_COND", "m")
|
|
|
+ verify_value("IMPLIED_Y_COND", "y")
|
|
|
+
|
|
|
+ verify_value("IMPLY_N_1", "n")
|
|
|
+ verify_value("IMPLY_N_2", "n")
|
|
|
+ verify_value("IMPLIED_FROM_N_1", "n")
|
|
|
+ verify_value("IMPLIED_FROM_N_2", "n")
|
|
|
+
|
|
|
+ verify_value("IMPLY_M", "m")
|
|
|
+ verify_value("IMPLIED_M", "m")
|
|
|
+ verify_value("IMPLIED_M_BOOL", "y")
|
|
|
+
|
|
|
+ verify_value("IMPLY_M_TO_Y", "y")
|
|
|
+ verify_value("IMPLIED_M_TO_Y", "y")
|
|
|
+
|
|
|
+ # Test user value semantics
|
|
|
+
|
|
|
+ # Verify that IMPLIED_TRISTATE is invalidated if the direct
|
|
|
+ # dependencies change
|
|
|
+
|
|
|
+ assign_and_verify("IMPLY", 2)
|
|
|
+ assign_and_verify("DIRECT_DEP", 2)
|
|
|
+ verify_value("IMPLIED_TRISTATE", 2)
|
|
|
+ assign_and_verify("DIRECT_DEP", 0)
|
|
|
+ verify_value("IMPLIED_TRISTATE", 0)
|
|
|
+ # Set back for later tests
|
|
|
+ assign_and_verify("DIRECT_DEP", 2)
|
|
|
+
|
|
|
+ # Verify that IMPLIED_TRISTATE can be set to anything when IMPLY has value
|
|
|
+ # n, and that it gets the value n by default (for non-imply-related
|
|
|
+ # reasons)
|
|
|
+
|
|
|
+ assign_and_verify("IMPLY", 0)
|
|
|
+ assign_and_verify("IMPLIED_TRISTATE", 0)
|
|
|
+ assign_and_verify("IMPLIED_TRISTATE", 1)
|
|
|
+ assign_and_verify("IMPLIED_TRISTATE", 2)
|
|
|
+ c.syms["IMPLIED_TRISTATE"].unset_value()
|
|
|
+ verify_value("IMPLIED_TRISTATE", "n")
|
|
|
+
|
|
|
+ # Same as above for m. Anything still goes, but m by default now.
|
|
|
+
|
|
|
+ assign_and_verify("IMPLY", 1)
|
|
|
+ assign_and_verify("IMPLIED_TRISTATE", 0)
|
|
|
+ assign_and_verify("IMPLIED_TRISTATE", 1)
|
|
|
+ assign_and_verify("IMPLIED_TRISTATE", 2)
|
|
|
+ c.syms["IMPLIED_TRISTATE"].unset_value()
|
|
|
+ verify_value("IMPLIED_TRISTATE", 1)
|
|
|
+
|
|
|
+ # Same as above for y. Only n and y should be accepted. m gets promoted to
|
|
|
+ # y. Default should be y.
|
|
|
+
|
|
|
+ assign_and_verify("IMPLY", 2)
|
|
|
+ assign_and_verify("IMPLIED_TRISTATE", 0)
|
|
|
+ assign_and_verify_value("IMPLIED_TRISTATE", 1, 2)
|
|
|
+ assign_and_verify("IMPLIED_TRISTATE", 2)
|
|
|
+ c.syms["IMPLIED_TRISTATE"].unset_value()
|
|
|
+ verify_value("IMPLIED_TRISTATE", 2)
|
|
|
+
|
|
|
+ # Being implied to either m or y should give a bool the value y
|
|
|
+
|
|
|
+ c.syms["IMPLY"].unset_value()
|
|
|
+ verify_value("IMPLIED_BOOL", 0)
|
|
|
+ assign_and_verify("IMPLY", 0)
|
|
|
+ verify_value("IMPLIED_BOOL", 0)
|
|
|
+ assign_and_verify("IMPLY", 1)
|
|
|
+ verify_value("IMPLIED_BOOL", 2)
|
|
|
+ assign_and_verify("IMPLY", 2)
|
|
|
+ verify_value("IMPLIED_BOOL", 2)
|
|
|
+
|
|
|
+ # A bool implied to m or y can take the values n and y
|
|
|
+
|
|
|
+ c.syms["IMPLY"].set_value(1)
|
|
|
+ assign_and_verify("IMPLIED_BOOL", 0)
|
|
|
+ assign_and_verify("IMPLIED_BOOL", 2)
|
|
|
+
|
|
|
+ c.syms["IMPLY"].set_value(2)
|
|
|
+ assign_and_verify("IMPLIED_BOOL", 0)
|
|
|
+ assign_and_verify("IMPLIED_BOOL", 2)
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing choice semantics")
|
|
|
+
|
|
|
+ # Would warn for choice value symbols defined without a type, even
|
|
|
+ # though the type is automatically derived. This is probably more
|
|
|
+ # helpful than ignoring those cases, as this feature isn't used
|
|
|
+ # deliberately anywhere from what I've seen.
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kchoice", warn=False)
|
|
|
+
|
|
|
+ for name in "BOOL", "BOOL_OPT", "BOOL_M", "DEFAULTS":
|
|
|
+ verify(c.named_choices[name].orig_type == BOOL,
|
|
|
+ "choice {} should have type bool".format(name))
|
|
|
+
|
|
|
+ for name in "TRISTATE", "TRISTATE_OPT", "TRISTATE_M":
|
|
|
+ verify(c.named_choices[name].orig_type == TRISTATE,
|
|
|
+ "choice {} should have type tristate".format(name))
|
|
|
+
|
|
|
+ def select_and_verify(sym):
|
|
|
+ choice = sym.nodes[0].parent.item
|
|
|
+ choice.set_value(2)
|
|
|
+
|
|
|
+ sym.set_value(2)
|
|
|
+
|
|
|
+ verify(sym.choice.selection is sym,
|
|
|
+ sym.name + " should be the selected symbol")
|
|
|
+
|
|
|
+ verify(choice.user_selection is sym,
|
|
|
+ sym.name + " should be the user selection of the choice")
|
|
|
+
|
|
|
+ verify(sym.tri_value == 2,
|
|
|
+ sym.name + " should have value y when selected")
|
|
|
+
|
|
|
+ verify(sym.user_value == 2,
|
|
|
+ sym.name + " should have user value y when selected")
|
|
|
+
|
|
|
+ for sibling in choice.syms:
|
|
|
+ if sibling is not sym:
|
|
|
+ verify(sibling.tri_value == 0,
|
|
|
+ sibling.name + " should be n when not selected")
|
|
|
+
|
|
|
+ def select_and_verify_all(choice_name):
|
|
|
+ choice = c.named_choices[choice_name]
|
|
|
+
|
|
|
+ # Select in forward order
|
|
|
+ for sym in choice.syms:
|
|
|
+ select_and_verify(sym)
|
|
|
+
|
|
|
+ # Select in reverse order
|
|
|
+ for sym in reversed(choice.syms):
|
|
|
+ select_and_verify(sym)
|
|
|
+
|
|
|
+ def verify_mode(choice_name, no_modules_mode, modules_mode):
|
|
|
+ choice = c.named_choices[choice_name]
|
|
|
+
|
|
|
+ c.modules.set_value(0)
|
|
|
+ verify(choice.tri_value == no_modules_mode,
|
|
|
+ 'Wrong mode for choice {} with no modules. Expected {}, got {}.'
|
|
|
+ .format(choice.name, no_modules_mode, choice.tri_value))
|
|
|
+
|
|
|
+ c.modules.set_value(2)
|
|
|
+ verify(choice.tri_value == modules_mode,
|
|
|
+ 'Wrong mode for choice {} with modules. Expected {}, got {}.'
|
|
|
+ .format(choice.name, modules_mode, choice.tri_value))
|
|
|
+
|
|
|
+ verify_mode("BOOL", 2, 2)
|
|
|
+ verify_mode("BOOL_OPT", 0, 0)
|
|
|
+ verify_mode("TRISTATE", 2, 1)
|
|
|
+ verify_mode("TRISTATE_OPT", 0, 0)
|
|
|
+ verify_mode("BOOL_M", 0, 2)
|
|
|
+ verify_mode("TRISTATE_M", 0, 1)
|
|
|
+
|
|
|
+ # Test defaults
|
|
|
+
|
|
|
+ choice = c.named_choices["DEFAULTS"]
|
|
|
+
|
|
|
+ c.syms["TRISTATE_SYM"].set_value(0)
|
|
|
+ verify(choice.selection is c.syms["OPT_4"],
|
|
|
+ "Wrong choice default with TRISTATE_SYM = n")
|
|
|
+
|
|
|
+ c.syms["TRISTATE_SYM"].set_value(2)
|
|
|
+ verify(choice.selection is c.syms["OPT_2"],
|
|
|
+ "Wrong choice default with TRISTATE_SYM = y")
|
|
|
+
|
|
|
+ c.syms["OPT_1"].set_value(2)
|
|
|
+ verify(choice.selection is c.syms["OPT_1"],
|
|
|
+ "User selection should override defaults")
|
|
|
+
|
|
|
+ verify(c.named_choices["DEFAULTS_NOT_VISIBLE"].selection
|
|
|
+ is c.syms["OPT_8"],
|
|
|
+ "Non-visible choice symbols should cause the next default to be "
|
|
|
+ "considered")
|
|
|
+
|
|
|
+ # Test y mode selection
|
|
|
+
|
|
|
+ c.modules.set_value(2)
|
|
|
+
|
|
|
+ select_and_verify_all("BOOL")
|
|
|
+ select_and_verify_all("BOOL_OPT")
|
|
|
+ select_and_verify_all("TRISTATE")
|
|
|
+ select_and_verify_all("TRISTATE_OPT")
|
|
|
+ # For BOOL_M, the mode should have been promoted
|
|
|
+ select_and_verify_all("BOOL_M")
|
|
|
+
|
|
|
+ # Test m mode selection
|
|
|
+
|
|
|
+ c.named_choices["TRISTATE"].set_value(1)
|
|
|
+
|
|
|
+ verify(c.named_choices["TRISTATE"].tri_value == 1,
|
|
|
+ "TRISTATE choice should have mode m after explicit mode assignment")
|
|
|
+
|
|
|
+ assign_and_verify_value("T_1", 0, 0)
|
|
|
+ assign_and_verify_value("T_2", 0, 0)
|
|
|
+ assign_and_verify_value("T_1", 1, 1)
|
|
|
+ assign_and_verify_value("T_2", 1, 1)
|
|
|
+ assign_and_verify_value("T_1", 2, 1)
|
|
|
+ assign_and_verify_value("T_2", 2, 1)
|
|
|
+
|
|
|
+ # Switching to y mode should cause T_2 to become selected
|
|
|
+ c.named_choices["TRISTATE"].set_value(2)
|
|
|
+ verify_value("T_1", 0)
|
|
|
+ verify_value("T_2", 2)
|
|
|
+
|
|
|
+ # Verify that choices with no explicitly specified type get the type of the
|
|
|
+ # first contained symbol with a type
|
|
|
+
|
|
|
+ verify(c.named_choices["NO_TYPE_BOOL"].orig_type == BOOL,
|
|
|
+ "Expected first choice without explicit type to have type bool")
|
|
|
+
|
|
|
+ verify(c.named_choices["NO_TYPE_TRISTATE"].orig_type == TRISTATE,
|
|
|
+ "Expected second choice without explicit type to have type "
|
|
|
+ "tristate")
|
|
|
+
|
|
|
+ # Verify that symbols without a type in the choice get the type of the
|
|
|
+ # choice
|
|
|
+
|
|
|
+ for name in "MMT_1", "MMT_2", "MMT_4", "MMT_5":
|
|
|
+ verify(c.syms[name].orig_type == BOOL,
|
|
|
+ "Expected {} to get type bool".format(name))
|
|
|
+
|
|
|
+ verify(c.syms["MMT_3"].orig_type == TRISTATE,
|
|
|
+ "Expected MMT_3 to have type tristate")
|
|
|
+
|
|
|
+ # Verify that the default selection can change depending on the
|
|
|
+ # visibility of the choice symbols
|
|
|
+
|
|
|
+ default_with_dep_choice = c.named_choices["DEFAULT_WITH_DEP"]
|
|
|
+
|
|
|
+ verify(default_with_dep_choice.selection is c.syms["B"],
|
|
|
+ "Wrong choice default with unsatisfied deps on default")
|
|
|
+
|
|
|
+ c.syms["DEP"].set_value("y")
|
|
|
+
|
|
|
+ verify(default_with_dep_choice.selection is c.syms["A"],
|
|
|
+ "Wrong choice default with satisfied deps on default")
|
|
|
+
|
|
|
+ c.syms["DEP"].set_value("n")
|
|
|
+
|
|
|
+ verify(default_with_dep_choice.selection is c.syms["B"],
|
|
|
+ "Wrong choice default with unsatisfied deps on default (round two)")
|
|
|
+
|
|
|
+ # Verify that symbols in choices that depend on the preceding symbol aren't
|
|
|
+ # considered choice symbols
|
|
|
+
|
|
|
+ weird_choice = c.named_choices["WEIRD_SYMS"]
|
|
|
+
|
|
|
+ def verify_is_normal_choice_symbol(name):
|
|
|
+ sym = c.syms[name]
|
|
|
+ verify(sym.choice is not None and
|
|
|
+ sym in weird_choice.syms and
|
|
|
+ sym.nodes[0].parent.item is weird_choice,
|
|
|
+ "{} should be a normal choice symbol".format(sym.name))
|
|
|
+
|
|
|
+ def verify_is_weird_choice_symbol(name):
|
|
|
+ sym = c.syms[name]
|
|
|
+ verify(sym.choice is None and
|
|
|
+ sym not in weird_choice.syms,
|
|
|
+ "{} should be a weird (non-)choice symbol"
|
|
|
+ .format(sym.name))
|
|
|
+
|
|
|
+ verify_is_normal_choice_symbol("WS1")
|
|
|
+ verify_is_weird_choice_symbol("WS2")
|
|
|
+ verify_is_weird_choice_symbol("WS3")
|
|
|
+ verify_is_weird_choice_symbol("WS4")
|
|
|
+ verify_is_weird_choice_symbol("WS5")
|
|
|
+ verify_is_normal_choice_symbol("WS6")
|
|
|
+ verify_is_weird_choice_symbol("WS7")
|
|
|
+ verify_is_weird_choice_symbol("WS8")
|
|
|
+ verify_is_normal_choice_symbol("WS9")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing 'if' node removal")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kifremoval", warn=False)
|
|
|
+
|
|
|
+ nodes = tuple(c.node_iter())
|
|
|
+ verify_equal(nodes[0].item.name, "A")
|
|
|
+ verify_equal(nodes[1].item.name, "B")
|
|
|
+ verify_equal(nodes[2].item.name, "C")
|
|
|
+ verify_equal(nodes[3].item.name, "D")
|
|
|
+ verify_equal(nodes[4].prompt[0], "E")
|
|
|
+ verify_equal(nodes[5].prompt[0], "F")
|
|
|
+ verify_equal(nodes[6].prompt[0], "G")
|
|
|
+ verify_equal(nodes[7].item.name, "H")
|
|
|
+ verify_equal(nodes[8].item.name, "I")
|
|
|
+ verify_equal(nodes[9].item.name, "J")
|
|
|
+ verify(len(nodes) == 10,
|
|
|
+ "Wrong number of nodes after 'if' removal")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing multi.def. property copying")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kdepcopy", warn=False)
|
|
|
+
|
|
|
+ def verify_props(desc, props, prop_names):
|
|
|
+ actual = [prop[0].name for prop in props]
|
|
|
+ expected = prop_names.split()
|
|
|
+
|
|
|
+ verify(actual == expected,
|
|
|
+ "Wrong {} properties, expected '{}', got '{}'"
|
|
|
+ .format(desc, expected, actual))
|
|
|
+
|
|
|
+ verify_props("default", c.syms["MULTIDEF"].defaults,
|
|
|
+ "A B C D E F G H I J K L M N O P Q R")
|
|
|
+
|
|
|
+ verify_props("select", c.syms["MULTIDEF"].selects,
|
|
|
+ "AA BB CC DD EE FF GG HH II JJ")
|
|
|
+
|
|
|
+ verify_props("imply", c.syms["MULTIDEF"].selects,
|
|
|
+ "AA BB CC DD EE FF GG HH II JJ")
|
|
|
+
|
|
|
+ verify_props("select", c.syms["MULTIDEF_CHOICE"].selects,
|
|
|
+ "A B C")
|
|
|
+
|
|
|
+ verify_props("range", c.syms["MULTIDEF_RANGE"].ranges,
|
|
|
+ "A B C D E F")
|
|
|
+
|
|
|
+ verify_props("default", c.choices[1].defaults,
|
|
|
+ "A B C D E")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing dependency loop detection")
|
|
|
+
|
|
|
+ # These are all expected to raise dependency loop errors
|
|
|
+ for i in range(11):
|
|
|
+ filename = "Kconfiglib/tests/Kdeploop" + str(i)
|
|
|
+ try:
|
|
|
+ Kconfig(filename)
|
|
|
+ except KconfigError as e:
|
|
|
+ if "Dependency loop" not in str(e):
|
|
|
+ fail("dependency loop in {} raised wrong KconfigError"
|
|
|
+ .format(filename))
|
|
|
+ except:
|
|
|
+ fail("dependency loop in {} raised wrong exception"
|
|
|
+ .format(filename))
|
|
|
+ else:
|
|
|
+ fail("dependency loop in {} not detected".format(filename))
|
|
|
+
|
|
|
+ # Check the most complicated message completely
|
|
|
+ try:
|
|
|
+ Kconfig("Kconfiglib/tests/Kdeploop10")
|
|
|
+ except KconfigError as e:
|
|
|
+ verify_equal(str(e), """
|
|
|
+Dependency loop
|
|
|
+===============
|
|
|
+
|
|
|
+A (defined at Kconfiglib/tests/Kdeploop10:1), with definition...
|
|
|
+
|
|
|
+config A
|
|
|
+ bool
|
|
|
+ depends on B
|
|
|
+
|
|
|
+...depends on B (defined at Kconfiglib/tests/Kdeploop10:5), with definition...
|
|
|
+
|
|
|
+config B
|
|
|
+ bool
|
|
|
+ depends on C = 7
|
|
|
+
|
|
|
+...depends on C (defined at Kconfiglib/tests/Kdeploop10:9), with definition...
|
|
|
+
|
|
|
+config C
|
|
|
+ int
|
|
|
+ range D 8
|
|
|
+
|
|
|
+...depends on D (defined at Kconfiglib/tests/Kdeploop10:13), with definition...
|
|
|
+
|
|
|
+config D
|
|
|
+ int
|
|
|
+ default 3 if E
|
|
|
+ default 8
|
|
|
+
|
|
|
+...depends on E (defined at Kconfiglib/tests/Kdeploop10:18), with definition...
|
|
|
+
|
|
|
+config E
|
|
|
+ bool
|
|
|
+
|
|
|
+(select-related dependencies: F && G)
|
|
|
+
|
|
|
+...depends on G (defined at Kconfiglib/tests/Kdeploop10:25), with definition...
|
|
|
+
|
|
|
+config G
|
|
|
+ bool
|
|
|
+ depends on H
|
|
|
+
|
|
|
+...depends on the choice symbol H (defined at Kconfiglib/tests/Kdeploop10:32), with definition...
|
|
|
+
|
|
|
+config H
|
|
|
+ bool "H"
|
|
|
+ depends on I && <choice>
|
|
|
+
|
|
|
+...depends on the choice symbol I (defined at Kconfiglib/tests/Kdeploop10:41), with definition...
|
|
|
+
|
|
|
+config I
|
|
|
+ bool "I"
|
|
|
+ depends on <choice>
|
|
|
+
|
|
|
+...depends on <choice> (defined at Kconfiglib/tests/Kdeploop10:38), with definition...
|
|
|
+
|
|
|
+choice
|
|
|
+ bool "choice" if J
|
|
|
+
|
|
|
+...depends on J (defined at Kconfiglib/tests/Kdeploop10:46), with definition...
|
|
|
+
|
|
|
+config J
|
|
|
+ bool
|
|
|
+ depends on A
|
|
|
+
|
|
|
+...depends again on A (defined at Kconfiglib/tests/Kdeploop10:1)
|
|
|
+"""[:-1])
|
|
|
+ except:
|
|
|
+ fail("Loop detection message check raised wrong exception")
|
|
|
+ else:
|
|
|
+ fail("Loop detection message check did not raise exception")
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing preprocessor")
|
|
|
+
|
|
|
+ os.environ["ENV_1"] = "env_1"
|
|
|
+ os.environ["ENV_2"] = "env_2"
|
|
|
+ os.environ["ENV_3"] = "env_3"
|
|
|
+ os.environ["ENV_4"] = "env_4"
|
|
|
+ os.environ["ENV_5"] = "n"
|
|
|
+ os.environ["ENV_6"] = "Kconfiglib/tests/empty"
|
|
|
+ os.environ["ENV_7"] = "env_7"
|
|
|
+ # We verify warnings manually
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kpreprocess", warn_to_stderr=False)
|
|
|
+
|
|
|
+ def verify_variable(name, unexp_value, exp_value, recursive, *args):
|
|
|
+ var = c.variables[name]
|
|
|
+
|
|
|
+ verify(var.value == unexp_value,
|
|
|
+ "expected variable '{}' to have the unexpanded value '{}', had "
|
|
|
+ "the value '{}'".format(name, unexp_value, var.value))
|
|
|
+
|
|
|
+ if not args:
|
|
|
+ verify(var.expanded_value == exp_value,
|
|
|
+ "expected expanded_value for {} to be '{}', was '{}'"
|
|
|
+ .format(name, exp_value, var.expanded_value))
|
|
|
+
|
|
|
+ verify(var.expanded_value_w_args(*args) == exp_value,
|
|
|
+ "expected expanded_value_w_args() for '{}' to be '{}', was '{}'"
|
|
|
+ .format(name, exp_value, var.expanded_value_w_args(*args)))
|
|
|
+
|
|
|
+ verify(var.is_recursive == recursive,
|
|
|
+ "{} was {}, shouldn't be"
|
|
|
+ .format(name, "recursive" if var.is_recursive else "simple"))
|
|
|
+
|
|
|
+ verify_variable("simple-recursive", "foo", "foo", True)
|
|
|
+ verify_variable("simple-immediate", "bar", "bar", False)
|
|
|
+ verify_variable("simple-recursive-2", "baz", "baz", True)
|
|
|
+
|
|
|
+ verify_variable("whitespaced", "foo", "foo", True)
|
|
|
+
|
|
|
+ verify_variable("preserve-recursive", "foo bar", "foo bar", True)
|
|
|
+ verify_variable("preserve-immediate", "foo bar", "foo bar", False)
|
|
|
+
|
|
|
+ verify_variable("recursive",
|
|
|
+ "$(foo) $(bar) $($(b-char)a$(z-char)) $(indir)",
|
|
|
+ "abc def ghi jkl mno",
|
|
|
+ True)
|
|
|
+
|
|
|
+ verify_variable("immediate", "foofoo", "foofoo", False)
|
|
|
+
|
|
|
+ verify_variable("messy-fn-res",
|
|
|
+ "$($(fn-indir)-unused-arg, a b (,) , c d )",
|
|
|
+ 'surround-rev-quote " c d " " a b (,) " surround-rev-quote ',
|
|
|
+ True)
|
|
|
+
|
|
|
+ verify_variable("special-chars-fn-res",
|
|
|
+ "$(fn,$(comma)$(dollar)$(left-paren)foo$(right-paren))",
|
|
|
+ '",$(foo)"',
|
|
|
+ True)
|
|
|
+
|
|
|
+ verify_variable("quote", '"$(1)" "$(2)"', '"" ""', True)
|
|
|
+ verify_variable("quote", '"$(1)" "$(2)"', '"one" ""', True,
|
|
|
+ "one")
|
|
|
+ verify_variable("quote", '"$(1)" "$(2)"', '"one" "two"', True,
|
|
|
+ "one", "two")
|
|
|
+ verify_variable("quote", '"$(1)" "$(2)"', '"one" "two"', True,
|
|
|
+ "one", "two", "three")
|
|
|
+
|
|
|
+ verify_str(c.syms["PRINT_ME"], r"""
|
|
|
+config PRINT_ME
|
|
|
+ string "env_1" if (FOO && BAR) || !BAZ || !QAZ
|
|
|
+ default "\"foo\"" if "foo \"bar\" baz" = ""
|
|
|
+""")
|
|
|
+
|
|
|
+ verify_str(c.syms["PRINT_ME_TOO"], r"""
|
|
|
+config PRINT_ME_TOO
|
|
|
+ bool "foo"
|
|
|
+ default FOOBARBAZQAZ if QAZ && QAZFOO && xxx
|
|
|
+""")
|
|
|
+
|
|
|
+ def verify_repr(name, s):
|
|
|
+ verify_equal(repr(c.variables[name]), s)
|
|
|
+
|
|
|
+ verify_repr(
|
|
|
+ "simple-immediate",
|
|
|
+ "<variable simple-immediate, immediate, value 'bar'>")
|
|
|
+
|
|
|
+ verify_repr(
|
|
|
+ "messy-fn-res",
|
|
|
+ "<variable messy-fn-res, recursive, value '$($(fn-indir)-unused-arg, a b (,) , c d )'>")
|
|
|
+
|
|
|
+ def verify_recursive(name):
|
|
|
+ try:
|
|
|
+ c.variables[name].expanded_value
|
|
|
+ except KconfigError:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ fail("Expected '{}' expansion to flag recursive expansion, didn't"
|
|
|
+ .format(name))
|
|
|
+
|
|
|
+ verify_recursive("rec-1")
|
|
|
+ # Indirectly verifies that it's not recursive
|
|
|
+ verify_variable("safe-fn-rec-res",
|
|
|
+ "$(safe-fn-rec,safe-fn-rec-2)",
|
|
|
+ "foo",
|
|
|
+ True)
|
|
|
+ verify_recursive("unsafe-fn-rec")
|
|
|
+
|
|
|
+ verify_variable("foo-bar-baz", "$(rhs)", "value", True)
|
|
|
+
|
|
|
+ verify_variable("space-var-res", "$(foo bar)", "value", True)
|
|
|
+
|
|
|
+ verify_variable("shell-res",
|
|
|
+ "$(shell,false && echo foo bar || echo baz qaz)",
|
|
|
+ "baz qaz",
|
|
|
+ True)
|
|
|
+
|
|
|
+ verify_variable("shell-stderr-res", "", "", False)
|
|
|
+
|
|
|
+ verify_variable("parens-res",
|
|
|
+ "pre-$(shell,echo '(a,$(b-char),(c,d),e)')-post",
|
|
|
+ "pre-(a,b,(c,d),e)-post",
|
|
|
+ True)
|
|
|
+
|
|
|
+ verify_variable("location-res",
|
|
|
+ "Kconfiglib/tests/Kpreprocess:129",
|
|
|
+ "Kconfiglib/tests/Kpreprocess:129",
|
|
|
+ False)
|
|
|
+
|
|
|
+ verify_variable("warning-res", "", "", False)
|
|
|
+ verify_variable("error-n-res", "", "", False)
|
|
|
+
|
|
|
+ try:
|
|
|
+ c.variables["error-y-res"].expanded_value
|
|
|
+ except KconfigError:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ fail("expanding error-y-res didn't raise an exception")
|
|
|
+
|
|
|
+ # Check Kconfig.env_vars
|
|
|
+ verify_equal(c.env_vars,
|
|
|
+ set(("ENV_1", "ENV_2", "ENV_3", "ENV_4", "ENV_5", "ENV_6")))
|
|
|
+
|
|
|
+ # Check that the expected warnings were generated
|
|
|
+ verify_equal(c.warnings, [
|
|
|
+ "Kconfiglib/tests/Kpreprocess:122: warning: 'echo message on stderr >&2' wrote to stderr: message on stderr",
|
|
|
+ "Kconfiglib/tests/Kpreprocess:134: warning: a warning"
|
|
|
+ ])
|
|
|
+
|
|
|
+
|
|
|
+ print("Testing user-defined preprocessor functions")
|
|
|
+
|
|
|
+ # Make Kconfiglib/tests/kconfigfunctions.py importable
|
|
|
+ sys.path.insert(0, "Kconfiglib/tests")
|
|
|
+
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kuserfunctions")
|
|
|
+
|
|
|
+ verify_variable("add-zero", "$(add)", "0", True)
|
|
|
+ verify_variable("add-one", "$(add,1)", "1", True)
|
|
|
+ verify_variable("add-three", "$(add,1,-1,2,1)", "3", True)
|
|
|
+
|
|
|
+ verify_variable("one-one", "$(one,foo bar)", "onefoo barfoo bar", True)
|
|
|
+
|
|
|
+ verify_variable("one-or-more-one", "$(one-or-more,foo)", "foo + ", True)
|
|
|
+ verify_variable("one-or-more-three", "$(one-or-more,foo,bar,baz)",
|
|
|
+ "foo + bar,baz", True)
|
|
|
+
|
|
|
+ verify_variable("location-1", "Kconfiglib/tests/Kuserfunctions:13",
|
|
|
+ "Kconfiglib/tests/Kuserfunctions:13", False)
|
|
|
+ verify_variable("location-2", "Kconfiglib/tests/Kuserfunctions:14",
|
|
|
+ "Kconfiglib/tests/Kuserfunctions:14", False)
|
|
|
+
|
|
|
+ def verify_bad_argno(name):
|
|
|
+ try:
|
|
|
+ c.variables[name].expanded_value
|
|
|
+ except KconfigError:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ fail("Expected '{}' expansion to flag wrong number of arguments, "
|
|
|
+ "didn't".format(name))
|
|
|
+
|
|
|
+ verify_bad_argno("one-zero")
|
|
|
+ verify_bad_argno("one-two")
|
|
|
+ verify_bad_argno("one-or-more-zero")
|
|
|
+
|
|
|
+ sys.path.pop(0)
|
|
|
+
|
|
|
+ # This test can fail on older Python 3.x versions, because they don't
|
|
|
+ # preserve dict insertion order during iteration. The output is still
|
|
|
+ # correct, just different.
|
|
|
+ if not (3, 0) <= sys.version_info <= (3, 5):
|
|
|
+ print("Testing KCONFIG_WARN_UNDEF")
|
|
|
+
|
|
|
+ os.environ["KCONFIG_WARN_UNDEF"] = "y"
|
|
|
+ c = Kconfig("Kconfiglib/tests/Kundef", warn_to_stderr=False)
|
|
|
+
|
|
|
+ verify_equal("\n".join(c.warnings), """
|
|
|
+warning: the int symbol INT (defined at Kconfiglib/tests/Kundef:8) has a non-int range [UNDEF_2 (undefined), 8 (undefined)]
|
|
|
+warning: undefined symbol UNDEF_1:
|
|
|
+
|
|
|
+- Referenced at Kconfiglib/tests/Kundef:4:
|
|
|
+
|
|
|
+config BOOL
|
|
|
+ bool "foo" if DEF || !UNDEF_1
|
|
|
+ default UNDEF_2
|
|
|
+
|
|
|
+- Referenced at Kconfiglib/tests/Kundef:19:
|
|
|
+
|
|
|
+menu "menu"
|
|
|
+ depends on UNDEF_1
|
|
|
+ visible if UNDEF_3
|
|
|
+warning: undefined symbol UNDEF_2:
|
|
|
+
|
|
|
+- Referenced at Kconfiglib/tests/Kundef:4:
|
|
|
+
|
|
|
+config BOOL
|
|
|
+ bool "foo" if DEF || !UNDEF_1
|
|
|
+ default UNDEF_2
|
|
|
+
|
|
|
+- Referenced at Kconfiglib/tests/Kundef:8:
|
|
|
+
|
|
|
+config INT
|
|
|
+ int
|
|
|
+ range UNDEF_2 8
|
|
|
+ range 5 15
|
|
|
+ default 10
|
|
|
+warning: undefined symbol UNDEF_3:
|
|
|
+
|
|
|
+- Referenced at Kconfiglib/tests/Kundef:19:
|
|
|
+
|
|
|
+menu "menu"
|
|
|
+ depends on UNDEF_1
|
|
|
+ visible if UNDEF_3
|
|
|
+"""[1:-1])
|
|
|
+
|
|
|
+ os.environ.pop("KCONFIG_WARN_UNDEF")
|
|
|
+
|
|
|
+
|
|
|
+ print("\nAll selftests passed\n" if all_passed else
|
|
|
+ "\nSome selftests failed\n")
|
|
|
+
|
|
|
+
|
|
|
+def run_compatibility_tests():
|
|
|
+ # Runs tests on configurations from the kernel. Tests compability with the
|
|
|
+ # C implementation by comparing outputs.
|
|
|
+
|
|
|
+ # Referenced inside the kernel Kconfig files.
|
|
|
+ #
|
|
|
+ # The str() makes the type of the value 'str' on both Python 2 and Python 3,
|
|
|
+ # which is nice for some later dictionary key sanity checks.
|
|
|
+
|
|
|
+ os.environ["KERNELVERSION"] = str(
|
|
|
+ subprocess.check_output("make kernelversion", shell=True)
|
|
|
+ .decode("utf-8").rstrip()
|
|
|
+ )
|
|
|
+
|
|
|
+ os.environ["CC_VERSION_TEXT"] = str(
|
|
|
+ subprocess.check_output("gcc --version | head -n1", shell=True)
|
|
|
+ .decode("utf-8").rstrip()
|
|
|
+ )
|
|
|
+
|
|
|
+ os.environ["srctree"] = "."
|
|
|
+ os.environ["CC"] = "gcc"
|
|
|
+ os.environ["LD"] = "ld"
|
|
|
+
|
|
|
+
|
|
|
+ if not os.path.exists("scripts/kconfig/conf"):
|
|
|
+ print("\nscripts/kconfig/conf does not exist -- running "
|
|
|
+ "'make allnoconfig' to build it...")
|
|
|
+ shell("make allnoconfig")
|
|
|
+
|
|
|
+
|
|
|
+ print("Running compatibility tests...\n")
|
|
|
+
|
|
|
+ test_fns = (test_defconfig,
|
|
|
+ # Fails for a few defconfigs due to a bug in the C tools. Will
|
|
|
+ # be enabled once patches get in.
|
|
|
+ #test_min_config,
|
|
|
+ test_alldefconfig,
|
|
|
+ test_allnoconfig,
|
|
|
+ test_allnoconfig_walk,
|
|
|
+ test_allmodconfig,
|
|
|
+ test_allyesconfig,
|
|
|
+ test_sanity)
|
|
|
+
|
|
|
+ for test_fn in test_fns:
|
|
|
+ # The test description is taken from the docstring of the corresponding
|
|
|
+ # function
|
|
|
+ print(textwrap.dedent(test_fn.__doc__))
|
|
|
+
|
|
|
+ for arch, srcarch in all_arch_srcarch():
|
|
|
+ # Referenced inside the Kconfig files
|
|
|
+ os.environ["ARCH"] = arch
|
|
|
+ os.environ["SRCARCH"] = srcarch
|
|
|
+
|
|
|
+ rm_configs()
|
|
|
+
|
|
|
+ test_fn(arch, srcarch)
|
|
|
+
|
|
|
+ if all_passed:
|
|
|
+ print("All selftests and compatibility tests passed")
|
|
|
+ else:
|
|
|
+ sys.exit("Some tests failed")
|
|
|
+
|
|
|
+
|
|
|
+def all_arch_srcarch():
|
|
|
+ for srcarch in os.listdir("arch"):
|
|
|
+ # arc and h8300 are currently broken with the C tools on linux-next as
|
|
|
+ # well. Perhaps they require cross-compilers to be installed.
|
|
|
+ #
|
|
|
+ # User-mode Linux has an unorthodox Kconfig setup that would require a
|
|
|
+ # different testing setup. Skip it too.
|
|
|
+ if srcarch in ("arc", "h8300", "um"):
|
|
|
+ continue
|
|
|
+
|
|
|
+ if os.path.exists(os.path.join("arch", srcarch, "Kconfig")):
|
|
|
+ yield (srcarch, srcarch)
|
|
|
+
|
|
|
+ # Some arches define additional ARCH settings with ARCH != SRCARCH
|
|
|
+ # (search for "Additional ARCH settings for" in the top-level Makefile)
|
|
|
+
|
|
|
+ yield ("i386", "x86")
|
|
|
+ yield ("x86_64", "x86")
|
|
|
+
|
|
|
+ yield ("sparc32", "sparc")
|
|
|
+ yield ("sparc64", "sparc")
|
|
|
+
|
|
|
+ yield ("sh64", "sh")
|
|
|
+
|
|
|
+
|
|
|
+def test_allnoconfig(arch, srcarch):
|
|
|
+ """
|
|
|
+ Verify that allnoconfig.py generates the same .config as
|
|
|
+ 'make allnoconfig', for each architecture. Runs the script via
|
|
|
+ 'make scriptconfig'.
|
|
|
+ """
|
|
|
+ shell("make scriptconfig SCRIPT=Kconfiglib/allnoconfig.py "
|
|
|
+ "PYTHONCMD='{}'".format(sys.executable))
|
|
|
+ shell("mv .config ._config")
|
|
|
+ shell("scripts/kconfig/conf --allnoconfig Kconfig")
|
|
|
+
|
|
|
+ compare_configs(arch)
|
|
|
+
|
|
|
+
|
|
|
+def test_allnoconfig_walk(arch, srcarch):
|
|
|
+ """
|
|
|
+ Verify that examples/allnoconfig_walk.py generates the same .config as
|
|
|
+ 'make allnoconfig', for each architecture. Runs the script via
|
|
|
+ 'make scriptconfig'.
|
|
|
+ """
|
|
|
+ shell("make scriptconfig SCRIPT=Kconfiglib/examples/allnoconfig_walk.py "
|
|
|
+ "PYTHONCMD='{}'".format(sys.executable))
|
|
|
+ shell("mv .config ._config")
|
|
|
+ shell("scripts/kconfig/conf --allnoconfig Kconfig")
|
|
|
+
|
|
|
+ compare_configs(arch)
|
|
|
+
|
|
|
+
|
|
|
+def test_allmodconfig(arch, srcarch):
|
|
|
+ """
|
|
|
+ Verify that allmodconfig.py generates the same .config as
|
|
|
+ 'make allmodconfig', for each architecture. Runs the script via
|
|
|
+ 'make scriptconfig'.
|
|
|
+ """
|
|
|
+ shell("make scriptconfig SCRIPT=Kconfiglib/allmodconfig.py "
|
|
|
+ "PYTHONCMD='{}'".format(sys.executable))
|
|
|
+ shell("mv .config ._config")
|
|
|
+ shell("scripts/kconfig/conf --allmodconfig Kconfig")
|
|
|
+
|
|
|
+ compare_configs(arch)
|
|
|
+
|
|
|
+
|
|
|
+def test_allyesconfig(arch, srcarch):
|
|
|
+ """
|
|
|
+ Verify that allyesconfig.py generates the same .config as
|
|
|
+ 'make allyesconfig', for each architecture. Runs the script via
|
|
|
+ 'make scriptconfig'.
|
|
|
+ """
|
|
|
+ shell("make scriptconfig SCRIPT=Kconfiglib/allyesconfig.py "
|
|
|
+ "PYTHONCMD='{}'".format(sys.executable))
|
|
|
+ shell("mv .config ._config")
|
|
|
+ shell("scripts/kconfig/conf --allyesconfig Kconfig")
|
|
|
+
|
|
|
+ compare_configs(arch)
|
|
|
+
|
|
|
+
|
|
|
+def test_sanity(arch, srcarch):
|
|
|
+ """
|
|
|
+ Do sanity checks on each configuration and call all public methods on all
|
|
|
+ symbols, choices, and menu nodes for all architectures to make sure we
|
|
|
+ never crash or hang.
|
|
|
+ """
|
|
|
+ print("For {}...".format(arch))
|
|
|
+
|
|
|
+ kconf = Kconfig()
|
|
|
+
|
|
|
+ for sym in kconf.defined_syms:
|
|
|
+ verify(sym._visited == 2,
|
|
|
+ "{} has broken dependency loop detection (_visited = {})"
|
|
|
+ .format(sym.name, sym._visited))
|
|
|
+
|
|
|
+ kconf.modules
|
|
|
+ kconf.defconfig_list
|
|
|
+ kconf.defconfig_filename
|
|
|
+
|
|
|
+ # Legacy warning functions
|
|
|
+ kconf.enable_redun_warnings()
|
|
|
+ kconf.disable_redun_warnings()
|
|
|
+ kconf.enable_undef_warnings()
|
|
|
+ kconf.disable_undef_warnings()
|
|
|
+ kconf.enable_warnings()
|
|
|
+ kconf.disable_warnings()
|
|
|
+ kconf.enable_stderr_warnings()
|
|
|
+ kconf.disable_stderr_warnings()
|
|
|
+
|
|
|
+ kconf.mainmenu_text
|
|
|
+ kconf.unset_values()
|
|
|
+
|
|
|
+ kconf.write_autoconf("/dev/null")
|
|
|
+
|
|
|
+ # No tempfile.TemporaryDirectory in Python 2
|
|
|
+ tmpdir = tempfile.mkdtemp()
|
|
|
+ kconf.sync_deps(os.path.join(tmpdir, "deps")) # Create
|
|
|
+ kconf.sync_deps(os.path.join(tmpdir, "deps")) # Update
|
|
|
+ shutil.rmtree(tmpdir)
|
|
|
+
|
|
|
+ # Python 2/3 compatible
|
|
|
+ for key, sym in kconf.syms.items():
|
|
|
+ verify(isinstance(key, str), "weird key '{}' in syms dict".format(key))
|
|
|
+
|
|
|
+ verify(not sym.is_constant, sym.name + " in 'syms' and constant")
|
|
|
+
|
|
|
+ verify(sym not in kconf.const_syms,
|
|
|
+ sym.name + " in both 'syms' and 'const_syms'")
|
|
|
+
|
|
|
+ for dep in sym._dependents:
|
|
|
+ verify(not dep.is_constant,
|
|
|
+ "the constant symbol {} depends on {}"
|
|
|
+ .format(dep.name, sym.name))
|
|
|
+
|
|
|
+ sym.__repr__()
|
|
|
+ sym.__str__()
|
|
|
+ sym.assignable
|
|
|
+ kconf.disable_warnings()
|
|
|
+ sym.set_value(2)
|
|
|
+ sym.set_value("foo")
|
|
|
+ sym.unset_value()
|
|
|
+ kconf.enable_warnings() # Legacy warning function
|
|
|
+ sym.str_value
|
|
|
+ sym.tri_value
|
|
|
+ sym.type
|
|
|
+ sym.user_value
|
|
|
+ sym.visibility
|
|
|
+
|
|
|
+ for sym in kconf.defined_syms:
|
|
|
+ verify(sym.nodes, sym.name + " is defined but lacks menu nodes")
|
|
|
+
|
|
|
+ verify(not (sym.orig_type not in (BOOL, TRISTATE) and sym.choice),
|
|
|
+ sym.name + " is a choice symbol but not bool/tristate")
|
|
|
+
|
|
|
+ for key, sym in kconf.const_syms.items():
|
|
|
+ verify(isinstance(key, str),
|
|
|
+ "weird key '{}' in const_syms dict".format(key))
|
|
|
+
|
|
|
+ verify(sym.is_constant,
|
|
|
+ '"{}" is in const_syms but not marked constant'
|
|
|
+ .format(sym.name))
|
|
|
+
|
|
|
+ verify(not sym.nodes,
|
|
|
+ '"{}" is constant but has menu nodes'.format(sym.name))
|
|
|
+
|
|
|
+ verify(not sym._dependents,
|
|
|
+ '"{}" is constant but is a dependency of some symbol'
|
|
|
+ .format(sym.name))
|
|
|
+
|
|
|
+ verify(not sym.choice,
|
|
|
+ '"{}" is constant and a choice symbol'.format(sym.name))
|
|
|
+
|
|
|
+ sym.__repr__()
|
|
|
+ sym.__str__()
|
|
|
+ sym.assignable
|
|
|
+ kconf.disable_warnings()
|
|
|
+ sym.set_value(2)
|
|
|
+ sym.set_value("foo")
|
|
|
+ sym.unset_value()
|
|
|
+ kconf.enable_warnings() # Legacy warning function
|
|
|
+ sym.str_value
|
|
|
+ sym.tri_value
|
|
|
+ sym.type
|
|
|
+ sym.visibility
|
|
|
+
|
|
|
+ for choice in kconf.choices:
|
|
|
+ for sym in choice.syms:
|
|
|
+ verify(sym.choice is choice,
|
|
|
+ "{0} is in choice.syms but 'sym.choice' is not the choice"
|
|
|
+ .format(sym.name))
|
|
|
+
|
|
|
+ verify(sym.type in (BOOL, TRISTATE),
|
|
|
+ "{} is a choice symbol but is not a bool/tristate"
|
|
|
+ .format(sym.name))
|
|
|
+
|
|
|
+ choice.__str__()
|
|
|
+ choice.__repr__()
|
|
|
+ choice.str_value
|
|
|
+ choice.tri_value
|
|
|
+ choice.user_value
|
|
|
+ choice.assignable
|
|
|
+ choice.selection
|
|
|
+ choice.type
|
|
|
+ choice.visibility
|
|
|
+
|
|
|
+ # Menu nodes
|
|
|
+
|
|
|
+ node = kconf.top_node
|
|
|
+
|
|
|
+ while 1:
|
|
|
+ # Everything else should be well exercised elsewhere
|
|
|
+ node.__repr__()
|
|
|
+ node.__str__()
|
|
|
+ verify(isinstance(node.item, (Symbol, Choice)) or \
|
|
|
+ node.item in (MENU, COMMENT),
|
|
|
+ "'{}' appeared as a menu item".format(node.item))
|
|
|
+
|
|
|
+ if node.list is not None:
|
|
|
+ node = node.list
|
|
|
+
|
|
|
+ elif node.next is not None:
|
|
|
+ node = node.next
|
|
|
+
|
|
|
+ else:
|
|
|
+ while node.parent is not None:
|
|
|
+ node = node.parent
|
|
|
+ if node.next is not None:
|
|
|
+ node = node.next
|
|
|
+ break
|
|
|
+ else:
|
|
|
+ break
|
|
|
+
|
|
|
+
|
|
|
+def test_alldefconfig(arch, srcarch):
|
|
|
+ """
|
|
|
+ Verify that alldefconfig.py generates the same .config as
|
|
|
+ 'make alldefconfig', for each architecture. Runs the script via
|
|
|
+ 'make scriptconfig'.
|
|
|
+ """
|
|
|
+ shell("make scriptconfig SCRIPT=Kconfiglib/alldefconfig.py "
|
|
|
+ "PYTHONCMD='{}'".format(sys.executable))
|
|
|
+ shell("mv .config ._config")
|
|
|
+ shell("scripts/kconfig/conf --alldefconfig Kconfig")
|
|
|
+
|
|
|
+ compare_configs(arch)
|
|
|
+
|
|
|
+
|
|
|
+def test_defconfig(arch, srcarch):
|
|
|
+ """
|
|
|
+ Verify that Kconfiglib generates the same .config as scripts/kconfig/conf,
|
|
|
+ for each architecture/defconfig pair. In obsessive mode, this test includes
|
|
|
+ nonsensical groupings of arches with defconfigs from other arches (every
|
|
|
+ arch/defconfig combination) and takes an order of magnitude longer time to
|
|
|
+ run.
|
|
|
+
|
|
|
+ With logging enabled, this test appends any failures to a file
|
|
|
+ test_defconfig_fails in the root.
|
|
|
+ """
|
|
|
+ kconf = Kconfig()
|
|
|
+
|
|
|
+ if obsessive:
|
|
|
+ defconfigs = []
|
|
|
+
|
|
|
+ # Collect all defconfigs. This could be done once instead, but it's
|
|
|
+ # a speedy operation comparatively.
|
|
|
+ for srcarch_ in os.listdir("arch"):
|
|
|
+ defconfigs.extend(defconfig_files(srcarch_))
|
|
|
+ else:
|
|
|
+ defconfigs = defconfig_files(srcarch)
|
|
|
+
|
|
|
+ # Test architecture for each defconfig
|
|
|
+
|
|
|
+ for defconfig in defconfigs:
|
|
|
+ rm_configs()
|
|
|
+
|
|
|
+ kconf.load_config(defconfig)
|
|
|
+ kconf.write_config("._config")
|
|
|
+ shell("scripts/kconfig/conf --defconfig='{}' Kconfig".
|
|
|
+ format(defconfig))
|
|
|
+
|
|
|
+ arch_defconfig_str = " {:14}with {:60} ".format(arch, defconfig)
|
|
|
+
|
|
|
+ if equal_configs():
|
|
|
+ print(arch_defconfig_str + "OK")
|
|
|
+ else:
|
|
|
+ print(arch_defconfig_str + "FAIL")
|
|
|
+ fail()
|
|
|
+ if log:
|
|
|
+ with open("test_defconfig_fails", "a") as fail_log:
|
|
|
+ fail_log.write("{} with {} did not match\n"
|
|
|
+ .format(arch, defconfig))
|
|
|
+
|
|
|
+
|
|
|
+def test_min_config(arch, srcarch):
|
|
|
+ """
|
|
|
+ Verify that Kconfiglib generates the same .config as 'make savedefconfig'
|
|
|
+ for each architecture/defconfig pair.
|
|
|
+ """
|
|
|
+ kconf = Kconfig()
|
|
|
+
|
|
|
+ if obsessive_min_config:
|
|
|
+ defconfigs = []
|
|
|
+ for srcarch_ in os.listdir("arch"):
|
|
|
+ defconfigs.extend(defconfig_files(srcarch_))
|
|
|
+ else:
|
|
|
+ defconfigs = defconfig_files(srcarch)
|
|
|
+
|
|
|
+ for defconfig in defconfigs:
|
|
|
+ rm_configs()
|
|
|
+
|
|
|
+ kconf.load_config(defconfig)
|
|
|
+ kconf.write_min_config("._config")
|
|
|
+
|
|
|
+ shell("cp {} .config".format(defconfig))
|
|
|
+
|
|
|
+ shell("scripts/kconfig/conf --savedefconfig=.config Kconfig")
|
|
|
+
|
|
|
+ arch_defconfig_str = " {:14}with {:60} ".format(arch, defconfig)
|
|
|
+
|
|
|
+ if equal_configs():
|
|
|
+ print(arch_defconfig_str + "OK")
|
|
|
+ else:
|
|
|
+ print(arch_defconfig_str + "FAIL")
|
|
|
+
|
|
|
+
|
|
|
+#
|
|
|
+# Helper functions
|
|
|
+#
|
|
|
+
|
|
|
+
|
|
|
+def defconfig_files(srcarch):
|
|
|
+ # Yields a list of defconfig file filenames for a particular srcarch
|
|
|
+ # subdirectory (arch/<srcarch>/)
|
|
|
+
|
|
|
+ srcarch_dir = os.path.join("arch", srcarch)
|
|
|
+
|
|
|
+ # Some arches have a defconfig in the root of their arch/<arch>/ directory
|
|
|
+ root_defconfig = os.path.join(srcarch_dir, "defconfig")
|
|
|
+ if os.path.exists(root_defconfig):
|
|
|
+ yield root_defconfig
|
|
|
+
|
|
|
+ # Assume all files in the arch/<arch>/configs/ directory (if it exists) are
|
|
|
+ # configurations
|
|
|
+ defconfigs_dir = os.path.join(srcarch_dir, "configs")
|
|
|
+
|
|
|
+ if not os.path.isdir(defconfigs_dir):
|
|
|
+ return
|
|
|
+
|
|
|
+ for dirpath, _, filenames in os.walk(defconfigs_dir):
|
|
|
+ for filename in filenames:
|
|
|
+ yield os.path.join(dirpath, filename)
|
|
|
+
|
|
|
+
|
|
|
+def rm_configs():
|
|
|
+ # Delete any old ".config" (generated by the C implementation) and
|
|
|
+ # "._config" (generated by us), if present.
|
|
|
+
|
|
|
+ def rm_if_exists(f):
|
|
|
+ if os.path.exists(f):
|
|
|
+ os.remove(f)
|
|
|
+
|
|
|
+ rm_if_exists(".config")
|
|
|
+ rm_if_exists("._config")
|
|
|
+
|
|
|
+
|
|
|
+def compare_configs(arch):
|
|
|
+ if equal_configs():
|
|
|
+ print("{:14}OK".format(arch))
|
|
|
+ else:
|
|
|
+ print("{:14}FAIL".format(arch))
|
|
|
+ fail()
|
|
|
+
|
|
|
+
|
|
|
+def equal_configs():
|
|
|
+ with open(".config") as f:
|
|
|
+ their = f.readlines()
|
|
|
+
|
|
|
+ # Strip the header generated by 'conf'
|
|
|
+ i = 0
|
|
|
+ for line in their:
|
|
|
+ if not line.startswith("#") or \
|
|
|
+ re.match(r"# CONFIG_(\w+) is not set", line):
|
|
|
+ break
|
|
|
+ i += 1
|
|
|
+ their = their[i:]
|
|
|
+
|
|
|
+ try:
|
|
|
+ f = open("._config")
|
|
|
+ except EnvironmentError as e:
|
|
|
+ if e.errno != errno.ENOENT:
|
|
|
+ raise
|
|
|
+ print("._config not found. Did you forget to apply the Makefile patch?")
|
|
|
+ return False
|
|
|
+ else:
|
|
|
+ with f:
|
|
|
+ our = f.readlines()
|
|
|
+
|
|
|
+ if their == our:
|
|
|
+ return True
|
|
|
+
|
|
|
+ # Print a unified diff to help debugging
|
|
|
+ print("Mismatched .config's! Unified diff:")
|
|
|
+ sys.stdout.writelines(difflib.unified_diff(their, our, fromfile="their",
|
|
|
+ tofile="our"))
|
|
|
+
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ run_tests()
|