From ef83ef416323c3e0f19c0df12e993b540a1d6fe1 Mon Sep 17 00:00:00 2001 From: Tomasz Leman Date: Wed, 28 Oct 2026 15:58:32 +0100 Subject: [PATCH] test: math: port window CMocka test to ztest Port the legacy CMocka unit test test/cmocka/src/math/window/window.c to a Zephyr ztest suite under test/ztest/unit/math/window/. sof.unit.math.window mirrors window.c: it links src/math/window.c and its trig/log/decibels dependencies, matching the legacy CMakeLists.txt, and converts the 4 window checks (rectangular, Blackman, Hamming, Povey) into one ZTEST case each, all against the same 1.0 LSB RMS error limit, computed by the same float math. The reference windows (ref_window_*.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,fft} already do. The CMocka test keeps running unchanged. Each ZTEST case now calls its window function directly, so the strcmp() dispatch on a window name string of the original test_window() is dropped, along with the #ifdef DEBUG_FILES dump, which was an unused diagnostic. The scratch window is a static array sized by the longest reference vector rather than a malloc() per call, with BUILD_ASSERTs tying it to the reference lengths, so the suite needs no allocator at all. Two build differences against CMocka, neither of which changes the error math: Zephyr compiles with -Wdouble-promotion, so the RMS uses sqrtf() and casts its float arguments to double explicitly at the printk() varargs call sites, and CONFIG_CBPRINTF_FP_SUPPORT=y is needed because printk() otherwise emits the %f conversions of the log and of the failure message literally. Verified with: west twister -T sof/test/ztest/unit/math/window/ --platform native_sim -> sof.unit.math.window: 4/4 cases PASSED -> whole unit tree: 15/15 configurations, 122/122 cases PASSED, with and without --enable-asan --enable-lsan The reported RMS errors are identical to the ones the CMocka test computes from the same references, in LSB: window CMocka ztest limit rectangular 0.00 0.00 1.00 blackman 0.67 0.67 1.00 hamming 0.00 0.00 1.00 povey 0.29 0.29 1.00 Like the CMocka test, the suite covers only the 16-bit windows that have reference vectors. win_rectangular_32b(), win_blackman_32b(), win_hann_16b(), win_hann_32b() and win_hamming_32b() stay uncovered. Assisted-by: Copilot:claude-opus-5 Signed-off-by: Tomasz Leman --- test/ztest/unit/math/window/CMakeLists.txt | 35 +++++ test/ztest/unit/math/window/prj.conf | 9 ++ .../unit/math/window/test_window_ztest.c | 120 ++++++++++++++++++ test/ztest/unit/math/window/testcase.yaml | 25 ++++ 4 files changed, 189 insertions(+) create mode 100644 test/ztest/unit/math/window/CMakeLists.txt create mode 100644 test/ztest/unit/math/window/prj.conf create mode 100644 test/ztest/unit/math/window/test_window_ztest.c create mode 100644 test/ztest/unit/math/window/testcase.yaml diff --git a/test/ztest/unit/math/window/CMakeLists.txt b/test/ztest/unit/math/window/CMakeLists.txt new file mode 100644 index 000000000000..1d6aeb557f6c --- /dev/null +++ b/test/ztest/unit/math/window/CMakeLists.txt @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2026 Intel Corporation. + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sof_unit_math_window) + +set(SOF_ROOT "${PROJECT_SOURCE_DIR}/../../../../..") + +target_include_directories(app PRIVATE + ${SOF_ROOT}/zephyr/include + ${SOF_ROOT}/src/include + # Reference windows are #included in place from the CMocka tree rather + # than copied, so both frameworks share identical data. + ${SOF_ROOT}/test/cmocka/src/math/window +) + +target_compile_definitions(app PRIVATE + -DCONFIG_ZEPHYR_POSIX=1 + -DCONFIG_LIBRARY=1 + -DUNIT_TEST=1 +) + +target_sources(app PRIVATE + test_window_ztest.c + ${SOF_ROOT}/src/math/window.c + ${SOF_ROOT}/src/math/trig.c + ${SOF_ROOT}/src/math/log_e.c + ${SOF_ROOT}/src/math/base2log.c + ${SOF_ROOT}/src/math/decibels.c +) + +target_link_libraries(app PRIVATE m) diff --git a/test/ztest/unit/math/window/prj.conf b/test/ztest/unit/math/window/prj.conf new file mode 100644 index 000000000000..6528b8285a9b --- /dev/null +++ b/test/ztest/unit/math/window/prj.conf @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2026 Intel Corporation. + +CONFIG_ZTEST=y +CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n +# The RMS error is reported and asserted with %f, which printk() and so +# TC_PRINT() and the zassert() messages only format with FP support enabled +CONFIG_CBPRINTF_FP_SUPPORT=y diff --git a/test/ztest/unit/math/window/test_window_ztest.c b/test/ztest/unit/math/window/test_window_ztest.c new file mode 100644 index 000000000000..47efa8e8df77 --- /dev/null +++ b/test/ztest/unit/math/window/test_window_ztest.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// +// These contents may have been developed with support from one or more +// Intel-operated generative artificial intelligence solutions. +// +// Converted from CMock to Ztest +// Original tests from test/cmocka/src/math/window/window.c: +// Author: Seppo Ingalsuo + +#include + +#include + +#include +#include + +/* Reference windows shared with the legacy CMocka test. They are #included + * in place from the CMocka tree (added to the include path in CMakeLists.txt) + * so the exact same fixed-point data is exercised. + */ +#include "ref_window_rectangular.h" +#include "ref_window_blackman.h" +#include "ref_window_hamming.h" +#include "ref_window_povey.h" + +/* Maximum tolerated RMS error against the reference, in LSB */ +#define MAX_ERR_RMS 1.0f + +/* Scratch window, large enough for the longest reference vector */ +#define MAX_WIN_LENGTH LENGTH_HAMMING + +BUILD_ASSERT(LENGTH_RECTANGULAR <= MAX_WIN_LENGTH); +BUILD_ASSERT(LENGTH_BLACKMAN <= MAX_WIN_LENGTH); +BUILD_ASSERT(LENGTH_HAMMING <= MAX_WIN_LENGTH); +BUILD_ASSERT(LENGTH_POVEY <= MAX_WIN_LENGTH); + +static int16_t win[MAX_WIN_LENGTH]; + +/** + * @brief Compute the RMS error of a calculated window against its reference. + * @param[in] ref_win Reference Q1.15 coefficients. + * @param[in] win Calculated Q1.15 coefficients. + * @param[in] win_length Number of coefficients to compare. + * @return RMS of the per-coefficient error, in LSB. + */ +static float window_delta_rms(const int16_t *ref_win, const int16_t *win, int win_length) +{ + float err_squared = 0.0; + int i; + + for (i = 0; i < win_length; i++) { + err_squared += + ((float)ref_win[i] - (float)win[i]) * ((float)ref_win[i] - (float)win[i]); + } + + return sqrtf(err_squared / (float)win_length); +} + +/** + * @brief Check the window left in win[] against a reference vector. + * @param[in] window_name Window name, for the test log. + * @param[in] ref_win Reference Q1.15 coefficients. + * @param[in] window_length Number of coefficients to compare. + */ +static void check_window(const char *window_name, const int16_t *ref_win, int window_length) +{ + float err_rms = window_delta_rms(ref_win, win, window_length); + + /* printk() is varargs, so the float arguments are promoted to double + * anyway; cast explicitly to keep -Wdouble-promotion quiet. + */ + TC_PRINT("Window %s RMS error = %5.2f LSB (max %5.2f)\n", window_name, (double)err_rms, + (double)MAX_ERR_RMS); + + zassert_true(err_rms < MAX_ERR_RMS, "Window %s RMS error %5.2f exceeds limit %5.2f", + window_name, (double)err_rms, (double)MAX_ERR_RMS); +} + +/** + * @brief Verify the rectangular window against its reference vector + */ +ZTEST(math_window_suite, test_math_window_rectangular) +{ + win_rectangular_16b(win, LENGTH_RECTANGULAR); + check_window("rectangular", ref_rectangular, LENGTH_RECTANGULAR); +} + +/** + * @brief Verify the Blackman window against its reference vector + */ +ZTEST(math_window_suite, test_math_window_blackman) +{ + win_blackman_16b(win, LENGTH_BLACKMAN, WIN_BLACKMAN_A0_Q15); + check_window("blackman", ref_blackman, LENGTH_BLACKMAN); +} + +/** + * @brief Verify the Hamming window against its reference vector + */ +ZTEST(math_window_suite, test_math_window_hamming) +{ + win_hamming_16b(win, LENGTH_HAMMING); + check_window("hamming", ref_hamming, LENGTH_HAMMING); +} + +/** + * @brief Verify the Povey window against its reference vector + */ +ZTEST(math_window_suite, test_math_window_povey) +{ + win_povey_16b(win, LENGTH_POVEY); + check_window("povey", ref_povey, LENGTH_POVEY); +} + +/** + * @brief Define and initialize the window function test suite + */ +ZTEST_SUITE(math_window_suite, NULL, NULL, NULL, NULL, NULL); diff --git a/test/ztest/unit/math/window/testcase.yaml b/test/ztest/unit/math/window/testcase.yaml new file mode 100644 index 000000000000..02d3fc790e6d --- /dev/null +++ b/test/ztest/unit/math/window/testcase.yaml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2026 Intel Corporation. +# +# Math window function unit tests converted from CMock to Ztest +# +# These contents may have been developed with support from one or more Intel-operated +# generative artificial intelligence solutions. +# + +common: + tags: + - SOF + - unit_test + - math + - window + integration_platforms: + - native_sim + arch_exclude: xtensa + +tests: + sof.unit.math.window: + platform_allow: + - native_sim + build_only: false