Twister builds every test in its own directory, and ccache keys on that
directory: it appears in the compiler command line, and in the hash that
keeps debug info apart because Zephyr always builds with -g. An object
compiled for one test is therefore never reused by another.
Set base_dir and hash_dir for the builds twister spawns. Building 121
qemu_x86 configurations from an empty cache, cache hits go from 1.5% to
70% and wall time from 727 s to 488 s. The gain grows with the number of
tests built, as more of them find an object a sibling already compiled.
Anything already set in the environment is left alone. A reused object
records the build directory it was first compiled in, so its debug info
points there; Zephyr passes absolute source paths, so in practice only
files generated into the build directory are affected.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Expect that string provided in platform argument is part of the
target platform instead of an exact name match. This relaxed setting
allows to handle cases like:
- unify argument for multiple boards with the same SoC
- handle multiple versions of the same board
It also matches handling in the west build command implementation.
Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
"west spdx" reads CMake's file-based API object model -- the codemodel
and toolchains replies -- to work out what went into a build. A file API
query has to exist in the build directory before CMake starts, so it
could not be created from the build system itself. Hence "west spdx
--init": a separate pass, run once per build directory before
configuring, easy to forget and recoverable only by reconfiguring.
CMake 3.27 added cmake_file_api(), which registers a query for the
current invocation and has the reply written at generation time. Zephyr
required CMake 3.20 until the floor was raised to 3.28, so the build can
now ask for its own object model and the pre-configure step goes away.
Tie it to CONFIG_BUILD_OUTPUT_META, which "west spdx" already requires:
it reads the zephyr.meta that option emits and bails without it. Builds
that are not producing an SBOM pay nothing.
This also covers sysbuild, where the query had to be created in each
domain's build directory -- one that does not exist until sysbuild has
configured it. Each domain is an ordinary Zephyr build, so each now
requests its own reply.
Cost, measured on hello_world and an LVGL sample for qemu_x86 with the
same tree and a warm ccache, varying only whether the query is present:
no measurable configure time (-0.02 s and +0.04 s median of 6 runs, both
inside run-to-run spread; CMake's generate phase does not move) and no
change to no-op rebuilds. The reply costs 1.1-1.4 MB of build-directory
disk. Without CONFIG_BUILD_OUTPUT_META no reply is written at all.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
On long runs the scrolling console output makes it hard to tell what
twister is actually doing: what is queued, which pipeline stage every
job is in, and which tests already failed and why. Add a
--console-monitor option that replaces the normal output with a live,
top-style full-screen dashboard for the duration of the run.
The monitoring core (twisterlib/runmonitor.py) is transport-free: the
worker processes emit one plain-dict event per pipeline op
start/completion over a multiprocessing.Queue (put_nowait, dropped on
any error, so monitoring can never stall or fail the pipeline), and a
drain thread in the main process folds them into a thread-safe state
model with one row per test instance, initialized from the test plan
so statically filtered instances are visible too. The curses UI
(twisterlib/consolemonitor.py) runs in a thread of the main process
and reads that state in-process; other frontends (e.g. a web view)
can be layered on the same core later.
The dashboard shows overall progress with counters and an ETA, the
in-flight jobs with their current pipeline stage, and a scrollable
instance table with filter tabs. f jumps to the failures view, /
starts an incremental search over instance names and failure reasons,
and arrows or j/k plus Enter open a per-instance detail view: the
pipeline stage timeline, the failing test cases with their reasons,
and scrollable log tails (l switches between the available logs).
While the UI owns the terminal, console log handlers are detached
(everything still lands in twister.log) and restored when it exits;
the workers' single-line progress ticker is skipped; the handlers'
`stty sane` reset after local runs is suppressed and simulator
processes get /dev/null as stdin so nothing can knock the terminal
out of curses mode - the UI also re-asserts its terminal modes on
every poll tick as a backstop. These worker-side accommodations are
keyed to a shared UI-active flag (a module-level multiprocessing
Value inherited by the forked workers - deliberately not an options
attribute, since options travels inside pickled TestInstance objects
through the manager queues where a Value cannot go), so they all
revert the moment the UI exits.
Leaving the dashboard always hands the terminal back cleanly: q
mid-run resumes the normal console output (including the progress
ticker) while the run continues; when the run finishes the dashboard
stays up for inspecting failures and q then writes the reports and
exits as usual; Ctrl-C aborts the run, shuts the dashboard down
without waiting for a keypress, and skips any retry iterations. The
option requires an interactive terminal and curses, and is ignored
with a warning otherwise (e.g. in CI).
Assisted-by: Claude:claude-fable-5
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
find_owning_component() returned the application component as soon as a
source file lived under the application source directory, before zephyr
and module components were considered. A module nested under the
application (or under the zephyr tree) was therefore mis-attributed to
the app package instead of its own module package.
Fold the app component into the same deepest-base_dir selection already
used for the zephyr and module components, so each file is assigned to
its most specific (true) owner regardless of check order.
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Claude:sonnet-5
get_expression_data() reimplemented, with a cruder regex, what REUSE's
own reuse_info_of() already does: parse the in-file
SPDX-License-Identifier tag and apply precedence against REUSE.toml.
Drop get_expression_data(), parse_line_for_expression(), and the now
unused num_lines_scanned option; rely solely on get_reuse_info().
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Claude:sonnet-5
get_copyright_info() built its REUSE Project with the bare Project()
constructor, which never loads REUSE.toml - only
Project.from_directory() does, so bulk licensing data was never read.
Build one Project.from_directory(component.base_dir) per component.
Rename get_copyright_info() to get_reuse_info(), also returning
license expressions used in scan_sbom_graph() when a file has no
in-file SPDX-License-Identifier tag, and quiet the reuse library's
logger to avoid noise from newly-surfaced compliance warnings.
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Claude:sonnet-5
Let users provide their own globs.
When they do, let's just use the provided glob as is.
Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
When a directory is provided, find scripts in directories and
sub-directories.
This is what users expect.
Note that now we also name the tests not just based on the "basename" of
the file, but on the relative path to the tests.yaml so we ensure we have
unique tests names.
We also sort the globing output.
Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
We may also not find any script at all (say the folder is empty,
or there is no tests_scripts folder).
Let's handle it gracefully too, by printing an appropriate
message instead of pointing at the build log.
This fixes more cases than what was covered by
60643b7b05
Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
Harness.process_test() failed a run on a kernel fatal error only when
two conditions held: the console line had to be exactly equal to
"ZEPHYR FATAL ERROR", and the fault had to be seen before the test
printed "PROJECT EXECUTION SUCCESSFUL". Neither holds in practice.
z_fatal_error() prints ">>> ZEPHYR FATAL ERROR %d: %s on CPU %d",
normally through LOG_ERR and therefore behind a timestamp and a module
prefix, so no console line ever compares equal to the marker. The
Console harness has always matched it as a substring and worked; the
Test and Ztest harnesses rely on process_test() alone, so for them
fault detection never triggered at all. The existing unit test passed
the bare marker as the whole line, which is why this went unnoticed.
Match the marker as a substring, move the check ahead of the RUN_PASSED
branch, and withdraw a PASS that was recorded earlier in the output.
Tests that fault on purpose are unaffected: they opt out with
ignore_faults, which clears fail_on_fault. Drop the now duplicated
check in Console.handle(), which process_test() covers.
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
SPDX 3.1 introduced a new field for the size of software artifacts.
Capture file sizes in the SBOM model at scan time and emit them when
serializing SPDX 3.1 output.
Assisted-by: Claude:fable-5
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Spec is still WIP but adding support for it will be useful to work on
adding more features to the SBOM, e.g. requirements or tests.
Assisted-by: Claude:fable-5
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
The QEMU monitor thread's _thread_update_instance_info() stamped the
instance reason with "Unknown Error" whenever the harness provided no
reason, regardless of the resulting status. Passing harnesses supply
no reason, so every passing QEMU test instance silently carried
reason="Unknown Error". The classic console output only prints the
reason for failures, which kept this invisible, but trying to view
status using other means reveals this issue.
Only fall back to "Unknown Error" when the status is actually a
failure (failed/error) and no more specific reason is known. For
non-failure results clear the reason instead of keeping it, so a
stale reason from an earlier retry iteration cannot stick to a
now-passing instance.
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
BinaryHandler captures the test binary's stderr to its own file,
handler_stderr.log, which no report ever reads. Whatever the
binary wrote there is therefore absent from twister.json and from
everything derived from it.
This is most visible with UndefinedBehaviorSanitizer on
native_sim. It writes its diagnostics to stderr rather than
stdout, so a run it aborts is recorded as a bare "rc=1": the
report says the test failed and nothing at all about why, even
though the reason was captured and sits on disk next to the
build. The same applies to any crash or loader failure that
reports on stderr.
Append the captured stderr to the log already selected for a
failing instance, rather than replacing it, so the stdout
transcript that shows how far the test got is preserved and the
stderr diagnostic explaining the failure follows it. Only failing
instances are affected, and only when stderr is non-empty, so
passing runs and reports for handlers that do not capture stderr
are unchanged.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
A module appears twice in the SBOM: as a "-sources" package (its
checked-out source tree, in the zephyr document) and as a "-deps"
package (its upstream identity for vulnerability tracking, in the
modules-deps document). Nothing connected the two, so a consumer could
not tell they describe the same module.
Add a VARIANT_OF relationship from each source package to its
dependency package (rendered as hasVariant in SPDX 3.0), so the checked-
out sources are tied to the upstream dependency they are a variant of.
The same link is added between zephyr-sources and zephyr-deps.
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Cursor:opus-4.8
ExternalDocumentRef was emitted after Created, past the Document
Creation Information section. Strict tag-value parsers reject that
("unknown tag ExternalDocumentRef in CreationInfo section") and fail to
load the document. Emit it right after DocumentNamespace and before
Creator, where the specification places it.
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Cursor:opus-4.8
The role of a module's "-sources" package versus its "-deps" package is
not self-evident, and a "-deps" package is emitted for every module in
the manifest even when its code is not built, which looks surprising.
Attach a package comment to each explaining what it represents and why
it is present.
This also replaces the "Utility target; no files" comment that was
applied to every fileless package: it was left over from the CMake
utility targets (now excluded) and mislabelled the source and
dependency packages.
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Cursor:opus-4.8
CMake UTILITY targets (menuconfig, ram_report, run/flash/debug, code
generation helpers, ...) are build-system conveniences, not software
components. They produced empty, purposeless packages that only added
noise to the build SBOM, so skip them while walking the codemodel.
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Cursor:opus-4.8
Components lacking a supplier or a package URL are flagged by SBOM
quality tooling and cannot be matched against vulnerability databases.
Populate both for Zephyr and its modules from the metadata already in
zephyr.meta: the git remote yields a supplier organization and a
revision-pinned purl. Modules recorded under "url" instead of "remote"
are now handled too, and curated purls from a module's security
metadata are left untouched.
In SPDX 3.0 the supplier is an Agent reference rather than a string, so
each package's suppliedBy points at a shared, deduplicated Organization
element.
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Cursor:opus-4.8
An SBOM should declare who authored it and which tool version produced
it; both were missing, so provenance/quality checks scored zero. Add
"The Zephyr Project" as the creating organization and stamp the
generator with the Zephyr version read from the VERSION file.
The values are recorded on the SBOM graph metadata by the walker and
emitted by both serializers: as SPDX 2.x Creator lines, and in SPDX 3.0
as an Organization in CreationInfo.createdBy plus a packageUrl on the
tool (which has no dedicated version field).
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Cursor:opus-4.8
Depending on conditions, PermissionError may also be raised if there
is no executable to run.
So let's also handle it in the same way.
This expands on the fix from a18fd195bf
Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
Several small copy-paste items in testplan.py:
- The `set(p.platform.name for p in self.instances.values())` expression
used to refresh selected_platforms appeared four times (three in load()
and once in apply_filters()); extract _refresh_selected_platforms().
- The five-argument instance.create_overlay(...) call with the same asan/
ubsan/coverage options appeared in load_from_file() and apply_filters();
extract _create_overlay(instance). The loop variable `platform` at the
first site equals instance.platform (the instance is built for it), so
reading instance.platform is equivalent.
- The three YAML schema paths all rebuilt the ZEPHYR_BASE/scripts/schemas/
twister prefix; hoist it to a TWISTER_SCHEMA_DIR constant.
- The "Not runnable on device" reason was set in one method and string-
compared in another 400 lines away; promote it to the module constant
NOT_RUNNABLE_ON_DEVICE_REASON so the two cannot drift.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
zephyr_module.parse_modules(ZEPHYR_BASE) walks the whole workspace for
module manifests, and twister ran it twice per invocation: once in
add_parse_arguments() to build the --board-root default and again in
TwisterEnv.__init__() to collect the snippet/soc/dts/arch roots. Wrap it
in a functools.cache helper (_parse_modules) so the scan happens once; the
result is read-only at both sites, so sharing it is safe.
While there, replace the five near-identical "settings.get('<x>_root')
-> append to self.<x>_roots" blocks in TwisterEnv.__init__ with a single
loop over a {setting_key: roots_list} mapping. project / snippet_root and
project / Path(soc_root) are equivalent, so wrapping every value in Path()
is a no-op.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
process() was a 287-line if/elif chain over nine pipeline ops, and every
branch repeated the same ~5-line "except StatusAttributeError" handler
(force ERROR, set the 'Incorrect status assignment' reason, block the
remaining cases) followed by a finally that enqueues the next op.
Split each op into its own _op_<name> method and drive them from a
_PIPELINE_OPS name->method table, so process() is now a short dispatcher.
The repeated exception body becomes a single _mark_status_error() helper
(the coverage op keeps its "on <op>" reason variant via op_label). Each
handler keeps its own control flow, including the irregular cases: 'run'
still re-queues the message on NoDeviceAvailable/RequiredAppNotReady,
'report' still clears next_op on error, and 'cleanup' still neither
enqueues nor reports.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
XUnitXMLReport.create() and create_with_all_testsuites() shared ~70% of
their bodies: loading the JSON, building the <testsuite> element with its
eight zeroed attributes and version property, the full-report testcase
loop, the counter-finalisation block, and the indent/tostring/write tail.
Factor these into _read_json(), _new_testsuite(), _finalize_testsuite(),
_emit_full_report_testcases() and _write_report(), and rewrite both public
methods in terms of them. create() keeps its per-platform grouping and its
full_report vs per-suite-summary branch; create_with_all_testsuites() keeps
its per-suite grouping and extra platform/architecture properties.
No behaviour change: rendering both methods over a synthetic report and
comparing canonicalised XML (ET.canonicalize) is byte-identical to the
previous code across full/summary/target/all-testsuites variants and both
detailed_skipped_report settings. The only difference is that the
all-testsuites <testsuite> now emits its time/timestamp attributes in the
same order as create() - JUnit attribute order is not significant and no
test pins it.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
QEMUHandler (POSIX) and QEMUWinHandler (Windows) duplicated _get_cpu_time,
_create_command, get_fifo, the pid-file half of _set_qemu_filenames, and a
byte-identical instance-info updater that had drifted into two names
(_thread_update_instance_info vs _monitor_update_instance_info). The
post-status timeout-extension block, with its bare 30/2 second literals,
was copied into both monitor loops.
Introduce a QEMUHandlerBase(Handler) holding these shared pieces and have
both handlers inherit it. The magic timeout values become the
COVERAGE_TIMEOUT_EXTENSION / POST_STATUS_TIMEOUT_EXTENSION constants,
consumed through a single _extend_timeout_on_status() helper. The Windows
handler drops its now-identical _set_qemu_filenames entirely; the POSIX
one keeps a two-line override that also sets the fifo path.
The two monitor loops themselves are deliberately left in the subclasses:
their wait/read mechanisms differ fundamentally (select.poll on POSIX
fifos vs a reader-thread Queue on Windows) and the Windows path is not
exercised by Linux CI, so merging them would risk an untestable
regression. The public method names the tests patch (_get_cpu_time,
_thread_update_instance_info, _thread_get_fifo_names, _set_qemu_filenames)
are preserved, so behaviour is unchanged.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
Pytest and Ctest each duplicated an almost identical run() skeleton and a
byte-identical _update_test_status(); the two differed only in the tool
name used in reason strings, the exception type run() catches, and
whether pytest's PYTHONPATH env dependencies are injected.
Introduce a ReportHarness(Script) base that owns run() and
_update_test_status(). Subclasses now declare a report_tool label and a
run_exception type, and override the _run_report_command() hook when they
need to alter how the tool is invoked (Pytest injects env dependencies).
The triple of ExitCodes meaning "the tool errored" becomes the module
constant REPORT_ERROR_EXIT_CODES. Script keeps its own script-oriented
run()/_update_test_status(); Bsim is unaffected.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
With no tests found when pointing to a directory with no tests, which is
not wrong, we were exiting with 1, when twister was crashing because of
bad syntax or when not able to load a python module, same thing.
Now fail early when there are schema errors or other issues, and fail
with a warning when no tests are found, which should be fine given that
a warning is emitted, but not an error.
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
The default for --ninja was computed from sys.argv:
default=not any(a in sys.argv for a in ("-k", "--make"))
sys.argv is the process argument list, which only coincides with the
argument list twister acts on when twister runs as a CLI. Calling
twister_main(args) in-process, as the blackbox tests do, therefore
selects the generator based on whatever launched the interpreter:
- twister_main(['--make', ...]) still builds with Ninja, silently
ignoring --make.
- Running the blackbox suite as 'pytest -k <expr>' puts '-k' into
sys.argv, so every build switches to Unix Makefiles.
Drop the sys.argv sniffing and derive options.ninja from options.make
after parsing, so the generator follows the arguments twister was
actually given.
Assisted-by: Claude:opus-4.8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
Add process-level caches to TwisterEnv.check_zephyr_version() and
get_toolchain() so repeated twister_main() calls within the same
process skip slow git/cmake subprocess re-runs, and add a module-level
cache to generate_platforms() keyed on the board/soc/arch root tuples.
This significantly speeds up in-process invocations such as the
blackbox test suite.
Assisted-by: Claude:opus-4.8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This change allows the following command to be run more than once
without crashing:
> twister --outdir directory-with-trailing-slash/
Without this fix, the 2nd run would abort with an error like this:
> shutil.Error: Cannot move a directory 'directory-with-trailing-slash/'
> into itself 'directory-with-trailing-slash/.1'.
Signed-off-by: Reto Schneider <code@reto-schneider.ch>
When the Robot Framework executable is not found, subprocess
raised an unhandled FileNotFoundError that aborted the entire
Twister run instead of just the affected test.
Catch the exception in the Robot harness, mark the instance as
skipped with a descriptive reason, and continue so remaining
tests still execute.
Signed-off-by: Grzegorz Chwierut <grzegorz.chwierut@nordicsemi.no>
Now that we are building with multiple toolchains some failures will only
happen with one toolchain.
So, also print the toolchain used, so it is easier to identify the issue.
And update the twister blackbox tests reference data.
Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
Before this patch, when a script is not found, `twister_harness.log` would
not be created.
This would result in the error reporting in runner.py to refer users to
the build.log; and if `--inline-logs` was used, it would inline the
build.log in the console output which was just a lot of confusing noise
for users.
Instead let's create twister_harness.log logging a "not found" error.
So we both refer users to it in the console output, and if we inline
anything we inline that.
Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
Twister picks exactly one toolchain per platform, so a board that is
buildable with several toolchains only ever gets coverage for one of
them. The only way to build a test more than once was the test scenario
level integration_toolchains option, which cannot express "build
everything on this platform with both GCC and Clang".
Add a build_toolchains option listing the toolchains every test assigned
to a platform should be built with. Twister then creates one test
instance per toolchain, each in its own build directory. The option can
be set in the board configuration, or per platform in the twister
configuration file, which lets multi-toolchain coverage be enabled for
CI only while local runs keep building each test once. An empty list in
the twister configuration disables it for a platform that requests it.
The toolchain resolution chain moves into get_toolchains(), keeping the
existing precedence, with build_toolchains taking effect just below a
scenario's integration_toolchains.
Toolchain is also added to the platform_key deduplication key. That key
deduplicates platforms, not toolchains, and without this every extra
toolchain of a scenario using platform_key is discarded as already
covered.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
For `script` type harnesses, when `--inline_logs` is used,
the output of all the scripts which are part of the test are printed
to console first (both passed and failed ones).
And then again the failed ones.
This is quite undesirable, as it fills the console with unnecessary
logs, which makes it more difficult and slow to find the problem.
Instead, for the script harness, let's print only the failed ones.
Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
The pytest harness now is a child of the script harness.
And all script harnesses use twister_harness.log for their
output.
Let's correct the variable name to not confuse readers.
Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
Add the first concrete sidecar. It shares a host directory with the
guest over virtiofs: tests run on QEMU with a vhost-user-fs-pci device,
which needs a virtiofsd daemon on a per-build-dir UNIX socket that QEMU
connects to before it boots. The sidecar starts virtiofsd in setup()
(seeding a private writable copy of the shared directory) and stops it
in teardown(), skipping the run cleanly if virtiofsd is not installed.
The runner injects the dynamic -chardev socket path via
QEMU_EXTRA_FLAGS.
Configured through the virtiofs_shared / virtiofsd_bin /
virtiofs_extra_args harness_config keys. Adds unit tests.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
Some QEMU tests need a host-side process running for the guest to talk to
(a virtiofsd daemon, an ivshmem reader, a network tap). Model this as a
first-class "sidecar": a testsuite field orthogonal to the harness, so a
test keeps its normal harness (ztest, console, ...) and just declares a
sidecar, and twister may also attach one itself.
This commit adds only the plumbing and the base concept:
- A Sidecar base class with configure()/setup()/teardown() and a
SidecarImporter registry (empty here; concrete sidecars register in
later commits), in twisterlib/sidecar.py.
- The `sidecar:` testsuite field (config_parser, TestSuite) and the
per-instance sidecar/dtc_overlay attributes (TestInstance).
- The runner wraps handler.handle() with the sidecar's setup/teardown and
skips the run cleanly if setup() reports the host side is unavailable.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
Add translation of DEPENDENCY_OF, CONTAINED_BY, DESCRIBED_BY,
and PREREQUISITE_FOR to SDPX3 relationships. Fix HAS_PREREQUISITE
to be mapped as hasPrerequisite relationship.
Signed-off-by: David Nepožitek <david@nepozitek.cz>
When no per-instance tracefiles exist, the merge stage built an lcov
command with just --output-file and no action option, which lcov rejects
with an opaque "capture report stage failed with 255".
Report the missing tracefiles instead.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This reverts commit 90555a1fe7.
This causing issues with CI due to missing colorlog module when PRs are
based on old state of main.
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
The 'Incorrect status assignment' reason was repeated across nine
pipeline stage handlers and 'runtime filter' twice; promote both to
module constants. report_out() also rebuilt four near-identical coloured
count sections inline; extract a _colored_count() helper. No behaviour
change; the colour output is verified byte-identical to the previous
formula.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
ExecutionCounter declared 22 shared counters, each with an identical
property getter, setter and _increment() method - roughly 300 lines of
copy-pasted lock handling. The duplication also hid a bug: the
notrun_cases setter locked and wrote the unrelated _notrun Value instead
of _notrun_cases, so assigning notrun_cases corrupted notrun and left
notrun_cases unchanged.
Introduce an AtomicCounter data descriptor that owns the locked get/set
and increment logic. Each counter is now a single class attribute, and
the <name>_increment(value=1) methods are synthesised on demand via
__getattr__, preserving the existing public API. This removes ~265 lines
and eliminates the setter bug class entirely.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
TestCase, TestSuite and TestInstance each defined a byte-identical
status property/setter validating assignments against TwisterStatus and
raising StatusAttributeError. Extract the shared logic into a StatusMixin
in statuses.py and have the three classes inherit it, dropping the now
unused Enum and StatusAttributeError imports.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
Use colorlog's `ColoredFormatter` for Twister's console formatter in
both timestamped and non-timestamped modes to improve readability
without changing file log formatting.
Signed-off-by: Lukasz Fundakowski <lukasz.fundakowski@nordicsemi.no>
Twister resolves filesystem paths from test YAML through
os.path.expandvars(), which only substitutes process environment
variables. The most useful pointer to a module's root,
ZEPHYR_<MODULE>_MODULE_DIR, is a CMake build-system variable and is
not available at test-discovery time.
Add a helper that expands ZEPHYR_<MODULE>_MODULE_DIR and
ZEPHYR_<MODULE>_MODULE_NAME, mirroring the CMake variables defined for
every module, with the name sanitized and upper-cased exactly as the
build system does. Apply it when resolving required_applications and
required_devices paths and pytest_root, so the same spelling works in
both CMake and Twister contexts.
Signed-off-by: Grzegorz Chwierut <grzegorz.chwierut@nordicsemi.no>
Each SPDX 3.0 document re-serialized every element it referenced, so
packages and files defined in one document were duplicated in full into
every other document that mentioned them.
Declare such cross-document references with an ExternalMap import
instead of copying their definitions and effectively do something
similar to what the SPDX 2 serializer did.
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Assisted-by: Claude:opus-4.8
Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>