Skip to content

test: ztest: port fft and fft_multi CMocka tests to ztest - #11369

Merged
lgirdwood merged 5 commits into
thesofproject:mainfrom
tmleman:topic/upstream/pr/ut/ztest/math/fft
Oct 28, 2026
Merged

lgirdwood merged 5 commits into
thesofproject:mainfrom
tmleman:topic/upstream/pr/ut/ztest/math/fft

Conversation

@tmleman

@tmleman tmleman commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Ports the fft and fft_multi CMocka unit tests to the Zephyr ztest framework, plus the shared infrastructure and the CI change needed to run them. Part of #10110.

The CMocka tests are left in place and keep running unchanged; the ztest suites #include their reference vectors directly from the CMocka tree, so both frameworks exercise identical fixed-point data.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues remain for the FFT and multi-size FFT test migration to Zephyr ztest.

No open issues 🎉

What changed in this PR

Ports the FFT and multi-size FFT tests to Zephyr ztest as part of #10110, while retaining the existing CMocka tests and sharing their reference vectors.

Changes:

  • Adds native-simulator suites covering FFT/IFFT accuracy, signal-to-noise ratios, and peak detection.
  • Adds shared test allocation tracking and minimal SOF context support.
  • Enables address and leak sanitizers in CI and adds object-pool cleanup.
File Description
zephyr/​lib/​alloc.c Guards the heap accessor to avoid conflicting with the unit-test stub.
test/​ztest/​unit/​objpool/​test_objpool_ztest.c Prunes object pools after tests.
test/​ztest/​unit/​math/​fft/​testcase.yaml Registers the FFT suite for native_sim.
test/​ztest/​unit/​math/​fft/​test_fft_ztest.c Ports 16-bit and 32-bit FFT/IFFT tests to ztest.
test/​ztest/​unit/​math/​fft/​prj.conf Configures sample formats and sanitizer-visible heap allocation.
test/​ztest/​unit/​math/​fft/​CMakeLists.txt Links FFT tests, shared vectors, and required SOF sources.
test/​ztest/​unit/​math/​fft_multi/​testcase.yaml Registers the multi-size FFT suite for native_sim.
test/​ztest/​unit/​math/​fft_multi/​test_fft_multi_ztest.c Checks multi-size FFT/IFFT results against shared reference vectors.
test/​ztest/​unit/​math/​fft_multi/​prj.conf Configures sanitizer-visible heap allocation.
test/​ztest/​unit/​math/​fft_multi/​CMakeLists.txt Links multi-size FFT tests and supporting sources.
test/​ztest/​unit/​common/​sof_context.c Supplies a minimal SOF context for standalone tests.
test/​ztest/​unit/​common/​mod_alloc.h Declares allocation tracking and cleanup helpers.
test/​ztest/​unit/​common/​mod_alloc.c Implements the test allocator and post-test leak checks.
.github/​workflows/​zephyr-unit-tests.yml Enables sanitizers and disables debuginfod queries during test execution.

Review effort: Balanced

The three objpool test cases allocate objects from a local objpool_head
but never release the pools themselves. objpool_free() only clears the
in-use bit of an object, the backing pool stays allocated, so every test
case leaks the pools it created: 772 bytes in 9 objects for the suite.

Call objpool_prune(), which unlinks and frees every pool of a head, at
the end of each test case.

Without this the suite fails once the unit tests run under LeakSanitizer:

  west twister --testsuite-root sof/test/ztest/unit/ --platform native_sim \
    --enable-asan --enable-lsan

Assisted-by: Copilot:claude-opus-5
Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
…r app

zephyr/lib/alloc.c is compiled by the firmware only when
CONFIG_SOF_FULL_ZEPHYR_APPLICATION=y. Without that option rtos/alloc.h
already provides a static inline sof_sys_heap_get() returning NULL, so a
standalone application that links alloc.c directly, such as a ztest unit
test, gets two conflicting definitions of the symbol.

Guard the out-of-line definition with the same condition as the header,
so the two always agree. Firmware builds are not affected: they set
CONFIG_SOF_FULL_ZEPHYR_APPLICATION and keep the out-of-line version.

Assisted-by: Copilot:claude-opus-5
Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
Suites that exercise code calling the mod_alloc() API cannot link the
production allocator in src/audio/module_adapter/module/generic.c without
pulling in the whole module adapter, IPC and objpool infrastructure. Suites
that link src/audio/component.c need sof_get(), which lives in
src/init/init.c and drags in the full firmware init path.

Add two reusable pieces under test/ztest/unit/common/:

mod_alloc.c/.h implements mod_alloc_ext(), mod_balloc_align(), mod_free()
and mod_free_all() on top of sof_ctx_alloc()/sof_ctx_free(), with the same
allocation flags and the same zero-size and unknown-pointer handling as
generic.c. Like generic.c it keeps a list of the live allocations, so
mod_free_all() works without an objpool and a suite can detect leaked,
double-freed or foreign pointers on its own. mod_alloc_test_after() is
ready to install as the ztest after-hook, so a leaking test case is named
instead of only producing an allocation stack at exit.

sof_context.c provides the minimal sof_get(), created on first use with
only its component driver list initialised, which is what buffer_new() and
the component infrastructure need.

Both files are unused until a suite links them, so no existing test
changes behaviour.

