[TEST] Add testcase for flexible tensor_filter
authorgichan <gichan2.jang@samsung.com>
Wed, 12 Apr 2023 05:00:36 +0000 (14:00 +0900)
committerjaeyun-jung <39614140+jaeyun-jung@users.noreply.github.com>
Mon, 5 Jun 2023 07:51:04 +0000 (16:51 +0900)
 - Testcase for flexible tensor filter is disabled and activate after
development is done.
 - In order to invoke tensor_filter with flexible input/output, you need to specify custom option.
  ex) ... ! tensor_filter framework=... invoke-dynamic=TRUE model=... ! ...

Signed-off-by: gichan <gichan2.jang@samsung.com>
tests/meson.build
tests/nnstreamer_filter_custom/unittest_filter_custom.cc [new file with mode: 0644]

index a4a3e1b..21df1fc 100644 (file)
@@ -198,6 +198,15 @@ if gtest_dep.found()
 
       test('unittest_converter', unittest_converter, env: testenv)
     endif
+
+    unittest_filter_custom = executable('unittest_filter_custom',
+      join_paths('nnstreamer_filter_custom', 'unittest_filter_custom.cc'),
+      dependencies: [nnstreamer_unittest_deps],
+      install: get_option('install-test'),
+      install_dir: unittest_install_dir
+    )
+
+    test('unittest_filter_custom', unittest_filter_custom, timeout: 120, env: testenv)
   endif
 
   # Armnn unittest
