1 /* SPDX-License-Identifier: LGPL-2.1-only */
3 * @file unittest_filter_onnxruntime.cc
5 * @brief Unit test for onnxruntime tensor filter sub-plugin
6 * @author Suyeon Kim <suyeon5.kim@samsung.com>
7 * @see http://github.com/nnstreamer/nnstreamer
12 #include <gtest/gtest.h>
16 #include <nnstreamer_plugin_api_filter.h>
17 #include <nnstreamer_util.h>
18 #include <tensor_common.h>
19 #include <unittest_util.h>
20 #include "nnstreamer_plugin_api.h"
21 #include "nnstreamer_plugin_api_util.h"
24 * @brief internal function to get model filename
27 _GetModelFilePath (gchar **model_file)
29 const gchar *src_root = g_getenv ("NNSTREAMER_SOURCE_ROOT_PATH");
30 g_autofree gchar *root_path = src_root ? g_strdup (src_root) : g_get_current_dir ();
31 std::string model_name = "mobilenet_v2_quant.onnx";
33 *model_file = g_build_filename (
34 root_path, "tests", "test_models", "models", model_name.c_str (), NULL);
36 return g_file_test (*model_file, G_FILE_TEST_EXISTS);
40 * @brief internal function to get the orange.png
43 _GetOrangePngFilePath (gchar **input_file)
45 const gchar *src_root = g_getenv ("NNSTREAMER_SOURCE_ROOT_PATH");
46 g_autofree gchar *root_path = src_root ? g_strdup (src_root) : g_get_current_dir ();
47 std::string input_file_name = "orange.png";
49 *input_file = g_build_filename (
50 root_path, "tests", "test_models", "data", input_file_name.c_str (), NULL);
52 return g_file_test (*input_file, G_FILE_TEST_EXISTS);
56 * @brief Set tensor filter properties
59 _SetFilterProp (GstTensorFilterProperties *prop, const gchar *name, const gchar **models)
61 memset (prop, 0, sizeof (GstTensorFilterProperties));
64 prop->model_files = models;
65 prop->num_models = g_strv_length ((gchar **) models);
69 * @brief Signal to validate the result in tensor_sink
72 check_output (GstElement *element, GstBuffer *buffer, gpointer user_data)
79 mem_res = gst_buffer_get_memory (buffer, 0);
80 mapped = gst_memory_map (mem_res, &info_res, GST_MAP_READ);
83 gint is_float = (gint) * ((guint8 *) user_data);
84 guint idx, max_idx = 0U;
87 guint8 *output = (guint8 *) info_res.data;
90 for (idx = 0; idx < info_res.size; ++idx) {
91 if (output[idx] > max_value) {
92 max_value = output[idx];
96 } else if (is_float == 1) {
97 gfloat *output = (gfloat *) info_res.data;
98 gfloat max_value = G_MINFLOAT;
100 for (idx = 0; idx < (info_res.size / sizeof (gfloat)); ++idx) {
101 if (output[idx] > max_value) {
102 max_value = output[idx];
108 gst_memory_unmap (mem_res, &info_res);
109 gst_memory_unref (mem_res);
111 EXPECT_EQ (max_idx, 951U);
115 * @brief Negative test case with invalid model file path
117 TEST (nnstreamerFilterOnnxRuntime, openClose00)
122 const gchar *model_files[] = {
123 "some/invalid/model/path.onnx",
127 const GstTensorFilterFramework *sp = nnstreamer_filter_find ("onnxruntime");
128 EXPECT_NE (sp, nullptr);
130 GstTensorFilterProperties prop;
131 _SetFilterProp (&prop, "onnxruntime", model_files);
133 ret = sp->open (&prop, &data);
138 * @brief Positive case with successful getModelInfo
140 TEST (nnstreamerFilterOnnxRuntime, getModelInfo00)
146 ASSERT_TRUE (_GetModelFilePath (&model_file));
148 const gchar *model_files[] = {
153 const GstTensorFilterFramework *sp = nnstreamer_filter_find ("onnxruntime");
154 EXPECT_NE (sp, nullptr);
156 GstTensorFilterProperties prop;
157 _SetFilterProp (&prop, "onnxruntime", model_files);
159 ret = sp->open (&prop, &data);
164 * @brief Positive case with successful getModelInfo
166 TEST (nnstreamerFilterOnnxRuntime, getModelInfo00_1)
170 g_autofree gchar *model_file = NULL;
172 ASSERT_TRUE (_GetModelFilePath (&model_file));
174 const gchar *model_files[] = {
179 const GstTensorFilterFramework *sp = nnstreamer_filter_find ("onnxruntime");
180 EXPECT_NE (sp, nullptr);
182 GstTensorFilterProperties prop;
183 _SetFilterProp (&prop, "onnxruntime", model_files);
185 ret = sp->open (&prop, &data);
189 GstTensorsInfo in_info, out_info;
190 ret = sp->getModelInfo (NULL, NULL, data, GET_IN_OUT_INFO, &in_info, &out_info);
193 EXPECT_EQ (in_info.num_tensors, 1U);
194 EXPECT_EQ (in_info.info[0].dimension[0], 224U);
195 EXPECT_EQ (in_info.info[0].dimension[1], 224U);
196 EXPECT_EQ (in_info.info[0].dimension[2], 3U);
197 EXPECT_EQ (in_info.info[0].dimension[3], 1U);
198 EXPECT_EQ (in_info.info[0].type, _NNS_FLOAT32);
200 EXPECT_EQ (out_info.num_tensors, 1U);
201 EXPECT_EQ (out_info.info[0].dimension[0], 1000U);
202 EXPECT_EQ (out_info.info[0].dimension[1], 1U);
203 EXPECT_EQ (out_info.info[0].dimension[2], 0U);
204 EXPECT_EQ (out_info.info[0].dimension[3], 0U);
205 EXPECT_EQ (out_info.info[0].type, _NNS_FLOAT32);
207 sp->close (&prop, &data);
209 gst_tensors_info_free (&in_info);
210 gst_tensors_info_free (&out_info);
214 * @brief Test onnxruntime subplugin with successful invoke for sample onnx model (input data type: float)
216 TEST (nnstreamerFilterOnnxRuntime, invoke00)
220 GstTensorMemory input, output;
221 g_autofree gchar *model_file = NULL;
223 ASSERT_TRUE (_GetModelFilePath (&model_file));
225 const gchar *model_files[] = {
230 const GstTensorFilterFramework *sp = nnstreamer_filter_find ("onnxruntime");
231 ASSERT_TRUE (sp != nullptr);
233 GstTensorFilterProperties prop;
234 _SetFilterProp (&prop, "onnxruntime", model_files);
236 input.size = sizeof (float) * 224 * 224 * 3 * 1;
237 output.size = sizeof (float) * 1000 * 1;
239 input.data = g_malloc0 (input.size);
240 output.data = g_malloc0 (output.size);
242 ret = sp->open (&prop, &data);
245 /* invoke successful */
246 ret = sp->invoke (NULL, &prop, data, &input, &output);
250 g_free (output.data);
252 sp->close (&prop, &data);
256 * @brief Negative case with invalid input/output
258 TEST (nnstreamerFilterOnnxRuntime, invoke01_n)
262 GstTensorMemory input, output;
263 g_autofree gchar *model_file = NULL;
265 ASSERT_TRUE (_GetModelFilePath (&model_file));
267 const gchar *model_files[] = {
272 const GstTensorFilterFramework *sp = nnstreamer_filter_find ("onnxruntime");
273 ASSERT_TRUE (sp != nullptr);
275 GstTensorFilterProperties prop;
276 _SetFilterProp (&prop, "onnxruntime", model_files);
278 output.size = input.size = sizeof (float) * 1;
279 input.data = g_malloc0 (input.size);
280 output.data = g_malloc0 (output.size);
282 ret = sp->open (&prop, &data);
285 /* catching exception */
286 EXPECT_NE (sp->invoke (NULL, &prop, data, NULL, &output), 0);
287 EXPECT_NE (sp->invoke (NULL, &prop, data, &input, NULL), 0);
290 g_free (output.data);
291 sp->close (&prop, &data);
295 * @brief Negative case to launch gst pipeline: wrong dimension
297 TEST (nnstreamerFilterOnnxRuntime, launch00_n)
301 g_autofree gchar *model_file = NULL;
303 ASSERT_TRUE (_GetModelFilePath (&model_file));
305 /* create a nnstreamer pipeline */
306 g_autofree gchar *pipeline = g_strdup_printf (
307 "videotestsrc num-buffers=10 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=42,height=42,framerate=0/1 ! tensor_converter ! tensor_filter framework=onnxruntime model=\"%s\" latency=1 ! tensor_sink",
310 gstpipe = gst_parse_launch (pipeline, &err);
311 ASSERT_TRUE (gstpipe != nullptr);
313 EXPECT_NE (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
315 gst_object_unref (gstpipe);
319 * @brief Negative case to launch gst pipeline: wrong data type
321 TEST (nnstreamerFilterOnnxRuntime, launch01_n)
325 g_autofree gchar *model_file = NULL;
327 ASSERT_TRUE (_GetModelFilePath (&model_file));
329 /* create a nnstreamer pipeline */
330 g_autofree gchar *pipeline = g_strdup_printf (
331 "videotestsrc num-buffers=10 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=0/1 ! tensor_converter ! tensor_filter framework=onnxruntime model=\"%s\" latency=1 ! tensor_sink",
334 gstpipe = gst_parse_launch (pipeline, &err);
335 ASSERT_TRUE (gstpipe != nullptr);
337 EXPECT_NE (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
339 gst_object_unref (gstpipe);
343 * @brief Positive case to launch gst pipeline
345 TEST (nnstreamerFilterOnnxRuntime, floatModelResult)
349 g_autofree gchar *model_file = NULL;
350 g_autofree gchar *input_file = NULL;
352 ASSERT_TRUE (_GetModelFilePath (&model_file));
353 ASSERT_TRUE (_GetOrangePngFilePath (&input_file));
355 /* create a nnstreamer pipeline */
356 g_autofree gchar *pipeline = g_strdup_printf (
357 "filesrc location=\"%s\" ! pngdec ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=0/1 ! tensor_converter ! tensor_transform mode=transpose option=1:2:0:3 ! tensor_transform mode=arithmetic option=typecast:float32,div:127.5,add:-1.0 ! tensor_filter framework=onnxruntime model=\"%s\" ! tensor_sink name=sink",
358 input_file, model_file);
360 gstpipe = gst_parse_launch (pipeline, &err);
361 ASSERT_TRUE (gstpipe != nullptr);
363 GstElement *sink_handle = gst_bin_get_by_name (GST_BIN (gstpipe), "sink");
365 ASSERT_TRUE (sink_handle != nullptr);
369 g_signal_connect (sink_handle, "new-data", (GCallback) check_output, &is_float);
371 EXPECT_EQ (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT * 10),
374 EXPECT_EQ (setPipelineStateSync (gstpipe, GST_STATE_NULL, UNITTEST_STATECHANGE_TIMEOUT), 0);
376 gst_object_unref (sink_handle);
377 gst_object_unref (gstpipe);
381 * @brief Negative case with incorrect path
383 TEST (nnstreamerFilterOnnxRuntime, error00_n)
388 const gchar *root_path = g_getenv ("NNSTREAMER_SOURCE_ROOT_PATH");
389 g_autofree gchar *model_file = g_build_filename (
390 root_path, "tests", "test_models", "models", "incorrect_path.onnx", NULL);
392 /* Create a nnstreamer pipeline */
393 g_autofree gchar *pipeline = g_strdup_printf (
394 "videotestsrc ! videoconvert ! videoscale ! videorate ! video/x-raw,format=RGB,width=224,height=224 ! tensor_converter ! tensor_filter framework=onnxruntime model=\"%s\" ! fakesink",
396 gstpipe = gst_parse_launch (pipeline, &err);
399 EXPECT_NE (gst_element_set_state (gstpipe, GST_STATE_PLAYING), GST_STATE_CHANGE_SUCCESS);
400 EXPECT_EQ (gst_element_set_state (gstpipe, GST_STATE_PLAYING), GST_STATE_CHANGE_FAILURE);
402 EXPECT_NE (gst_element_set_state (gstpipe, GST_STATE_NULL), GST_STATE_CHANGE_FAILURE);
405 gst_object_unref (gstpipe);
408 ml_loge ("GST PARSE LAUNCH FAILED: [%s], %s\n", pipeline,
409 (err) ? err->message : "unknown reason");
410 g_clear_error (&err);
412 EXPECT_EQ (status, 0);
416 * @brief Negative case with incorrect tensor meta
418 TEST (nnstreamerFilterOnnxRuntime, error01_n)
423 g_autofree gchar *model_file = NULL;
425 ASSERT_TRUE (_GetModelFilePath (&model_file));
427 /* Create a nnstreamer pipeline */
428 g_autofree gchar *pipeline = g_strdup_printf (
429 "videotestsrc ! videoconvert ! videoscale ! videorate ! video/x-raw,format=RGB,width=240,height=224 ! tensor_converter ! tensor_filter framework=onnxruntime model=\"%s\" ! fakesink",
432 gstpipe = gst_parse_launch (pipeline, &err);
434 GstState state, pending;
436 EXPECT_NE (gst_element_set_state (gstpipe, GST_STATE_PLAYING), GST_STATE_CHANGE_SUCCESS);
438 /* This should fail: dimension mismatched. */
439 EXPECT_EQ (gst_element_get_state (gstpipe, &state, &pending, GST_SECOND / 4),
440 GST_STATE_CHANGE_FAILURE);
442 EXPECT_NE (gst_element_set_state (gstpipe, GST_STATE_NULL), GST_STATE_CHANGE_FAILURE);
445 gst_object_unref (gstpipe);
448 ml_loge ("GST PARSE LAUNCH FAILED: [%s], %s\n", pipeline,
449 (err) ? err->message : "unknown reason");
450 g_clear_error (&err);
452 EXPECT_EQ (status, 0);
459 main (int argc, char **argv)
464 testing::InitGoogleTest (&argc, argv);
466 g_warning ("catch 'testing::internal::<unnamed>::ClassUniqueToAlwaysTrue'");
469 gst_init (&argc, &argv);
471 /* Force the binary to use dlog_print of untitest-util by calling it directly */
472 ml_logd ("onnxruntime test starts w/ dummy backend.");
475 result = RUN_ALL_TESTS ();
477 g_warning ("catch `testing::internal::GoogleTestFailureException`");