Assisted-by: Copilot:claude-opus-5
Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
Port the legacy CMocka unit tests test/cmocka/src/math/fft/fft.c and
fft_multi.c to Zephyr ztest suites under
test/ztest/unit/math/{fft,fft_multi}/.

sof.unit.math.fft mirrors fft.c: it links comp_buffer/audio_buffer and
the surrounding module_adapter/pipeline/ipc plumbing (as
test/ztest/unit/audio/drc already does) so buffer_new()/rzalloc() work
standalone, and converts each of the 9 SNR/peak-bin checks (16- and
32-bit FFT/IFFT, mono and 2-channel) into its own ZTEST case.

sof.unit.math.fft_multi mirrors fft_multi.c: it only links
fft_multi[.c/_generic.c/_hifi3.c] and fft_common.c/fft_32.c (no
comp_buffer/pipeline needed, matching the legacy CMakeLists.txt), and
splits the single CMocka group test into 11 ZTEST cases, one per FFT/
IFFT size, each still checked against the same max-error-abs/RMS
thresholds. The debug fopen()/fclose() dumps in the original
fft_multi_32_test() were unused diagnostics and are dropped; everything
else is unchanged. Reference vectors (input.h, ref_*fft_multi_*_32.h)
are #included in place from the CMocka tree via the include path, shared
verbatim between both frameworks, as test/ztest/unit/math/{a_law,mu_law}
already do. The CMocka tests keep running unchanged.

Both suites link the real SOF allocator, zephyr/lib/alloc.c, instead of a
per-suite malloc() replacement of rmalloc()/rzalloc()/rfree()/
sof_heap_alloc(). alloc.c has two native_sim back ends: the Zephyr
sys_heap, the default and the same allocator as firmware, and host
malloc()/free() through nsi_host_malloc()
(CONFIG_SOF_NATIVE_SIM_HOST_HEAP). ASan and LSan cannot see blocks carved
out of the static sys_heap arena, so the suites select the host back end
and stay checkable; the arena is still reserved and initialised by
heap_init(), hence CONFIG_SYS_HEAP_BIG_ONLY=y, its 256 KiB being too
large for SYS_HEAP_SMALL_ONLY. Firmware and the libfuzzer build do not
set the option and keep sys_heap.

The module allocator and the minimal sof_get() come from
test/ztest/unit/common/. mod_alloc_test_after() is installed as the ztest
after-hook of both suites, so a test case leaking an FFT plan is named
rather than only reported as an allocation stack at exit. ASan builds get
-fno-omit-frame-pointer, so the reports show the full allocation stack.

Verified with:
  west twister --testsuite-root sof/test/ztest/unit/ --platform native_sim
  -> sof.unit.math.fft: 9/9 cases PASSED
  -> sof.unit.math.fft_multi: 11/11 cases PASSED
  -> whole unit tree: 14/14 configurations, 118/118 cases PASSED, with
     and without --enable-asan --enable-lsan

  ctest --test-dir <cmocka build> -R '^fft$|^fft_multi$'
  -> fft, fft_multi: PASSED (unchanged)

A leaked mod_alloc() is reported by mod_alloc_test_after() and a leaked
rzalloc() by LSan, both naming test_fft_ztest.c. The sof/app native_sim
build works with both heap back ends and the mtl firmware build is
unchanged.

Coverage of the sources under test, CMocka (gcc) vs ztest (clang);
totals differ because clang does not instrument bare
variable-declaration lines that gcc counts:

  file                          CMocka line%   ztest line%
  src/math/fft/fft_16.c              100%          94.4%
  src/math/fft/fft_32.c              100%          94.7%
  src/math/fft/fft_common.c         73.2%          70.9%
  src/math/fft/fft_multi.c          72.9%          73.6%
  src/math/fft/fft_multi_generic.c   100%           100%

For fft_16.c that is 42 lines counted by gcc against 36 by clang. The
executed logic (bit-reverse loop, FFT butterfly stages, ifft branch) is
fully exercised by both and the function-entry counts match exactly, for
example 5 calls to fft_execute_16().

Assisted-by: Copilot:claude-opus-5
Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
The Zephyr Unit tests workflow builds and runs the whole
test/ztest/unit/ tree on native_sim, but without any sanitizer, so a
suite that leaks memory still reports as passed.

Add --enable-asan --enable-lsan to the twister invocation, so a leak
fails CI.

DEBUGINFOD_URLS has to be empty there. Otherwise llvm-symbolizer queries
debuginfod while LSan prints the leak report, which takes longer than
twister's post-verdict grace period; twister then kills the binary,
ignores its exit code and reports the leaking suite as passed.

The whole tree is clean under ASan and LSan: 14/14 configurations,
118/118 cases passed.

Assisted-by: Copilot:claude-opus-5
Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
@intel-sofci

Copy link
Copy Markdown

PR 11369: test results

Run date: 2026-10-28 14:46 UTC

Tested commit: 27e2e9f0130385430bfed313cb7d469244cc874e

mtl pass rate lnl pass rate ptl pass rate wcl pass rate nvl pass rate

@lgirdwood

Copy link
Copy Markdown
Member

CI failure, not PR.

@lgirdwood
lgirdwood merged commit 5d9f507 into thesofproject:main Oct 28, 2026
44 of 45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants