[Test] Unittest for filter shared key
authorJunhwan Kim <jejudo.kim@samsung.com>
Thu, 19 Aug 2021 08:45:10 +0000 (17:45 +0900)
committerjaeyun-jung <39614140+jaeyun-jung@users.noreply.github.com>
Wed, 28 Sep 2022 10:15:44 +0000 (19:15 +0900)
Unittest to validate filter with shared key

Signed-off-by: Junhwan Kim <jejudo.kim@samsung.com>
tests/meson.build
tests/nnstreamer_filter_shared_model/unittest_filter_shared_model.cc [new file with mode: 0644]

index b93401b..1aec936 100644 (file)
@@ -302,6 +302,17 @@ if gtest_dep.found()
   if nnstreamer_edge_support_is_available
     subdir('nnstreamer_edge')
   endif
+
+  if tflite_support_is_available
+    unittest_filter_shared_model = executable('unittest_filter_shared_model',
+      join_paths('nnstreamer_filter_shared_model', 'unittest_filter_shared_model.cc'),
+      dependencies: [nnstreamer_unittest_deps, unittest_util_dep],
+      install: get_option('install-test'),
+      install_dir: unittest_install_dir
+    )
+
+    test('unittest_filter_shared_model', unittest_filter_shared_model, timeout: 30, env: testenv)
+  endif
 endif # gtest_dep.found()
 
 tensor_filter_ext_enabled = tflite_support_is_available or \
diff --git a/tests/nnstreamer_filter_shared_model/unittest_filter_shared_model.cc b/tests/nnstreamer_filter_shared_model/unittest_filter_shared_model.cc
new file mode 100644 (file)
index 0000000..c8d60f0
--- /dev/null
@@ -0,0 +1,231 @@
+/**
+ * Copyright (C) 2021 Junhwan Kim <jejudo.kim@samsung.com>
+ *
+ * @file    unittest_filter_shared_model.cc
+ * @date    19 Aug 2021
+ * @brief   Unit test for nnstreamer filter shared model features.
+ * @author  Junhwan Kim <jejudo.kim@samsung.com>
+ * @see     http://github.com/nnstreamer/nnstreamer
+ * @bug     No known bugs
+ */
+
+#include <gtest/gtest.h>
+#include <glib.h>
+#include <gst/gst.h>
+#include <unittest_util.h>
+#include <nnstreamer_util.h>
+#include <tensor_common.h>
+
+static const gchar model_name1[] = "mobilenet_v1_1.0_224_quant.tflite";
+static const gchar model_name2[] = "mobilenet_v2_1.0_224_quant.tflite";
+static const gchar data_name[] = "orange.png";
+static const gchar shared_key[] = "mobilenet";
+static guint res[2];
+
+/**
+ * @brief callback for tensor sink to get arg max
+ */
+static void
+_new_data_cb (GstElement *element, GstBuffer *buffer, gpointer user_data)
+{
+  GstMemory *mem;
+  GstMapInfo info;
+  gsize i, max_i = 0;
+  guint8 max_val = 0;
+  gint idx = *(gint*)user_data;
+  UNUSED (element);
+
+  mem = gst_buffer_get_memory (buffer, 0);
+  ASSERT_TRUE (gst_memory_map (mem, &info, GST_MAP_READ));
+
+  for (i = 0; i < info.size; ++i) {
+    if (info.data[i] > max_val) {
+      max_val = info.data[i];
+      max_i = i;
+    }
+  }
+  res[idx] = (guint) max_i;
+  gst_memory_unmap (mem, &info);
+  gst_memory_unref (mem);
+}
+
+/**
+ * @brief helper to get base pipeline string
+ */
+static void _get_pipeline_str (gchar **str, const gchar *model1, const gchar *model2)
+{
+  const gchar *src_root = g_getenv ("NNSTREAMER_SOURCE_ROOT_PATH");
+  gchar *root_path = src_root ? g_strdup (src_root) : g_get_current_dir ();
+  gchar *model_path1 = g_build_filename (
+      root_path, "tests", "test_models", "models", model1, NULL);
+  gchar *model_path2 = g_build_filename (
+      root_path, "tests", "test_models", "models", model2, NULL);
+  gchar *image_path = g_build_filename (
+      root_path, "tests", "test_models", "data", data_name, NULL);
+
+  ASSERT_TRUE (g_file_test (model_path1, G_FILE_TEST_EXISTS));
+  ASSERT_TRUE (g_file_test (model_path2, G_FILE_TEST_EXISTS));
+  ASSERT_TRUE (g_file_test (image_path, G_FILE_TEST_EXISTS));
+
+  *str = g_strdup_printf (
+      "filesrc location=%s ! pngdec ! videoscale ! imagefreeze ! videoconvert ! "
+      "video/x-raw,format=RGB,framerate=10/1 ! tensor_converter ! tee name=t t. ! "
+      "queue ! tensor_filter name=filter1 framework=tensorflow-lite model=%s is-updatable=TRUE "
+      "shared-tensor-filter-key=%s ! tensor_sink name=sink1 t. ! "
+      "queue ! tensor_filter name=filter2 framework=tensorflow-lite model=%s is-updatable=TRUE "
+      "shared-tensor-filter-key=%s ! tensor_sink name=sink2",
+      image_path, model_path1, shared_key, model_path2, shared_key);
+  g_free (root_path);
+  g_free (model_path1);
+  g_free (model_path2);
+  g_free (image_path);
+}
+
+/**
+ * @brief Test filters share key but have different model paths.
+ */
+TEST (nnstreamerFilterSharedModel, tfliteSharedModelNotEqual_n)
+{
+  gchar *pipeline_str;
+  GstElement *pipeline;
+  _get_pipeline_str (&pipeline_str, model_name1, model_name2);
+  pipeline = gst_parse_launch (pipeline_str, NULL);
+
+  EXPECT_EQ (setPipelineStateSync (pipeline, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+  g_usleep (TEST_DEFAULT_SLEEP_TIME);
+  EXPECT_EQ (setPipelineStateSync (pipeline, GST_STATE_NULL, UNITTEST_STATECHANGE_TIMEOUT), 0);
+
+  g_free (pipeline_str);
+  gst_object_unref (pipeline);
+}
+
+/**
+ * @brief Test filter has invalid shape for shared model
+ */
+TEST (nnstreamerFilterSharedModel, tfliteInvalidShape_n)
+{
+  const gchar *src_root = g_getenv ("NNSTREAMER_SOURCE_ROOT_PATH");
+  gchar *root_path = src_root ? g_strdup (src_root) : g_get_current_dir ();
+  gchar *model_path1 = g_build_filename (
+      root_path, "tests", "test_models", "models", model_name1, NULL);
+  gchar *image_path = g_build_filename (
+      root_path, "tests", "test_models", "data", data_name, NULL);
+  gchar *pipeline_str;
+  GstElement *pipeline;
+  ASSERT_TRUE (g_file_test (model_path1, G_FILE_TEST_EXISTS));
+  ASSERT_TRUE (g_file_test (image_path, G_FILE_TEST_EXISTS));
+
+  pipeline_str = g_strdup_printf (
+      "filesrc location=%s ! pngdec ! videoscale ! imagefreeze ! videoconvert ! video/x-raw,format=RGB,framerate=0/1 ! tee name=t t. ! "
+      "queue ! videoscale ! videoconvert ! video/x-raw ! "
+      "tensor_converter ! tensor_filter name=filter1 framework=tensorflow-lite model=%s is-updatable=TRUE "
+      "shared-tensor-filter-key=%s ! tensor_sink name=sink1 t. ! "
+      "queue ! videoscale ! videoconvert ! video/x-raw,width=30,height=30 ! "
+      "tensor_converter ! tensor_filter name=filter2 framework=tensorflow-lite model=%s is-updatable=TRUE "
+      "shared-tensor-filter-key=%s ! tensor_sink name=sink2",
+      image_path, model_path1, shared_key, model_path1, shared_key);
+  g_free (root_path);
+  g_free (model_path1);
+  g_free (image_path);
+
+  pipeline = gst_parse_launch (pipeline_str, NULL);
+
+  EXPECT_NE (setPipelineStateSync (pipeline, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+  g_usleep (TEST_DEFAULT_SLEEP_TIME);
+  EXPECT_EQ (setPipelineStateSync (pipeline, GST_STATE_NULL, UNITTEST_STATECHANGE_TIMEOUT), 0);
+
+  g_free (pipeline_str);
+  gst_object_unref (pipeline);
+}
+
+/**
+ * @brief Test filters to reload new model
+ */
+TEST (nnstreamerFilterSharedModel, tfliteSharedReload)
+{
+  gchar *pipeline_str;
+  GstElement *pipeline, *filter1, *filter2, *sink1, *sink2;
+  gint idx0=0, idx1=1;
+  const gchar *src_root = g_getenv ("NNSTREAMER_SOURCE_ROOT_PATH");
+  gchar *root_path = src_root ? g_strdup (src_root) : g_get_current_dir ();
+  gchar *new_model_path = g_build_filename (
+      root_path, "tests", "test_models", "models", model_name2, NULL);
+  gchar *path;
+  g_free (root_path);
+
+  _get_pipeline_str (&pipeline_str, model_name1, model_name1);
+  pipeline = gst_parse_launch (pipeline_str, NULL);
+  g_free (pipeline_str);
+  memset (res, 0, sizeof(res));
+
+  filter1 = gst_bin_get_by_name (GST_BIN (pipeline), "filter1");
+  ASSERT_TRUE (filter1 != NULL);
+  filter2 = gst_bin_get_by_name (GST_BIN (pipeline), "filter2");
+  ASSERT_TRUE (filter2 != NULL);
+
+  sink1 = gst_bin_get_by_name (GST_BIN (pipeline), "sink1");
+  EXPECT_NE (sink1, nullptr);
+  g_signal_connect (sink1, "new-data", (GCallback) _new_data_cb, (gpointer)&idx0);
+  sink2 = gst_bin_get_by_name (GST_BIN (pipeline), "sink2");
+  EXPECT_NE (sink2, nullptr);
+  g_signal_connect (sink2, "new-data", (GCallback) _new_data_cb, (gpointer)&idx1);
+
+  EXPECT_EQ (setPipelineStateSync (pipeline, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+  g_usleep (TEST_DEFAULT_SLEEP_TIME);
+  EXPECT_EQ (setPipelineStateSync (pipeline, GST_STATE_PAUSED, UNITTEST_STATECHANGE_TIMEOUT), 0);
+  g_usleep (TEST_DEFAULT_SLEEP_TIME);
+
+  /* check two filters have same output */
+  EXPECT_NE (res[0], 0U);
+  EXPECT_EQ (res[0], res[1]);
+  memset (res, 0, sizeof(res));
+
+  /* reload filter */
+  g_object_set (filter1, "model", new_model_path, NULL);
+  g_object_get (filter1, "model", &path, NULL);
+  EXPECT_STREQ (new_model_path, path);
+  g_free (new_model_path);
+  g_free (path);
+
+  EXPECT_EQ (setPipelineStateSync (pipeline, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+  g_usleep (TEST_DEFAULT_SLEEP_TIME);
+  EXPECT_EQ (setPipelineStateSync (pipeline, GST_STATE_PAUSED, UNITTEST_STATECHANGE_TIMEOUT), 0);
+  g_usleep (TEST_DEFAULT_SLEEP_TIME);
+
+  /* same output with new model */
+  EXPECT_NE (res[0], 0U);
+  EXPECT_EQ (res[0], res[1]);
+
+  EXPECT_EQ (setPipelineStateSync (pipeline, GST_STATE_NULL, UNITTEST_STATECHANGE_TIMEOUT), 0);
+
+  gst_object_unref (filter1);
+  gst_object_unref (filter2);
+  gst_object_unref (sink1);
+  gst_object_unref (sink2);
+  gst_object_unref (pipeline);
+}
+
+/**
+ * @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;
+}