Re: [PATCH v2] kunit: tool: reconfigure when the used kunitconfig changes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, Nov 20, 2021 at 7:23 AM Daniel Latypov <dlatypov@xxxxxxxxxx> wrote:
>
> Problem: currently, if you remove something from your kunitconfig,
> kunit.py will not regenerate the .config file.
> The same thing happens if you did --kunitconfig_add=CONFIG_KASAN=y [1]
> and then ran again without it. Your new run will still have KASAN.
>
> The reason is that kunit.py won't regenerate the .config file if it's a
> superset of the kunitconfig. This speeds it up a bit for iterating.
>
> This patch adds an additional check that forces kunit.py to regenerate
> the .config file if the current kunitconfig doesn't match the previous
> one.
>
> What this means:
> * deleting entries from .kunitconfig works as one would expect
> * dropping  a --kunitconfig_add also triggers a rebuild
> * you can still edit .config directly to turn on new options
>
> We implement this by creating a `last_used_kunitconfig` file in the
> build directory (so .kunit, by default) after we generate the .config.
> When comparing the kconfigs, we compare python sets, so duplicates and
> permutations don't trip us up.
>
> The majority of this patch is adding unit tests for the existing logic
> and for the new case where `last_used_kunitconfig` differs.
>
> [1] https://lore.kernel.org/linux-kselftest/20211106013058.2621799-2-dlatypov@xxxxxxxxxx/
>
> Signed-off-by: Daniel Latypov <dlatypov@xxxxxxxxxx>
> ---

Thanks! No problems with this version, looks good to go!

Reviewed-by: David Gow <davidgow@xxxxxxxxxx>

Cheers,
-- David

