diff options
| author | Ulf Magnusson <ulfalizer@gmail.com> | 2012-07-10 21:18:36 +0200 |
|---|---|---|
| committer | Ulf Magnusson <ulfalizer@gmail.com> | 2012-07-10 21:18:36 +0200 |
| commit | ec33e7cf05a984c0a1df4dd01878bece32e7ddc7 (patch) | |
| tree | c3544309e86363f0220307cf473f05ceb38b1e0e /kconfigtest.py | |
| parent | 13afd048d659f6a458161801bda1787efeb9aeba (diff) | |
Upload Kconfiglib, patch level 3.
Diffstat (limited to 'kconfigtest.py')
| -rw-r--r-- | kconfigtest.py | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/kconfigtest.py b/kconfigtest.py new file mode 100644 index 0000000..e6961a4 --- /dev/null +++ b/kconfigtest.py @@ -0,0 +1,396 @@ +# This is a test suite for kconfiglib, primarily testing compatibility with the +# C kconfig implementation by comparing outputs. It should be run from the +# top-level kernel directory with +# +# $ PYTHONPATH=scripts/kconfig python scripts/kconfig/kconfigtest.py +# +# Note that running these could take a long time: running all tests on a Core +# i7@2.67 GHz takes ~2 hours. The tests have been arranged in order of time +# needed. +# +# All tests should pass. Report regressions to kconfiglib@gmail.com + +import kconfiglib +import os +import re +import subprocess +import sys + +# Assume that the value of KERNELVERSION does not affect the configuration +# (true as of Linux 2.6.38-rc3). Here we could fetch the correct version +# instead. +os.environ["KERNELVERSION"] = "2" + +# Number of arch/defconfig pairs tested so far +nconfigs = 0 + +def run_tests(): + # The set of tests that want to run for all architectures in the kernel + # tree -- currently, all tests. The boolean flag indicates whether .config + # (generated by the C implementation) should be compared to ._config + # (generated by us) after each invocation. + all_arch_tests = [(test_all_no, True), + (test_config_absent, True), + (test_all_yes, True), + (test_call_all, False), + # Needs to report success/failure for each arch/defconfig + # combo, hence False. + (test_defconfig, False)] + + print "Loading Config instances for all architectures..." + arch_configs = get_arch_configs() + + for (test_fn, compare_configs) in all_arch_tests: + print "Resetting all architecture Config instances prior to next test..." + for arch in arch_configs: + arch.reset() + + # The test description is taken from the docstring of the corresponding + # function + print test_fn.__doc__ + + for conf in arch_configs: + rm_configs() + + # This should be set correctly for any 'make *config' commands the + # test might run + os.environ["ARCH"] = conf.get_arch() + + test_fn(conf) + + if compare_configs: + sys.stdout.write(" {0:<14}".format(conf.get_arch())) + + if equal_confs(): + print "OK" + else: + print "FAIL" + fail() + + print "" + + if all_ok(): + print "All OK" + print nconfigs, "arch/defconfig pairs tested" + else: + print "Some tests failed" + +def get_arch_configs(): + """Returns a list with Config instances corresponding to all arch Kconfigs.""" + + def add_arch(ARCH, res): + os.environ["SRCARCH"] = archdir + os.environ["ARCH"] = ARCH + res.append(kconfiglib.Config(base_dir = ".")) + + res = [] + + # Nothing looks at this as of Linux 2.6.38-rc3 + os.environ["KERNELVERSION"] = "2" + + for archdir in os.listdir("arch"): + if archdir == "h8300": + # Broken Kconfig as of Linux 2.6.38-rc3 + continue + + if os.path.exists(os.path.join("arch", archdir, "Kconfig")): + if archdir == "x86": + add_arch("i386", res) + add_arch("x86_64", res) + elif archdir == "sparc": + add_arch("sparc32", res) + add_arch("sparc64", res) + elif archdir == "sh": + add_arch("sh64", res) + else: + # For most architectures, ARCH = SRCARCH + add_arch(archdir, res) + + # Don't want subsequent 'make *config' commands in tests to see this + del os.environ["ARCH"] + del os.environ["SRCARCH"] + + return res + +def test_all_no(conf): + """Test if our allnoconfig implementation generates the same .config as 'make allnoconfig', for all architectures""" + + while True: + done = True + + for sym in conf: + # Choices take care of themselves for allnoconf, so we only need to + # worry about non-choice symbols + if not sym.is_choice_item(): + lower_bound = sym.get_lower_bound() + + # If we can assign a lower value to the symbol (where "n", "m" and + # "y" are ordered from lowest to highest), then do so. + # lower_bound() returns None for symbols whose values cannot + # (currently) be changed, as well as for non-bool, non-tristate + # symbols. + if lower_bound is not None and \ + kconfiglib.tri_less(lower_bound, sym.calc_value()): + + sym.set_value(lower_bound) + + # We just changed the value of some symbol. As this may effect + # other symbols, we need to keep looping. + done = False + + if done: + break + + conf.write_config("._config") + + shell("make allnoconfig") + +def test_all_yes(conf): + """Test if our allyesconfig implementation generates the same .config as 'make allyesconfig', for all architectures""" + + # Get a list of all symbols that are not choice items + non_choice_syms = [sym for sym in conf.get_symbols() if + not sym.is_choice_item()] + + while True: + done = True + + # Handle symbols outside of choices + + for sym in non_choice_syms: + upper_bound = sym.get_upper_bound() + + # See corresponding comment for allnoconf implementation + if upper_bound is not None and \ + kconfiglib.tri_less(sym.calc_value(), upper_bound): + sym.set_value(upper_bound) + done = False + + # Handle symbols within choices + + for choice in conf.get_choices(): + + # Handle choices whose visibility allow them to be in "y" mode + + if choice.get_visibility() == "y": + selection = choice.get_selection_from_defaults() + if selection is not None and \ + selection is not choice.get_user_selection(): + selection.set_value("y") + done = False + + # Handle choices whose visibility only allow them to be in "m" mode + + elif choice.get_visibility() == "m": + for sym in choice.get_items(): + if sym.calc_value() != "m" and \ + sym.get_upper_bound() != "n": + sym.set_value("m") + done = False + + + if done: + break + + conf.write_config("._config") + + shell("make allyesconfig") + +def test_call_all(conf): + """Call all public methods on all symbols, menus, choices and comments (nearly all public methods: some are hard to test like this, but are exercised by other tests) for all architectures to make sure we never crash or hang""" + print " For {0}...".format(conf.get_arch()) + + conf.get_arch() + conf.get_defconfig_filename() + conf.get_top_level_items() + conf.eval("y && ARCH") + + # Syntax error + caught_exception = False + try: + conf.eval("y & y") + except kconfiglib.Kconfig_Syntax_Error: + caught_exception = True + + if not caught_exception: + print "Fail: no exception generated for expression with syntax error" + fail() + + conf.get_config_header() + conf.get_base_dir() + conf.reset() + conf.get_symbols(False) + conf.get_mainmenu_text() + + for s in conf.get_symbols(): + s.reset() + s.calc_value() + s.calc_default_value() + s.get_user_value() + s.get_name() + s.get_upper_bound() + s.get_lower_bound() + s.get_assignable_values() + s.get_type() + s.get_visibility() + s.get_parent() + s.get_sibling_symbols() + s.get_sibling_items() + s.get_referenced_symbols() + s.get_referenced_symbols(True) + s.get_selected_symbols() + s.get_help() + s.get_config() + s.get_def_locations() + s.get_ref_locations() + s.is_modifiable() + s.is_defined() + s.is_special() + s.is_from_environment() + s.has_ranges() + s.is_choice_item() + s.is_choice_selection() + s.__str__() + + for c in conf.get_choices(): + if c.get_name() is not None: + print "LOOOOOOOOL", c.get_name() + c.get_selection() + c.get_selection_from_defaults() + c.get_user_selection() + c.get_type() + c.get_name() + c.get_items() + c.get_actual_items() + c.get_parent() + c.get_referenced_symbols() + c.get_referenced_symbols(True) + c.get_def_locations() + c.get_visibility() + c.calc_mode() + c.is_optional() + c.__str__() + + for m in conf.get_menus(): + m.get_items() + m.get_symbols(False) + m.get_symbols(True) + m.get_depends_on_visibility() + m.get_visible_if_visibility() + m.get_title() + m.get_parent() + m.get_referenced_symbols() + m.get_referenced_symbols(True) + m.get_location() + m.__str__() + + for c in conf.get_comments(): + c.get_text() + c.get_parent() + c.get_referenced_symbols() + c.get_referenced_symbols(True) + c.get_location() + c.__str__() + +def test_config_absent(conf): + """Test if kconfiglib generates the same configuration as 'conf' without a .config, for each architecture""" + conf.write_config("._config") + + # Use an empty .config + shell("> .config") + shell("make kconfiglibtestconfig") + +def test_defconfig(conf): + """Test if kconfiglib generates the same .config as conf for each architecture/defconfig pair (this takes two hours on a Core i7@2.67 GHz system)""" + # Collect defconfigs. This could be done once instead, but it's a speedy + # operation comparatively. + + global nconfigs + + defconfigs = [] + + for arch in os.listdir("arch"): + arch_dir = os.path.join("arch", arch) + + # Some arches have a "defconfig" in their + # arch directory. + defconfig = os.path.join(arch_dir, "defconfig") + if os.path.exists(defconfig): + defconfigs.append(defconfig) + + defconfigs_dir = os.path.join(arch_dir, "configs") + if os.path.isdir(defconfigs_dir): + for c in os.listdir(defconfigs_dir): + defconfig = os.path.join(defconfigs_dir, c) + if os.path.isfile(defconfig): + defconfigs.append(defconfig) + + # Test architecture for each defconfig + + for defconfig in defconfigs: + rm_configs() + + nconfigs += 1 + + conf.load_config(defconfig) + conf.write_config("._config") + shell("cp {0} .config".format(defconfig)) + shell("make kconfiglibtestconfig") + + sys.stdout.write(" {0:<14}with {1:<60} ".format(conf.get_arch(), defconfig)) + + if equal_confs(): + print "OK" + else: + print "FAIL" + fail() + +# +# Helper functions +# + +def shell(cmd): + subprocess.Popen(cmd, + shell = True, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE).wait() + +def rm_configs(): + """Delete any old ".config" (generated by the C implementation) and + "._config" (generated by us), if present.""" + def rm_if_exists(f): + if os.path.exists(f): + os.remove(f) + + rm_if_exists(".config") + rm_if_exists("._config") + +def equal_confs(): + with open(".config") as menu_conf: + l1 = menu_conf.readlines() + + with open("._config") as my_conf: + l2 = my_conf.readlines() + + # Skip the header generated by 'conf' + unset_re = r"# CONFIG_(\w+) is not set" + i = 0 + for line in l1: + if not line.startswith("#") or \ + re.match(unset_re, line): + break + i += 1 + + return (l1[i:] == l2) + +_all_ok = True + +def fail(): + global _all_ok + _all_ok = False + +def all_ok(): + return _all_ok + +if __name__ == "__main__": + run_tests() |