diff --git a/tests/nnstreamer_filter_custom/unittest_filter_custom.cc b/tests/nnstreamer_filter_custom/unittest_filter_custom.cc
new file mode 100644 (file)
index 0000000..ec7f009
--- /dev/null
@@ -0,0 +1,409 @@
+/**
+ * @file       unittest_filter_custom.cc
+ * @date       11 Apr 2023
+ * @brief      Unit test for tensor filter custom-easy plugin
+ * @see                https://github.com/nnstreamer/nnstreamer
+ * @author     Gichan Jang <gichan2.jang@samsung.com>
+ * @bug                No known bugs.
+ */
+
+#include <gtest/gtest.h>
+#include <glib/gstdio.h>
+#include <gst/gst.h>
+#include <stdlib.h>
+#include <unittest_util.h>
+#include <tensor_filter_custom_easy.h>
+#include <nnstreamer_util.h>
+#include <nnstreamer_plugin_api.h>
+#include <nnstreamer_plugin_api_util.h>
+
+static guint filter_received;
+static guint sink_received;
+
+#define NNS_custom_easy_dynamic_register(...) 0
+/**
+ * @brief In-Code Test Function for custom-easy filter
+ */
+// static int
+// _custom_easy_filter_dynamic (void *data, const GstTensorsInfo * in_info,
+//     GstTensorsInfo * out_info, const GstTensorMemory * input,
+//     GstTensorMemory * output)
+// {
+//   gchar *dim_str;
+//   guint i;
+
+//  /* Fill output tensors info */
+//   gst_tensors_info_init (out_info);
+//   out_info->info[0].type = _NNS_UINT32;
+//   dim_str = g_strdup_printf ("%u:1:1:1", ++filter_received);
+//   gst_tensor_parse_dimension (dim_str, out_info->info[0].dimension);
+//   out_info->num_tensors = 1;
+//   out_info->format = _NNS_TENSOR_FORMAT_FLEXIBLE;
+
+//   /* Allocate and fill output memory */
+//   output[0].size = sizeof (guint) * filter_received;
+//   output[0].data = g_malloc0 (output[0].size);
+
+//   for (i = 0; i < filter_received; i++) {
+//     ((guint *) output[0].data)[i] = i;
+//   }
+
+//   return 0;
+// }
+
+/**
+ * @brief Callback for tensor sink signal.
+ */
+static void
+new_data_cb (GstElement *element, GstBuffer *buffer, gpointer user_data)
+{
+  gsize mem_size, header_size, expected_size;
+  GstTensorMetaInfo meta;
+  GstMemory *mem;
+  GstMapInfo map;
+  guint *data;
+  guint i;
+
+  expected_size = sizeof (guint) * ++sink_received;
+
+  EXPECT_EQ (1U, gst_buffer_n_memory (buffer));
+
+  mem = gst_buffer_peek_memory (buffer, 0);
+  if (!gst_memory_map (mem, &map, GST_MAP_READ)) {
+    g_message ("Failed to map the info buffer.");
+    return;
+  }
+
+  gst_tensor_meta_info_parse_header (&meta, map.data);
+  EXPECT_EQ (_NNS_TENSOR_FORMAT_FLEXIBLE, meta.format);
+  EXPECT_EQ (sink_received, meta.dimension[0]);
+
+  mem_size = gst_memory_get_sizes (mem, NULL, NULL);
+  header_size = gst_tensor_meta_info_get_header_size (&meta);
+  EXPECT_EQ (expected_size, mem_size - header_size);
+
+  data = (guint *) (map.data + header_size);
+  for (i = 0; i < filter_received; i++) {
+    EXPECT_EQ (i, data[i]);
+  }
+
+  gst_memory_unmap (mem, &map);
+}
+
+/**
+ * @brief Test custom-easy filter with flexible tensor input/output.
+ * @todo Enable the test after development is done.
+ */
+TEST (tensorFilterCustom, DISABLED_flexibleInvoke_p)
+{
+  gchar *pipeline;
+  GstElement *gstpipe;
+  GError *err = NULL;
+  GstTensorsInfo info_in;
+  GstElement *sink_handle;
+  int ret;
+
+  gst_tensors_info_init (&info_in);
+  info_in.num_tensors = 1U;
+  info_in.info[0].name = NULL;
+  info_in.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
+
+  ret = NNS_custom_easy_dynamic_register (
+      "flexbible_filter", _custom_easy_filter_dynamic, NULL, &info_in);
+  ASSERT_EQ (ret, 0);
+
+  /* create a nnstreamer pipeline */
+  pipeline = g_strdup_printf (
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_0 "
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_1 "
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_2 "
+    "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy invoke-dynamic=TRUE model=flexbible_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
+
+  gstpipe = gst_parse_launch (pipeline, &err);
+  ASSERT_TRUE (gstpipe != nullptr);
+
+  sink_handle = gst_bin_get_by_name (GST_BIN (gstpipe), "sinkx");
+  EXPECT_NE (sink_handle, nullptr);
+
+  g_signal_connect (sink_handle, "new-data", (GCallback) new_data_cb, NULL);
+
+  filter_received = 0;
+  sink_received = 0;
+  EXPECT_EQ (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+
+  EXPECT_TRUE (wait_pipeline_process_buffers (&sink_received, 6, TEST_TIMEOUT_LIMIT));
+  g_usleep (1000000);
+
+  /** cleanup registered custom_easy filter */
+  ret = NNS_custom_easy_unregister ("flexbible_filter");
+  ASSERT_EQ (0, ret);
+
+  gst_object_unref (sink_handle);
+  gst_object_unref (gstpipe);
+  g_free (pipeline);
+}
+
+
+/**
+ * @brief Test custom-easy filter with static input, flexible output.
+ * @todo Enable the test after development is done.
+ */
+TEST (tensorFilterCustom, DISABLED_staticFlexibleInvoke_p)
+{
+  gchar *pipeline;
+  GstElement *gstpipe;
+  GError *err = NULL;
+  GstTensorsInfo info_in;
+  GstElement *sink_handle;
+  int ret;
+
+  gst_tensors_info_init (&info_in);
+  info_in.num_tensors = 1U;
+  info_in.info[0].name = NULL;
+  info_in.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
+
+  ret = NNS_custom_easy_dynamic_register (
+      "flexbible_filter", _custom_easy_filter_dynamic, NULL, &info_in);
+  ASSERT_EQ (ret, 0);
+
+  /* create a nnstreamer pipeline */
+  pipeline = g_strdup_printf (
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! j.sink_0 "
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! j.sink_1 "
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! j.sink_2 "
+    "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy invoke-dynamic=TRUE model=flexbible_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
+
+  gstpipe = gst_parse_launch (pipeline, &err);
+  ASSERT_TRUE (gstpipe != nullptr);
+
+  sink_handle = gst_bin_get_by_name (GST_BIN (gstpipe), "sinkx");
+  EXPECT_NE (sink_handle, nullptr);
+
+  g_signal_connect (sink_handle, "new-data", (GCallback) new_data_cb, NULL);
+
+  filter_received = 0;
+  sink_received = 0;
+  EXPECT_EQ (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+
+  EXPECT_TRUE (wait_pipeline_process_buffers (&sink_received, 6, TEST_TIMEOUT_LIMIT));
+  g_usleep (1000000);
+
+  /** cleanup registered custom_easy filter */
+  ret = NNS_custom_easy_unregister ("flexbible_filter");
+  ASSERT_EQ (0, ret);
+
+  gst_object_unref (sink_handle);
+  gst_object_unref (gstpipe);
+  g_free (pipeline);
+}
+
+
+/**
+ * @brief Test dynamic invoke with invalid prop..
+ * @todo Enable the test after development is done.
+ */
+TEST (tensorFilterCustom, DISABLED_flexibleInvokeInvalidProp_n)
+{
+  gchar *pipeline;
+  GstElement *gstpipe;
+  GError *err = NULL;
+  GstTensorsInfo info_in;
+  GstElement *sink_handle;
+  int ret;
+
+  gst_tensors_info_init (&info_in);
+  info_in.num_tensors = 1U;
+  info_in.info[0].name = NULL;
+  info_in.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
+
+  ret = NNS_custom_easy_dynamic_register (
+      "flexbible_filter", _custom_easy_filter_dynamic, NULL, &info_in);
+  ASSERT_EQ (ret, 0);
+
+  /* create a nnstreamer pipeline */
+  pipeline = g_strdup_printf (
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_0 "
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_1 "
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_2 "
+    "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy invoke-dynamic=FALSE model=flexbible_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
+
+  gstpipe = gst_parse_launch (pipeline, &err);
+  ASSERT_TRUE (gstpipe != nullptr);
+
+  sink_handle = gst_bin_get_by_name (GST_BIN (gstpipe), "sinkx");
+  EXPECT_NE (sink_handle, nullptr);
+
+  g_signal_connect (sink_handle, "new-data", (GCallback) new_data_cb, NULL);
+
+  filter_received = 0;
+  sink_received = 0;
+  EXPECT_NE (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+
+  /** cleanup registered custom_easy filter */
+  ret = NNS_custom_easy_unregister ("flexbible_filter");
+  ASSERT_EQ (0, ret);
+
+  gst_object_unref (sink_handle);
+  gst_object_unref (gstpipe);
+  g_free (pipeline);
+}
+
+/**
+ * @brief In-Code Test Function for custom-easy filter
+ */
+static int
+_custom_easy_filter (void *data, const GstTensorFilterProperties *prop,
+    const GstTensorMemory *input, GstTensorMemory *output)
+{
+  guint i;
+
+  /* Allocate and fill output memory */
+  output[0].size = sizeof (guint) * ++filter_received;
+  output[0].data = g_malloc0 (output[0].size);
+
+  for (i = 0; i < filter_received; i++) {
+    ((guint *) output[0].data)[i] = i;
+  }
+
+  return 0;
+}
+
+/**
+ * @brief Test custom-easy statc invoke with flexible tensor input/output.
+ * @todo Enable the test after development is done.
+ */
+TEST (tensorFilterCustom, DISABLED_staticInvoke_n)
+{
+  gchar *pipeline;
+  GstElement *gstpipe;
+  GError *err = NULL;
+  GstTensorsInfo info_in;
+  GstTensorsInfo info_out;
+  GstElement *sink_handle;
+  int ret;
+
+  gst_tensors_info_init (&info_in);
+  info_in.num_tensors = 1U;
+  info_in.info[0].name = NULL;
+  info_in.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
+
+  gst_tensors_info_init (&info_out);
+  info_out.num_tensors = 1U;
+  info_out.info[0].name = NULL;
+  info_out.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
+
+  ret = NNS_custom_easy_register (
+      "normal_filter", _custom_easy_filter, NULL, &info_in, &info_out);
+  ASSERT_EQ (ret, 0);
+
+  /* create a nnstreamer pipeline */
+  pipeline = g_strdup_printf (
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_0 "
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_1 "
+    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_2 "
+    "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy model=normal_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
+
+  gstpipe = gst_parse_launch (pipeline, &err);
+  ASSERT_TRUE (gstpipe != nullptr);
+
+  sink_handle = gst_bin_get_by_name (GST_BIN (gstpipe), "sinkx");
+  EXPECT_NE (sink_handle, nullptr);
+
+  g_signal_connect (sink_handle, "new-data", (GCallback) new_data_cb, NULL);
+
+  filter_received = 0;
+  sink_received = 0;
+  EXPECT_NE (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+
+  /** cleanup registered custom_easy filter */
+  ret = NNS_custom_easy_unregister ("normal_filter");
+  ASSERT_EQ (0, ret);
+
+  gst_object_unref (sink_handle);
+  gst_object_unref (gstpipe);
+  g_free (pipeline);
+}
+
+/**
+ * @brief Test custom-easy filter with flexible tensor input/output without register custom easy model.
+ */
+TEST (tensorFilterCustom, DISABLED_notRegisterFlexibleInvoke_n)
+{
+  gchar *pipeline;
+  GstElement *gstpipe;
+  GError *err = NULL;
+  const gchar *root_path = g_getenv ("NNSTREAMER_SOURCE_ROOT_PATH");
+  if (root_path == NULL)
+    root_path = "..";
+
+  gchar *model_file = g_build_filename (root_path, "build", "tests",
+      "nnstreamer_example", "libnnstreamer_customfilter_passthrough.so", NULL);
+  ASSERT_TRUE (g_file_test (model_file, G_FILE_TEST_EXISTS));
+
+  /* create a nnstreamer pipeline */
+  pipeline = g_strdup_printf (
+    "videotestsrc num-buffers=3 ! videoconvert ! video/x-raw,width=160,height=120,format=RGB,framerate=10/1 ! "
+        "tensor_converter ! tensor_filter name=test_filter framework=custom invoke-dynamic=TRUE model=%s ! tensor_sink sync=true", model_file);
+
+  gstpipe = gst_parse_launch (pipeline, &err);
+  ASSERT_TRUE (gstpipe != nullptr);
+
+  filter_received = 0;
+  EXPECT_NE (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+  g_usleep (100000);
+
+  gst_object_unref (gstpipe);
+  g_free (pipeline);
+}
+
+/**
+ * @brief Test dynamic invoke with invalid param.
+ * @todo Enable the test after development is done.
+ */
+TEST (tensorFilterCustom, DISABLED_dynamicRegisterInvalidParam_n)
+{
+  GstTensorsInfo info_in;
+  int ret;
+
+  gst_tensors_info_init (&info_in);
+  info_in.num_tensors = 1U;
+  info_in.info[0].name = NULL;
+  info_in.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
+
+  ret = NNS_custom_easy_dynamic_register (
+      NULL, _custom_easy_filter_dynamic, NULL, &info_in);
+  EXPECT_NE (0, ret);
+
+  ret = NNS_custom_easy_dynamic_register (
+      "temp_name", NULL, NULL, &info_in);
+  EXPECT_NE (0, ret);
+
+  ret = NNS_custom_easy_dynamic_register (
+      "temp_name", _custom_easy_filter_dynamic, NULL, NULL);
+  EXPECT_NE (0, ret);
+}
+
+/**
+ * @brief Main gtest
+ */
+int
+main (int argc, char **argv)
+{
+  int result = -1;
+
+  try {
+    testing::InitGoogleTest (&argc, argv);
+  } catch (...) {
+    g_warning ("catch 'testing::internal::<unnamed>::ClassUniqueToAlwaysTrue'");
+  }
+
+  gst_init (&argc, &argv);
+
+  try {
+    result = RUN_ALL_TESTS ();
+  } catch (...) {
+    g_warning ("catch `testing::internal::GoogleTestFailureException`");
+  }
+
+  return result;
+}