> Note: this patch is based on
> https://lore.kernel.org/linux-kselftest/20211106013058.2621799-1-dlatypov@xxxxxxxxxx/
> This patch will work without it, but there'll be a false merge conflict.
>
> v1 -> v2:
> * always regenerate if last_used_kunitconfig doesn't exist
> * don't call os.remove() if last_used_kunitconfig doesn't exist
> * add in get_old_kunitconfig_path() to match get_kunitconfig_path()
> * s/_kconfig_changed/_kunitconfig_changed
> ---
>  Documentation/dev-tools/kunit/start.rst |  8 ++---
>  tools/testing/kunit/kunit_kernel.py     | 40 ++++++++++++++++------
>  tools/testing/kunit/kunit_tool_test.py  | 45 +++++++++++++++++++++++++
>  3 files changed, 78 insertions(+), 15 deletions(-)
>
> diff --git a/Documentation/dev-tools/kunit/start.rst b/Documentation/dev-tools/kunit/start.rst
> index 1e00f9226f74..0a5e65540974 100644
> --- a/Documentation/dev-tools/kunit/start.rst
> +++ b/Documentation/dev-tools/kunit/start.rst
> @@ -50,10 +50,10 @@ It'll warn you if you haven't included the dependencies of the options you're
>  using.
>
>  .. note::
> -   Note that removing something from the ``.kunitconfig`` will not trigger a
> -   rebuild of the ``.config`` file: the configuration is only updated if the
> -   ``.kunitconfig`` is not a subset of ``.config``. This means that you can use
> -   other tools (such as make menuconfig) to adjust other config options.
> +   If you change the ``.kunitconfig``, kunit.py will trigger a rebuild of the
> +   ``.config`` file. But you can edit the ``.config`` file directly or with
> +   tools like ``make menuconfig O=.kunit``. As long as its a superset of
> +   ``.kunitconfig``, kunit.py won't overwrite your changes.
>
>
>  Running the tests (KUnit Wrapper)
> diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
> index 350883672be0..12085e04a80c 100644
> --- a/tools/testing/kunit/kunit_kernel.py
> +++ b/tools/testing/kunit/kunit_kernel.py
> @@ -21,6 +21,7 @@ import qemu_config
>
>  KCONFIG_PATH = '.config'
>  KUNITCONFIG_PATH = '.kunitconfig'
> +OLD_KUNITCONFIG_PATH = 'last_used_kunitconfig'
>  DEFAULT_KUNITCONFIG_PATH = 'tools/testing/kunit/configs/default.config'
>  BROKEN_ALLCONFIG_PATH = 'tools/testing/kunit/configs/broken_on_uml.config'
>  OUTFILE_PATH = 'test.log'
> @@ -179,6 +180,9 @@ def get_kconfig_path(build_dir) -> str:
>  def get_kunitconfig_path(build_dir) -> str:
>         return get_file_path(build_dir, KUNITCONFIG_PATH)
>
> +def get_old_kunitconfig_path(build_dir) -> str:
> +       return get_file_path(build_dir, OLD_KUNITCONFIG_PATH)
> +
>  def get_outfile_path(build_dir) -> str:
>         return get_file_path(build_dir, OUTFILE_PATH)
>
> @@ -289,24 +293,38 @@ class LinuxSourceTree(object):
>                 except ConfigError as e:
>                         logging.error(e)
>                         return False
> -               return self.validate_config(build_dir)
> +               if not self.validate_config(build_dir):
> +                       return False
> +
> +               old_path = get_old_kunitconfig_path(build_dir)
> +               if os.path.exists(old_path):
> +                       os.remove(old_path)  # write_to_file appends to the file
> +               self._kconfig.write_to_file(old_path)
> +               return True
> +
> +       def _kunitconfig_changed(self, build_dir: str) -> bool:
> +               old_path = get_old_kunitconfig_path(build_dir)
> +               if not os.path.exists(old_path):
> +                       return True
> +
> +               old_kconfig = kunit_config.parse_file(old_path)
> +               return old_kconfig.entries() != self._kconfig.entries()
>
>         def build_reconfig(self, build_dir, make_options) -> bool:
>                 """Creates a new .config if it is not a subset of the .kunitconfig."""
>                 kconfig_path = get_kconfig_path(build_dir)
> -               if os.path.exists(kconfig_path):
> -                       existing_kconfig = kunit_config.parse_file(kconfig_path)
> -                       self._ops.make_arch_qemuconfig(self._kconfig)
> -                       if not self._kconfig.is_subset_of(existing_kconfig):
> -                               print('Regenerating .config ...')
> -                               os.remove(kconfig_path)
> -                               return self.build_config(build_dir, make_options)
> -                       else:
> -                               return True
> -               else:
> +               if not os.path.exists(kconfig_path):
>                         print('Generating .config ...')
>                         return self.build_config(build_dir, make_options)
>
> +               existing_kconfig = kunit_config.parse_file(kconfig_path)
> +               self._ops.make_arch_qemuconfig(self._kconfig)
> +               if self._kconfig.is_subset_of(existing_kconfig) and not self._kunitconfig_changed(build_dir):
> +                       return True
> +               print('Regenerating .config ...')
> +               os.remove(kconfig_path)
> +               return self.build_config(build_dir, make_options)
> +
>         def build_kernel(self, alltests, jobs, build_dir, make_options) -> bool:
>                 try:
>                         if alltests:
> diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py
> index 7e42a7c27987..572f133511aa 100755
> --- a/tools/testing/kunit/kunit_tool_test.py
> +++ b/tools/testing/kunit/kunit_tool_test.py
> @@ -358,6 +358,51 @@ class LinuxSourceTreeTest(unittest.TestCase):
>                         with open(kunit_kernel.get_outfile_path(build_dir), 'rt') as outfile:
>                                 self.assertEqual(outfile.read(), 'hi\nbye\n', msg='Missing some output')
>
> +       def test_build_reconfig_no_config(self):
> +               with tempfile.TemporaryDirectory('') as build_dir:
> +                       with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f:
> +                               f.write('CONFIG_KUNIT=y')
> +
> +                       tree = kunit_kernel.LinuxSourceTree(build_dir)
> +                       mock_build_config = mock.patch.object(tree, 'build_config').start()
> +
> +                       # Should generate the .config
> +                       self.assertTrue(tree.build_reconfig(build_dir, make_options=[]))
> +                       mock_build_config.assert_called_once_with(build_dir, [])
> +
> +       def test_build_reconfig_existing_config(self):
> +               with tempfile.TemporaryDirectory('') as build_dir:
> +                       # Existing .config is a superset, should not touch it
> +                       with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f:
> +                               f.write('CONFIG_KUNIT=y')
> +                       with open(kunit_kernel.get_old_kunitconfig_path(build_dir), 'w') as f:
> +                               f.write('CONFIG_KUNIT=y')
> +                       with open(kunit_kernel.get_kconfig_path(build_dir), 'w') as f:
> +                               f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')
> +
> +                       tree = kunit_kernel.LinuxSourceTree(build_dir)
> +                       mock_build_config = mock.patch.object(tree, 'build_config').start()
> +
> +                       self.assertTrue(tree.build_reconfig(build_dir, make_options=[]))
> +                       self.assertEqual(mock_build_config.call_count, 0)
> +
> +       def test_build_reconfig_remove_option(self):
> +               with tempfile.TemporaryDirectory('') as build_dir:
> +                       # We removed CONFIG_KUNIT_TEST=y from our .kunitconfig...
> +                       with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f:
> +                               f.write('CONFIG_KUNIT=y')
> +                       with open(kunit_kernel.get_old_kunitconfig_path(build_dir), 'w') as f:
> +                               f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')
> +                       with open(kunit_kernel.get_kconfig_path(build_dir), 'w') as f:
> +                               f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')
> +
> +                       tree = kunit_kernel.LinuxSourceTree(build_dir)
> +                       mock_build_config = mock.patch.object(tree, 'build_config').start()
> +
> +                       # ... so we should trigger a call to build_config()
> +                       self.assertTrue(tree.build_reconfig(build_dir, make_options=[]))
> +                       mock_build_config.assert_called_once_with(build_dir, [])
> +
>         # TODO: add more test cases.
>
>
> --
> 2.34.0.rc2.393.gf8c9666880-goog
>



[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux