Skip to content

Commit 3effb1e

Browse files
tools: plugin: ov_noise_suppression: add regression test for adversarial model shapes
Add a regression test that exercises the input-shape guard introduced in "tools: plugin: ov_noise_suppression: validate model input shape dimensions". The test is a small C++ executable living next to the module sources at tools/plugin/modules/ov_noise_suppression/test_ns_shape_validation.cpp and wired into the module's CMakeLists.txt inside the existing if(OpenVINO_FOUND) block, so when OpenVINO is not installed the test is skipped along with the module itself. The test drives the public C API of the module (ov_ns_init / ov_ns_free / NOISE_SUPPRESSION_MODEL_NAME) rather than reaching into private structs, and is configurable from the command line: --model PATH use a caller-supplied OpenVINO model XML file. --dim N [--dim N ...] synthesize a minimal model XML with the given input-port dimensions, write it to a temporary file (mkstemps), and use that. --expect ok|reject expected outcome; default "reject". -h, --help print usage describing what the test does and how to run it. The mock-model writer emits the smallest XML that ov::Core::read_model() will accept for a Parameter input with the configured shape, so ov_ns_init() reaches the dimension-validation loop the previous commit added. The test then asserts that ov_ns_init() returned 0 for --expect ok and non-zero for --expect reject, and exits 0 (PASS) or 1 (FAIL) accordingly. Two ctest cases are registered at CMake time covering the two adversarial shapes that motivated the fix: ns_shape_zero --dim 0 --dim 480 # zero dim ns_shape_overflow --dim 9223372036854775807 --dim 1024 # overflow Real models can be exercised by invoking the executable directly with --model PATH --expect ok. Addresses review comments on PR #10886: r3403937663 - move test next to the module, build via the module's CMakeLists so OpenVINO/dep checks are reused. r3403939540 - take the model file / shape parameters on the command line instead of hard-coding /tmp/test_model_<pid>.xml. r3403944866 - add a -h help option describing the test and its usage. r3403950342 - add the missing SPDX-License-Identifier and copyright header; let CMake gate the build on OpenVINO presence. Signed-off-by: OrbisAI Security <mediratta01.pally@gmail.com>
1 parent e65ff91 commit 3effb1e

2 files changed

Lines changed: 157 additions & 0 deletions

File tree

tools/plugin/modules/ov_noise_suppression/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,22 @@ set_target_properties(sof_ns
3939
INSTALL_RPATH "${sof_install_directory}/alsa-lib"
4040
INSTALL_RPATH_USE_LINK_PATH TRUE
4141
)
42+
43+
# Regression test for the input-shape validation guard. Gated on
44+
# OpenVINO_FOUND alongside the module itself, so a host without
45+
# OpenVINO simply skips it.
46+
add_executable(test_ns_shape_validation test_ns_shape_validation.cpp)
47+
target_link_libraries(test_ns_shape_validation PRIVATE sof_ns_interface)
48+
target_include_directories(test_ns_shape_validation PRIVATE
49+
${CMAKE_CURRENT_SOURCE_DIR}
50+
${sof_source_directory}/src/include
51+
${sof_source_directory}/posix/include
52+
${sof_source_directory}/src/arch/host/include
53+
${sof_source_directory}/src/platform/posix/include)
54+
set_target_properties(test_ns_shape_validation PROPERTIES LINKER_LANGUAGE CXX)
55+
56+
add_test(NAME ns_shape_zero
57+
COMMAND test_ns_shape_validation --dim 0 --dim 480)
58+
add_test(NAME ns_shape_overflow
59+
COMMAND test_ns_shape_validation --dim 9223372036854775807 --dim 1024)
4260
endif()
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2026 OrbisAI Security. All rights reserved.
4+
//
5+
// Author: OrbisAI Security <mediratta01.pally@gmail.com>
6+
7+
#include <cstdio>
8+
#include <cstdlib>
9+
#include <fstream>
10+
#include <string>
11+
#include <vector>
12+
#include <unistd.h>
13+
14+
#include "noise_suppression_interface.h"
15+
16+
static void usage(const char *prog)
17+
{
18+
std::printf(
19+
"Usage: %s [--model PATH | --dim N [--dim N ...]] [--expect ok|reject]\n"
20+
"\n"
21+
"Regression test for the OpenVINO noise-suppression plugin's input-shape\n"
22+
"validation (commit \"tools: plugin: ov_noise_suppression: validate model\n"
23+
"input shape dimensions\"). Invokes ov_ns_init() against either:\n"
24+
"\n"
25+
" --model PATH a caller-supplied OpenVINO model XML file, or\n"
26+
" --dim N [--dim N ...] a minimal synthesized model XML built from the\n"
27+
" given input port dimensions and written to a\n"
28+
" temporary file.\n"
29+
"\n"
30+
" --expect ok|reject expected outcome (default: reject).\n"
31+
" \"ok\": ov_ns_init() must return 0.\n"
32+
" \"reject\": ov_ns_init() must return non-zero.\n"
33+
"\n"
34+
"Returns 0 on PASS, 1 on FAIL, 2 on usage error.\n"
35+
"\n"
36+
"Examples:\n"
37+
" %s --dim 0 --dim 480 # zero dim, expect reject\n"
38+
" %s --dim 9223372036854775807 --dim 1024 # overflow, expect reject\n"
39+
" %s --model real_model.xml --expect ok # real model, expect accept\n",
40+
prog, prog, prog, prog);
41+
}
42+
43+
static int write_mock_model(const std::string &path,
44+
const std::vector<long long> &dims)
45+
{
46+
std::ofstream f(path);
47+
if (!f)
48+
return -1;
49+
50+
f << "<?xml version=\"1.0\"?><net><layers>"
51+
"<layer id=\"0\" name=\"input\" type=\"Parameter\">"
52+
"<output><port id=\"0\" precision=\"FP32\">";
53+
for (auto d : dims)
54+
f << "<dim>" << d << "</dim>";
55+
f << "</port></output></layer></layers></net>";
56+
return f ? 0 : -1;
57+
}
58+
59+
int main(int argc, char **argv)
60+
{
61+
std::string model_path;
62+
std::vector<long long> dims;
63+
bool expect_reject = true;
64+
65+
for (int i = 1; i < argc; i++) {
66+
std::string a = argv[i];
67+
if (a == "-h" || a == "--help") {
68+
usage(argv[0]);
69+
return 0;
70+
}
71+
if (a == "--model" && i + 1 < argc) {
72+
model_path = argv[++i];
73+
} else if (a == "--dim" && i + 1 < argc) {
74+
dims.push_back(std::strtoll(argv[++i], nullptr, 0));
75+
} else if (a == "--expect" && i + 1 < argc) {
76+
std::string v = argv[++i];
77+
if (v == "ok") {
78+
expect_reject = false;
79+
} else if (v == "reject") {
80+
expect_reject = true;
81+
} else {
82+
std::fprintf(stderr,
83+
"unknown --expect value: %s\n",
84+
v.c_str());
85+
usage(argv[0]);
86+
return 2;
87+
}
88+
} else {
89+
std::fprintf(stderr, "unknown or incomplete arg: %s\n",
90+
a.c_str());
91+
usage(argv[0]);
92+
return 2;
93+
}
94+
}
95+
96+
std::string scratch;
97+
if (model_path.empty()) {
98+
if (dims.empty()) {
99+
std::fprintf(stderr,
100+
"must supply --model PATH or one or more --dim N\n");
101+
usage(argv[0]);
102+
return 2;
103+
}
104+
105+
char tmpl[] = "/tmp/ns_shape_test_XXXXXX.xml";
106+
int fd = mkstemps(tmpl, 4);
107+
if (fd < 0) {
108+
std::perror("mkstemps");
109+
return 2;
110+
}
111+
close(fd);
112+
scratch = tmpl;
113+
if (write_mock_model(scratch, dims) != 0) {
114+
std::fprintf(stderr, "failed to write mock model %s\n",
115+
scratch.c_str());
116+
std::remove(scratch.c_str());
117+
return 2;
118+
}
119+
model_path = scratch;
120+
}
121+
122+
setenv("NOISE_SUPPRESSION_MODEL_NAME", model_path.c_str(), 1);
123+
124+
ns_handle h = nullptr;
125+
int rc = ov_ns_init(&h);
126+
if (h)
127+
ov_ns_free(h);
128+
129+
if (!scratch.empty())
130+
std::remove(scratch.c_str());
131+
132+
bool rejected = (rc != 0);
133+
bool pass = (rejected == expect_reject);
134+
135+
std::printf("model=%s rc=%d expect=%s -> %s\n", model_path.c_str(), rc,
136+
expect_reject ? "reject" : "ok",
137+
pass ? "PASS" : "FAIL");
138+
return pass ? 0 : 1;
139+
}

0 commit comments

Comments
 (0)