Thanks for the review. I'll add tests before submitting a new revision.

Tom

On Thu, Jan 8, 2026 at 12:30 PM Alexander Kanavin <[email protected]>
wrote:

> This looks alright, but it really does need tests, in
> meta/lib/oeqa/selftest/cases/devtool.py
>
> Alex
>
> On Thu, 8 Jan 2026 at 00:12, Tom Geelen <[email protected]> wrote:
> >
> > Based of a feature of AUH this is a plugin to run a testimage directly
> with one packages installed that you are working on via devtool.
> >
> > Inputs would be a:
> > - target image
> > - target packages
> >
> > The tool will take care to make sure it also installs the minimal
> necessary dependencies to be able to run ptest on the target image.
> > Logs will be captured and stored in the devtool workspace for easy
> access.
> >
> > Minimal example to test:
> > - modify a package via devtool
> > - call this new method with said package and for instance
> core-image-minimal
> >
> > Signed-off-by: Tom Geelen <[email protected]>
> > ---
> >  scripts/lib/devtool/testimage.py | 122 +++++++++++++++++++++++++++++++
> >  1 file changed, 122 insertions(+)
> >  create mode 100644 scripts/lib/devtool/testimage.py
> >
> > diff --git a/scripts/lib/devtool/testimage.py
> b/scripts/lib/devtool/testimage.py
> > new file mode 100644
> > index 0000000000..809bd4bfb2
> > --- /dev/null
> > +++ b/scripts/lib/devtool/testimage.py
> > @@ -0,0 +1,122 @@
> > +# Development tool - test-image plugin
> > +#
> > +# Copyright (C) 2026 Authors
> > +#
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +
> > +"""Devtool plugin containing the test-image subcommand.
> > +
> > +Builds a target image, installs specified package(s) from the workspace
> or
> > +layer, and runs the image's test suite via the BitBake `testimage` task.
> > +"""
> > +
> > +import os
> > +import logging
> > +
> > +from devtool import setup_tinfoil, parse_recipe, DevtoolError
> > +from devtool.build_image import build_image_task
> > +
> > +logger = logging.getLogger('devtool')
> > +
> > +
> > +def _collect_install_packages(tinfoil, config, package_names):
> > +    """Return list of packages to install, including -ptest when
> available.
> > +
> > +    package_names: list of PN values (typically recipe names).
> > +    """
> > +    install = []
> > +    for pn in package_names:
> > +        rd = parse_recipe(config, tinfoil, pn, True)
> > +        if not rd:
> > +            # parse_recipe already logs errors
> > +            raise DevtoolError(f'Unable to find or parse recipe for
> package {pn}')
> > +
> > +        install.append(pn)
> > +        packages_var = rd.getVar('PACKAGES') or ''
> > +        packages = packages_var.split()
> > +        ptest_pkg = f'{pn}-ptest'
> > +        if ptest_pkg in packages:
> > +            install.append(ptest_pkg)
> > +            logger.info('Including ptest package %s', ptest_pkg)
> > +        else:
> > +            logger.debug('No ptest package found for %s', pn)
> > +    return install
> > +
> > +
> > +def test_image(args, config, basepath, workspace):
> > +    """Entry point for the devtool 'test-image' subcommand."""
> > +
> > +    if not args.imagename:
> > +        raise DevtoolError('Image recipe to test must be specified')
> > +    if not args.package:
> > +        raise DevtoolError('Package(s) to install must be specified via
> -p/--package')
> > +
> > +    package_names = [p.strip() for p in args.package.split(',') if
> p.strip()]
> > +    if not package_names:
> > +        raise DevtoolError('No valid package name(s) provided')
> > +
> > +    # Prepare a bbappend with IMAGE_INSTALL and testimage variables
> > +    tinfoil = setup_tinfoil(basepath=basepath)
> > +    try:
> > +        install_pkgs = _collect_install_packages(tinfoil, config,
> package_names)
> > +    finally:
> > +        tinfoil.shutdown()
> > +
> > +    logdir = os.path.join(config.workspace_path, 'testimage-logs')
> > +    try:
> > +        os.makedirs(logdir, exist_ok=True)
> > +    except Exception as exc:
> > +        raise DevtoolError(f'Failed to create test logs directory
> {logdir}: {exc}')
> > +
> > +    pkg_append = ' '.join(sorted(set(install_pkgs)))
> > +    extra_append = [
> > +        f'TEST_LOG_DIR = "{logdir}"',
> > +        # Ensure changes to these vars retrigger testimage and are
> visible
> > +        'TESTIMAGE_UPDATE_VARS:append = " TEST_LOG_DIR IMAGE_CLASSES
> TEST_SUITES DISTRO_FEATURES"',
> > +        # Ensure runtime test framework is enabled even if image/distro
> omitted it
> > +        'IMAGE_CLASSES:append = " testimage"',
> > +        'TEST_SUITES = "ping ssh ptest"',
> > +        'DISTRO_FEATURES:append = " ptest"',
> > +        'TEST_RUNQEMUPARAMS = "slirp"',
> > +        # Ensure requested packages (and -ptest where available) are
> installed
> > +        f'IMAGE_INSTALL:append = " {pkg_append}"',
> > +    ]
> > +
> > +    logger.info('Building and testing image %s with packages: %s',
> > +                args.imagename, ' '.join(install_pkgs))
> > +
> > +    # Reuse build_image_task to run -c testimage with our bbappend
> > +    result, _outputdir = build_image_task(
> > +        config,
> > +        basepath,
> > +        workspace,
> > +        args.imagename,
> > +        add_packages=install_pkgs,
> > +        task='testimage',
> > +        extra_append=extra_append,
> > +    )
> > +
> > +    if result == 0:
> > +        logger.info('Testimage completed. Logs are in %s', logdir)
> > +    return result
> > +
> > +
> > +def register_commands(subparsers, context):
> > +    """Register devtool subcommands from the test-image plugin"""
> > +    parser = subparsers.add_parser(
> > +        'test-image',
> > +        help='Build image, install package(s), and run testimage',
> > +        description=(
> > +            'Builds an image, installs specified package(s), and runs
> the\n'
> > +            'BitBake testimage task to validate on-target
> functionality.'
> > +        ),
> > +        group='testbuild',
> > +        order=-9,
> > +    )
> > +    parser.add_argument('imagename', help='Image recipe to test')
> > +    parser.add_argument(
> > +        '-p', '--package', '--packages',
> > +        help='Package(s) to install into the image (comma-separated)',
> > +        metavar='PACKAGES',
> > +    )
> > +    parser.set_defaults(func=test_image)
> > --
> > 2.43.0
> >
>
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#229081): 
https://lists.openembedded.org/g/openembedded-core/message/229081
Mute This Topic: https://lists.openembedded.org/mt/117144059/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to