| # 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: |
|