[test/snpe] Add unittest for SNPE subplugin
authorYongjoo Ahn <yongjoo1.ahn@samsung.com>
Thu, 16 Sep 2021 08:09:04 +0000 (17:09 +0900)
committerjaeyun-jung <39614140+jaeyun-jung@users.noreply.github.com>
Tue, 21 Sep 2021 01:21:01 +0000 (10:21 +0900)
- Add unittest for SNPE subplugin.
- Add a sample model file `add2_float.dlc` for the test.
It takes a single float value and returns a single float value
with plus 2.0.

Signed-off-by: Yongjoo Ahn <yongjoo1.ahn@samsung.com>
tests/meson.build
tests/nnstreamer_filter_snpe/unittest_filter_snpe.cc [new file with mode: 0644]
tests/test_models/models/add2_float.dlc [new file with mode: 0644]

index 967dbcd..c7be319 100644 (file)
@@ -210,6 +210,18 @@ if gtest_dep.found()
     test('unittest_filter_lua', unittest_filter_lua, env: testenv)
   endif
 
+  # SNPE unittest
+  if snpe_support_is_available
+    unittest_filter_snpe = executable('unittest_filter_snpe',
+      join_paths('nnstreamer_filter_snpe', 'unittest_filter_snpe.cc'),
+      dependencies: [unittest_util_dep, glib_dep, gst_dep, nnstreamer_dep, gtest_dep],
+      install: get_option('install-test'),
+      install_dir: unittest_install_dir
+    )
+
+    test('unittest_filter_snpe', unittest_filter_snpe, env: testenv)
+  endif
+
   # Run unittest_decoder
   if flatbuf_support_is_available
     unittest_decoder = executable('unittest_decoder',
diff --git a/tests/nnstreamer_filter_snpe/unittest_filter_snpe.cc b/tests/nnstreamer_filter_snpe/unittest_filter_snpe.cc
new file mode 100644 (file)
index 0000000..53a75ca
--- /dev/null
@@ -0,0 +1,360 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/**
+ * @file    unittest_filter_snpe.cc
+ * @date    16 Sep 2021
+ * @brief   Unit test for snpe tensor filter sub-plugin
+ * @author  Yongjoo Ahn <yongjoo1.ahn@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 <tensor_common.h>
+#include <nnstreamer_plugin_api_filter.h>
+#include <nnstreamer_util.h>
+
+/**
+ * @brief internal function to get model filename
+ */
+static gboolean
+_GetModelFilePath (gchar ** model_file)
+{
+  const gchar *src_root = g_getenv ("NNSTREAMER_SOURCE_ROOT_PATH");
+  gchar *root_path = src_root ? g_strdup (src_root) : g_get_current_dir ();
+  const gchar model_name[] = "add2_float.dlc";
+
+  *model_file = g_build_filename (
+      root_path, "tests", "test_models", "models", model_name, NULL);
+
+  g_free (root_path);
+
+  return g_file_test (*model_file, G_FILE_TEST_EXISTS);
+}
+
+/**
+ * @brief Set tensor filter properties
+ */
+static void
+_SetFilterProp (GstTensorFilterProperties *prop, const gchar *name, const gchar **models)
+{
+  memset (prop, 0, sizeof (GstTensorFilterProperties));
+  prop->fwname = name;
+  prop->fw_opened = 0;
+  prop->model_files = models;
+  prop->num_models = g_strv_length ((gchar **) models);
+}
+
+/**
+ * @brief Positive case with successful getModelInfo
+ */
+TEST (nnstreamerFilterSnpe, getModelInfo00)
+{
+  int ret;
+  void *data = NULL;
+  gchar *model_file;
+  GstTensorFilterProperties prop;
+  ASSERT_TRUE (_GetModelFilePath (&model_file));
+
+  const gchar *model_files[] = {
+    model_file,
+    NULL,
+  };
+
+  const GstTensorFilterFramework *sp = nnstreamer_filter_find ("snpe");
+  ASSERT_TRUE (sp != nullptr);
+  _SetFilterProp (&prop, "snpe", model_files);
+
+  ret = sp->open (&prop, &data);
+  EXPECT_EQ (ret, 0);
+
+  GstTensorsInfo in_info, out_info;
+
+  ret = sp->getModelInfo (NULL, NULL, data, GET_IN_OUT_INFO, &in_info, &out_info);
+  EXPECT_EQ (ret, 0);
+
+  EXPECT_EQ (in_info.num_tensors, 1U);
+  EXPECT_EQ (in_info.info[0].dimension[0], 1U);
+  EXPECT_EQ (in_info.info[0].dimension[1], 1U);
+  EXPECT_EQ (in_info.info[0].dimension[2], 1U);
+  EXPECT_EQ (in_info.info[0].dimension[3], 1U);
+  EXPECT_EQ (in_info.info[0].type, _NNS_FLOAT32);
+
+  EXPECT_EQ (out_info.num_tensors, 1U);
+  EXPECT_EQ (out_info.info[0].dimension[0], 1U);
+  EXPECT_EQ (out_info.info[0].dimension[1], 1U);
+  EXPECT_EQ (out_info.info[0].dimension[2], 1U);
+  EXPECT_EQ (out_info.info[0].dimension[3], 1U);
+  EXPECT_EQ (out_info.info[0].type, _NNS_FLOAT32);
+
+  sp->close (&prop, &data);
+  g_free (model_file);
+}
+
+/**
+ * @brief Negative case calling getModelInfo before open
+ */
+TEST (nnstreamerFilterSnpe, getModelInfo01_n)
+{
+  int ret;
+  void *data = NULL;
+  gchar *model_file;
+  GstTensorFilterProperties prop;
+  ASSERT_TRUE (_GetModelFilePath (&model_file));
+
+  const gchar *model_files[] = {
+    model_file,
+    NULL,
+  };
+
+  const GstTensorFilterFramework *sp = nnstreamer_filter_find ("snpe");
+  ASSERT_TRUE (sp != nullptr);
+
+  GstTensorsInfo in_info, out_info;
+
+  ret = sp->getModelInfo (NULL, NULL, data, SET_INPUT_INFO, &in_info, &out_info);
+  EXPECT_NE (ret, 0);
+  _SetFilterProp (&prop, "snpe", model_files);
+
+  sp->close (&prop, &data);
+  g_free (model_file);
+}
+
+/**
+ * @brief Negative case with invalid argument
+ */
+TEST (nnstreamerFilterSnpe, getModelInfo02_n)
+{
+  int ret;
+  void *data = NULL;
+  gchar *model_file;
+  GstTensorFilterProperties prop;
+  ASSERT_TRUE (_GetModelFilePath (&model_file));
+
+  const gchar *model_files[] = {
+    model_file,
+    NULL,
+  };
+
+  const GstTensorFilterFramework *sp = nnstreamer_filter_find ("snpe");
+  ASSERT_TRUE (sp != nullptr);
+  _SetFilterProp (&prop, "snpe", model_files);
+
+  ret = sp->open (&prop, &data);
+  EXPECT_EQ (ret, 0);
+  sp->close (&prop, &data);
+
+  GstTensorsInfo in_info, out_info;
+
+  /* not supported */
+  ret = sp->getModelInfo (NULL, NULL, data, SET_INPUT_INFO, &in_info, &out_info);
+  EXPECT_NE (ret, 0);
+
+  sp->close (&prop, &data);
+  g_free (model_file);
+}
+
+/**
+ * @brief Test snpe subplugin with successful invoke for sample dlc model
+ */
+TEST (nnstreamerFilterSnpe, invoke00)
+{
+  int ret;
+  void *data = NULL;
+  GstTensorMemory input, output;
+  gchar *model_file;
+  GstTensorFilterProperties prop;
+
+  ASSERT_TRUE (_GetModelFilePath (&model_file));
+  const gchar *model_files[] = {
+    model_file,
+    NULL,
+  };
+
+  const GstTensorFilterFramework *sp = nnstreamer_filter_find ("snpe");
+  ASSERT_TRUE (sp != nullptr);
+  _SetFilterProp (&prop, "snpe", model_files);
+
+  output.size = input.size = sizeof (float) * 1;
+  input.data = g_malloc0 (input.size);
+  output.data = g_malloc0 (output.size);
+
+  ret = sp->open (&prop, &data);
+  EXPECT_EQ (ret, 0);
+
+  /** invoke successful */
+  ret = sp->invoke (NULL, NULL, data, &input, &output);
+  EXPECT_EQ (ret, 0);
+  EXPECT_EQ (static_cast<float *> (output.data)[0], 2.0);
+
+  static_cast<float *> (input.data)[0] = 10.0;
+  ret = sp->invoke (NULL, NULL, data, &input, &output);
+  EXPECT_EQ (ret, 0);
+  EXPECT_EQ (static_cast<float *> (output.data)[0], 12.0);
+
+  static_cast<float *> (input.data)[0] = 1.0;
+  ret = sp->invoke (NULL, NULL, data, &input, &output);
+  EXPECT_EQ (ret, 0);
+  EXPECT_EQ (static_cast<float *> (output.data)[0], 3.0);
+
+  g_free (input.data);
+  g_free (output.data);
+  sp->close (&prop, &data);
+  g_free (model_file);
+}
+
+/**
+ * @brief Negative case with invalid input/output
+ */
+TEST (nnstreamerFilterSnpe, invoke01_n)
+{
+  int ret;
+  void *data = NULL;
+  GstTensorMemory input, output;
+  gchar *model_file;
+  GstTensorFilterProperties prop;
+
+  ASSERT_TRUE (_GetModelFilePath (&model_file));
+  const gchar *model_files[] = {
+    model_file,
+    NULL,
+  };
+
+  const GstTensorFilterFramework *sp = nnstreamer_filter_find ("snpe");
+  ASSERT_TRUE (sp != nullptr);
+  _SetFilterProp (&prop, "snpe", model_files);
+
+  output.size = input.size = sizeof (float) * 1;
+  input.data = g_malloc0 (input.size);
+  output.data = g_malloc0 (output.size);
+
+  ret = sp->open (&prop, &data);
+  EXPECT_EQ (ret, 0);
+
+  /* catching assertion error */
+  EXPECT_DEATH (sp->invoke (NULL, NULL, data, NULL, &output), "");
+  EXPECT_DEATH (sp->invoke (NULL, NULL, data, &input, NULL), "");
+
+  g_free (input.data);
+  g_free (output.data);
+  sp->close (&prop, &data);
+  g_free (model_file);
+}
+
+/**
+ * @brief Negative test case with invalid private_data
+ */
+TEST (nnstreamerFilterSnpe, invoke02_n)
+{
+  int ret;
+  void *data = NULL;
+  GstTensorMemory input, output;
+  gchar *model_file;
+  GstTensorFilterProperties prop;
+
+  ASSERT_TRUE (_GetModelFilePath (&model_file));
+  const gchar *model_files[] = {
+    model_file,
+    NULL,
+  };
+
+  const GstTensorFilterFramework *sp = nnstreamer_filter_find ("snpe");
+  ASSERT_TRUE (sp != nullptr);
+  _SetFilterProp (&prop, "snpe", model_files);
+
+  ret = sp->open (&prop, &data);
+  EXPECT_EQ (ret, 0);
+  EXPECT_NE (data, nullptr);
+
+  output.size = input.size = sizeof (float) * 1;
+  input.data = g_malloc0 (input.size);
+  output.data = g_malloc0 (output.size);
+
+  /* unsuccessful invoke with NULL priv_data */
+  ret = sp->invoke (NULL, NULL, NULL, &input, &output);
+  EXPECT_NE (ret, 0);
+
+  g_free (model_file);
+  g_free (input.data);
+  g_free (output.data);
+  sp->close (&prop, &data);
+}
+
+/**
+ * @brief Positive case to launch gst pipeline
+ */
+TEST (nnstreamerFilterSnpe, launch00)
+{
+  gchar *pipeline;
+  GstElement *gstpipe;
+  GError *err = NULL;
+  gchar *model_file;
+  ASSERT_TRUE (_GetModelFilePath (&model_file));
+
+  /* create a nnstreamer pipeline */
+  pipeline = g_strdup_printf ("videotestsrc num-buffers=1 ! videoconvert ! videoscale ! video/x-raw,format=GRAY8,width=1,height=1 ! tensor_converter ! tensor_transform mode=typecast option=float32 ! tensor_filter framework=snpe model=\"%s\" ! tensor_sink name=sink",
+      model_file);
+
+  gstpipe = gst_parse_launch (pipeline, &err);
+  ASSERT_TRUE (gstpipe != nullptr);
+
+  EXPECT_EQ (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+  EXPECT_EQ (setPipelineStateSync (gstpipe, GST_STATE_NULL, UNITTEST_STATECHANGE_TIMEOUT), 0);
+
+  gst_object_unref (gstpipe);
+  g_free (pipeline);
+  g_free (model_file);
+}
+
+/**
+ * @brief Negative case to launch gst pipeline: wrong input dimension
+ */
+TEST (nnstreamerFilterSnpe, launch01_n)
+{
+  gchar *pipeline;
+  GstElement *gstpipe;
+  GError *err = NULL;
+  gchar *model_file;
+  ASSERT_TRUE (_GetModelFilePath (&model_file));
+
+  /* create a nnstreamer pipeline */
+  pipeline = g_strdup_printf ("videotestsrc num-buffers=1 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=3,height=3 ! tensor_converter ! tensor_transform mode=typecast option=float32 ! tensor_filter framework=snpe model=\"%s\" ! tensor_sink name=sink",
+      model_file);
+
+  gstpipe = gst_parse_launch (pipeline, &err);
+  ASSERT_TRUE (gstpipe != nullptr);
+
+  EXPECT_NE (setPipelineStateSync (gstpipe, GST_STATE_PLAYING, UNITTEST_STATECHANGE_TIMEOUT), 0);
+
+  gst_object_unref (gstpipe);
+  g_free (pipeline);
+  g_free (model_file);
+}
+
+/**
+ * @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;
+}
diff --git a/tests/test_models/models/add2_float.dlc b/tests/test_models/models/add2_float.dlc
new file mode 100644 (file)
index 0000000..e3d8d1d
Binary files /dev/null and b/tests/test_models/models/add2_float.dlc differ