[subplugin/ext] Added support for setInputDim and reloadModel in unittests
authorParichay Kapoor <pk.kapoor@samsung.com>
Tue, 24 Dec 2019 08:31:26 +0000 (17:31 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Wed, 19 Feb 2020 10:36:22 +0000 (02:36 -0800)
Added check for setInputDim and reloadModel in unittests
Correspondingly two sets of unittests are now generated using getDim or setDim (when possible)
Error values for tflite and python subplugin are updated
Added more error checks

Signed-off-by: Parichay Kapoor <pk.kapoor@samsung.com>
ext/nnstreamer/tensor_filter/tensor_filter_nnfw.c
ext/nnstreamer/tensor_filter/tensor_filter_python.cc
ext/nnstreamer/tensor_filter/tensor_filter_tensorflow_lite.cc
gst/nnstreamer/tensor_filter/tensor_filter_custom.c
tests/nnstreamer_filter_extensions_common/meson.build
tests/nnstreamer_filter_extensions_common/unittest_tizen_template.cc.in

index 36465a8..0d5cb09 100644 (file)
@@ -506,6 +506,9 @@ nnfw_setInputDim (const GstTensorFilterProperties * prop, void **private_data,
   if (in_info->num_tensors != pdata->in_info.num_tensors)
     return -EPERM;
 
+  /** Return -ENOENT till nnfw supports set input dimension internally */
+  return -ENOENT;
+
   for (idx = 0; idx < pdata->in_info.num_tensors; idx ++) {
     err = nnfw_tensor_info_set (pdata, in_info, idx);
     if (err)
index d7dde57..e44c29e 100644 (file)
@@ -779,7 +779,7 @@ py_setInputDim (const GstTensorFilterProperties * prop, void **private_data,
   g_return_val_if_fail (core && in_info && out_info, -EINVAL);
 
   if (core->getCbType () != CB_SETDIM) {
-    return -EINVAL;
+    return -ENOENT;
   }
 
   return core->setInputTensorDim (in_info, out_info);
@@ -799,7 +799,7 @@ py_getInputDim (const GstTensorFilterProperties * prop, void **private_data,
   g_return_val_if_fail (core && info, -EINVAL);
 
   if (core->getCbType () != CB_GETDIM) {
-    return -EINVAL;
+    return -ENOENT;
   }
 
   return core->getInputTensorDim (info);
@@ -819,7 +819,7 @@ py_getOutputDim (const GstTensorFilterProperties * prop, void **private_data,
   g_return_val_if_fail (core && info, -EINVAL);
 
   if (core->getCbType () != CB_GETDIM) {
-    return -EINVAL;
+    return -ENOENT;
   }
 
   return core->getOutputTensorDim (info);
index 83c9de3..7dd088d 100644 (file)
@@ -445,7 +445,7 @@ TFLiteInterpreter::setInputTensorsInfo (const GstTensorsInfo * info)
      */
     input_rank = gst_tensor_info_get_rank (&info->info[tensor_idx]);
     for (int rank = NNS_TENSOR_RANK_LIMIT; rank >= input_rank; rank--) {
-                       std::vector<int> dims(rank);
+      std::vector<int> dims(rank);
       /* the order of dimension is reversed at CAPS negotiation */
       for (int idx = 0; idx < rank; ++idx) {
         /** check overflow when storing uint32_t in int container */
@@ -463,12 +463,12 @@ TFLiteInterpreter::setInputTensorsInfo (const GstTensorsInfo * info)
 
     /** return error when none of the ranks worked */
     if (status != kTfLiteOk)
-      return -EINVAL;
+      return -EPERM;
   }
 
   status = interpreter->AllocateTensors ();
   if (status != kTfLiteOk)
-    return -EINVAL;
+    return -EPERM;
 
   return 0;
 }
@@ -910,6 +910,8 @@ tflite_setInputDim (const GstTensorFilterProperties * prop, void **private_data,
   int status;
 
   g_return_val_if_fail (core, -EINVAL);
+  g_return_val_if_fail (in_info, -EINVAL);
+  g_return_val_if_fail (out_info, -EINVAL);
 
   /** get current input tensor info for resetting */
   status = core->getInputTensorDim (&cur_in_info);
index b48279f..19332e2 100644 (file)
@@ -176,7 +176,7 @@ custom_getInputDim (const GstTensorFilterProperties * prop, void **private_data,
   g_return_val_if_fail (info != NULL, -EINVAL);
 
   if (ptr->methods->getInputDim == NULL)
-    return -EINVAL;
+    return -ENOENT;
 
   return ptr->methods->getInputDim (ptr->customFW_private_data, prop, info);
 }
@@ -195,7 +195,7 @@ custom_getOutputDim (const GstTensorFilterProperties * prop,
   g_return_val_if_fail (info != NULL, -EINVAL);
 
   if (ptr->methods->getOutputDim == NULL)
-    return -EINVAL;
+    return -ENOENT;
 
   return ptr->methods->getOutputDim (ptr->customFW_private_data, prop, info);
 }
@@ -215,7 +215,7 @@ custom_setInputDim (const GstTensorFilterProperties * prop, void **private_data,
   g_return_val_if_fail (out_info != NULL, -EINVAL);
 
   if (ptr->methods->setInputDim == NULL)
-    return -EINVAL;
+    return -ENOENT;
 
   return ptr->methods->setInputDim (ptr->customFW_private_data,
       prop, in_info, out_info);
index 5c2cddb..33e9bc0 100644 (file)
@@ -3,40 +3,42 @@ tizen_apptest_deps = [
   glib_dep
 ]
 
-# TODO: enable for python
-
 # Format for adding subplugin into extensions -
-# [name, extension abbreviation, dependencies, model file name/folder path/file path]
+# [name, extension abbreviation, dependencies, model file name/folder path/file path, test name]
 extensions = []
 
-extensions += [['custom', 'custom', nnstreamer_unittest_deps, '../build/nnstreamer_example/custom_example_passthrough/libnnstreamer_customfilter_passthrough.so']]
+extensions += [['custom', 'custom', nnstreamer_unittest_deps, '../build/nnstreamer_example/custom_example_passthrough/libnnstreamer_customfilter_passthrough.so', 'custom']]
+extensions += [['custom', 'custom', nnstreamer_unittest_deps, '../build/nnstreamer_example/custom_example_passthrough/libnnstreamer_customfilter_passthrough_variable.so', 'custom-set']]
 
 if get_option('enable-tensorflow-lite')
-  extensions += [['tensorflow-lite', 'tflite', nnstreamer_filter_tflite_deps, 'mobilenet_v1_1.0_224_quant.tflite']]
+  extensions += [['tensorflow-lite', 'tflite', nnstreamer_filter_tflite_deps, 'mobilenet_v1_1.0_224_quant.tflite', 'tensorflow_lite']]
+  extensions += [['tensorflow-lite', 'tflite', nnstreamer_filter_tflite_deps, 'add.tflite', 'tensorflow_lite-set']]
 endif
 
 if get_option('enable-tensorflow')
-  extensions += [['tensorflow', 'tf', nnstreamer_filter_tf_deps, 'mnist.pb']]
+  extensions += [['tensorflow', 'tf', nnstreamer_filter_tf_deps, 'mnist.pb', 'tensorflow']]
 endif
 
 if get_option('enable-nnfw-runtime')
-  extensions += [['nnfw', 'nnfw', nnstreamer_filter_nnfw_deps, 'add.tflite']]
+  extensions += [['nnfw', 'nnfw', nnstreamer_filter_nnfw_deps, 'add.tflite', 'nnfw']]
 endif
 
 if get_option('enable-pytorch')
-  extensions += [['pytorch', 'torch', nnstreamer_filter_torch_deps, 'pytorch_lenet5.pt']]
+  extensions += [['pytorch', 'torch', nnstreamer_filter_torch_deps, 'pytorch_lenet5.pt', 'pytorch']]
 endif
 
 if get_option('enable-caffe2')
-  extensions += [['caffe2', 'caffe2', nnstreamer_filter_caffe2_deps, 'caffe2_init_net.pb,caffe2_predict_net.pb']]
+  extensions += [['caffe2', 'caffe2', nnstreamer_filter_caffe2_deps, 'caffe2_init_net.pb,caffe2_predict_net.pb', 'caffe2']]
 endif
 
 if have_python2
-  extensions += [['python2', 'python2', nnstreamer_filter_python2_deps, 'passthrough.py']]
+  extensions += [['python2', 'python2', nnstreamer_filter_python2_deps, 'passthrough.py', 'python2-get']]
+  extensions += [['python2', 'python2', nnstreamer_filter_python2_deps, 'scaler.py', 'python2-set']]
 endif
 
 if have_python3
-  extensions += [['python3', 'python3', nnstreamer_filter_python3_deps, 'passthrough.py']]
+  extensions += [['python3', 'python3', nnstreamer_filter_python3_deps, 'passthrough.py', 'python3-get']]
+  extensions += [['python3', 'python3', nnstreamer_filter_python3_deps, 'scaler.py', 'python3-set']]
 endif
 
 sed_command = find_program('sed', required: true)
@@ -44,7 +46,7 @@ ext_test_template_prefix = 'unittest_tizen_'
 ext_test_template = files (ext_test_template_prefix + 'template.cc.in')
 
 foreach ext : extensions
-  ext_test_path_each = ext_test_template_prefix + ext[0] + '.cc'
+  ext_test_path_each = ext_test_template_prefix + ext[4] + '.cc'
 
   sed_ext_name_option = 's|@EXT_NAME@|' + ext[0] + '|'
   sed_ext_abbrv_option = 's|@EXT_ABBRV@|' + ext[1] + '|'
@@ -61,12 +63,12 @@ foreach ext : extensions
   )
 
   exec = executable(
-    ext_test_template_prefix + ext[0],
+    ext_test_template_prefix + ext[4],
     ext_test_each,
     dependencies: [tizen_apptest_deps, ext[2]],
     install: get_option('install-test'),
     install_dir: unittest_install_dir
   )
 
-  test(ext_test_template_prefix + ext[0], exec, args: ['--gst-plugin-path=../..'])
+  test(ext_test_template_prefix + ext[4], exec, args: ['--gst-plugin-path=../..'])
 endforeach
index 68eff04..4189231 100644 (file)
@@ -170,15 +170,12 @@ TEST (nnstreamer_@EXT_ABBRV@_basic_functions, get_dimension_fail_n)
     ret = sp->open (&prop, &data);
     EXPECT_EQ (ret, 0);
 
-    /** python subplugin modifies methods based on model file */
-    if (sp->getInputDimension && sp->getOutputDimension) {
-      /** get input/output dimension unsuccessfully */
-      ret = sp->getInputDimension (&prop, &data, NULL);
-      EXPECT_NE (ret, 0);
-
-      ret = sp->getOutputDimension (&prop, &data, NULL);
-      EXPECT_NE (ret, 0);
-    }
+    /** get input/output dimension unsuccessfully */
+    ret = sp->getInputDimension (&prop, &data, NULL);
+    EXPECT_NE (ret, 0);
+
+    ret = sp->getOutputDimension (&prop, &data, NULL);
+    EXPECT_NE (ret, 0);
 
     sp->close (&prop, &data);
   }
@@ -212,22 +209,156 @@ TEST (nnstreamer_@EXT_ABBRV@_basic_functions, get_dimension)
   ret = sp->open (&prop, &data);
   EXPECT_EQ (ret, 0);
 
-  /** python subplugin modifies methods based on model file */
+  /** get input/output dimension successfully */
   if (sp->getInputDimension && sp->getOutputDimension) {
-    /** get input/output dimension successfully */
     gst_tensors_info_init (&res);
     ret = sp->getInputDimension (&prop, &data, &res);
     gst_tensors_info_free (&res);
-    EXPECT_EQ (ret, 0);
+    EXPECT_TRUE (ret == 0 || ret == -ENOENT);
 
     gst_tensors_info_init (&res);
     ret = sp->getOutputDimension (&prop, &data, &res);
     gst_tensors_info_free (&res);
+    EXPECT_TRUE (ret == 0 || ret == -ENOENT);
+  }
+
+  sp->close (&prop, &data);
+
+  g_strfreev (model_files);
+}
+
+/**
+ * @brief Set input dimensions with @EXT_ABBRV@ subplugin
+ */
+TEST (nnstreamer_@EXT_ABBRV@_basic_functions, set_dimension_fail_n)
+{
+  int ret;
+  void *data = NULL;
+  GstTensorsInfo set;
+  gchar **model_files;
+
+  model_files = get_model_files ();
+  ASSERT_TRUE (model_files != nullptr);
+
+  GstTensorFilterProperties prop = {
+    .fwname = "@EXT_NAME@",
+    .fw_opened = 0,
+    .model_files = const_cast<const char **>(model_files),
+    .num_models = (int) g_strv_length (model_files),
+
+  };
+
+  const GstTensorFilterFramework *sp = nnstreamer_filter_find ("@EXT_NAME@");
+  EXPECT_NE (sp, (void *) NULL);
+
+  if (sp->setInputDimension) {
+    /** set input dimension without open */
+    ret = sp->setInputDimension (&prop, &data, &set, &set);
+    EXPECT_NE (ret, 0);
+
+    ret = sp->open (&prop, &data);
     EXPECT_EQ (ret, 0);
+
+    gst_tensors_info_init (&set);
+
+    /** set input dimension unsuccessfully */
+    ret = sp->setInputDimension (&prop, &data, NULL, &set);
+    EXPECT_NE (ret, 0);
+    ret = sp->setInputDimension (&prop, &data, &set, NULL);
+    EXPECT_NE (ret, 0);
+
+    gst_tensors_info_free (&set);
+
+    sp->close (&prop, &data);
+  }
+
+  g_strfreev (model_files);
+
+}
+
+/**
+ * @brief Set input dimensions with @EXT_ABBRV@ subplugin
+ */
+TEST (nnstreamer_@EXT_ABBRV@_basic_functions, set_dimension)
+{
+  int ret;
+  void *data = NULL;
+  GstTensorsInfo set, get;
+  gchar **model_files;
+
+  model_files = get_model_files ();
+  ASSERT_TRUE (model_files != nullptr);
+
+  GstTensorFilterProperties prop = {
+    .fwname = "@EXT_NAME@",
+    .fw_opened = 0,
+    .model_files = const_cast<const char **>(model_files),
+    .num_models = (int) g_strv_length (model_files),
+
+  };
+
+  const GstTensorFilterFramework *sp = nnstreamer_filter_find ("@EXT_NAME@");
+  EXPECT_NE (sp, (void *) NULL);
+
+  ret = sp->open (&prop, &data);
+  EXPECT_EQ (ret, 0);
+
+  gst_tensors_info_init (&set);
+
+  /** Get the current input dimension, if allowed */
+  ret = -EINVAL;
+  if (sp->getInputDimension) {
+    ret = sp->getInputDimension (&prop, &data, &set);
+    EXPECT_TRUE (ret == 0 || ret == -ENOENT);
+  }
+
+  /** Set the dimension to be set in the framework */
+  if (ret == 0) {
+    /** if get input dimension was success */
+    set.info[0].dimension[0] *= 2;
+    set.info[0].dimension[2] *= 2;
+  } else {
+    set.num_tensors = 1;
+    set.info[0].type = _NNS_INT8;
+    set.info[0].dimension[0] = 10;
+    set.info[0].dimension[1] = 9;
+    set.info[0].dimension[2] = 8;
+    set.info[0].dimension[3] = 7;
+  }
+
+  if (sp->setInputDimension) {
+    /** set input dimension successfully */
+    ret = sp->setInputDimension (&prop, &data, &set, &set);
+    /**
+     * EPERM - changing the input dimension is not permitted for the loaded model
+     * ENOENT - setInputDimension operation is not supported by the framework
+     */
+    if (ret != -EPERM && ret != -ENOENT) {
+      EXPECT_EQ (ret, 0);
+    }
+
+    /** if set was successful and getInputDimension is supported, verify */
+    if (sp->getInputDimension && ret != -EPERM && ret != -ENOENT) {
+      /** use get dimension to verify the set values, if entry exists */
+      gst_tensors_info_init (&get);
+      ret = sp->getInputDimension (&prop, &data, &get);
+      EXPECT_TRUE (ret == 0 || ret == -ENOENT);
+
+      if (ret == 0) {
+        EXPECT_EQ (set.num_tensors, get.num_tensors);
+        EXPECT_EQ (set.info[0].type, get.info[0].type);
+        EXPECT_EQ (set.info[0].dimension[0], get.info[0].dimension[0]);
+        EXPECT_EQ (set.info[0].dimension[1], get.info[0].dimension[1]);
+        EXPECT_EQ (set.info[0].dimension[2], get.info[0].dimension[2]);
+        EXPECT_EQ (set.info[0].dimension[3], get.info[0].dimension[3]);
+      }
+      gst_tensors_info_free (&get);
+    }
   }
 
   sp->close (&prop, &data);
 
+  gst_tensors_info_free (&set);
   g_strfreev (model_files);
 }
 
@@ -288,6 +419,7 @@ TEST (nnstreamer_@EXT_ABBRV@_basic_functions, invoke_fail_n)
 TEST (nnstreamer_@EXT_ABBRV@_basic_functions, invoke)
 {
   int ret;
+  int ret_get_in, ret_get_out, ret_set_in;
   void *data = NULL;
   GstTensorMemory input[NNS_TENSOR_SIZE_LIMIT] = { 0, };
   GstTensorMemory output[NNS_TENSOR_SIZE_LIMIT] = { 0, };
@@ -308,29 +440,52 @@ TEST (nnstreamer_@EXT_ABBRV@_basic_functions, invoke)
   const GstTensorFilterFramework *sp = nnstreamer_filter_find ("@EXT_NAME@");
   EXPECT_NE (sp, (void *) NULL);
 
+  EXPECT_TRUE ((sp->getInputDimension && sp->getOutputDimension) ||
+      (sp->setInputDimension));
+
   ret = sp->open (&prop, &data);
   EXPECT_EQ (ret, 0);
 
-  ASSERT_TRUE ((sp->getInputDimension && sp->getOutputDimension) ||
-      (sp->setInputDimension));
-
   /** Decide the size for input and output */
   gst_tensors_info_init (&input_info);
   gst_tensors_info_init (&output_info);
 
+  /** if get dimension is supported, try it */
+  ret_get_in = ret_get_out = -EINVAL;
   if (sp->getInputDimension && sp->getOutputDimension) {
-    ret = sp->getInputDimension (&prop, &data, &input_info);
-    EXPECT_EQ (ret, 0);
+    ret_get_in = sp->getInputDimension (&prop, &data, &input_info);
+    EXPECT_TRUE (ret_get_in == 0 || ret_get_in == -ENOENT);
+    ret_get_out = sp->getOutputDimension (&prop, &data, &output_info);
+    EXPECT_TRUE (ret_get_out == 0 || ret_get_out == -ENOENT);
+  }
 
-    ret = sp->getOutputDimension (&prop, &data, &output_info);
-    EXPECT_EQ (ret, 0);
-  } else {
-    input_info.num_tensors = output_info.num_tensors = 1;
-    input_info.info[0].type = output_info.info[0].type = _NNS_FLOAT32;
-    input_info.info[0].dimension[0] = output_info.info[0].dimension[0] = 10;
-    input_info.info[0].dimension[1] = output_info.info[0].dimension[1] = 1;
-    input_info.info[0].dimension[2] = output_info.info[0].dimension[2] = 1;
-    input_info.info[0].dimension[3] = output_info.info[0].dimension[3] = 1;
+  /** if get dimension was not supported, use set dimension */
+  if (ret_get_in != 0 || ret_get_out != 0 ||
+      !gst_tensors_info_validate (&input_info) ||
+      !gst_tensors_info_validate (&output_info)) {
+    if (sp->setInputDimension) {
+      GstTensorsInfo res;
+
+      gst_tensors_info_init (&res);
+      res.num_tensors = 1;
+      res.info[0].type = _NNS_FLOAT32;
+      res.info[0].dimension[0] = 10;
+      res.info[0].dimension[1] = 1;
+      res.info[0].dimension[2] = 1;
+      res.info[0].dimension[3] = 1;
+
+      /** As get dimension failed, set dimension must pass */
+      ret_set_in = sp->setInputDimension (&prop, &data, &res, &res);
+      EXPECT_EQ (ret_set_in, 0);
+
+      input_info.num_tensors = output_info.num_tensors = 1;
+      input_info.info[0].type = output_info.info[0].type = _NNS_FLOAT32;
+      input_info.info[0].dimension[0] = output_info.info[0].dimension[0] = 10;
+      input_info.info[0].dimension[1] = output_info.info[0].dimension[1] = 1;
+      input_info.info[0].dimension[2] = output_info.info[0].dimension[2] = 1;
+      input_info.info[0].dimension[3] = output_info.info[0].dimension[3] = 1;
+      gst_tensors_info_free (&res);
+    }
   }
 
   for (i = 0; i < input_info.num_tensors; ++i) {
@@ -347,9 +502,8 @@ TEST (nnstreamer_@EXT_ABBRV@_basic_functions, invoke)
 
   /** should never crash */
   ret = sp->invoke_NN (&prop, &data, input, output);
-  /** should be successful for single input/output case */
-  if (sp->getInputDimension && sp->getOutputDimension &&
-      input_info.num_tensors > 0 && output_info.num_tensors > 0) {
+  if (input_info.num_tensors != 0 && output_info.num_tensors != 0) {
+    /** should be successful for valid input/output case */
     EXPECT_EQ (ret, 0);
   }
 
@@ -369,6 +523,58 @@ TEST (nnstreamer_@EXT_ABBRV@_basic_functions, invoke)
 }
 
 /**
+ * @brief Reload model with @EXT_ABBRV@ subplugin
+ */
+TEST (nnstreamer_@EXT_ABBRV@_basic_functions, reload_model)
+{
+  int ret;
+  void *data = NULL;
+  gchar **model_files;
+  GstTensorMemory input, output;
+
+  model_files = get_model_files ();
+  ASSERT_TRUE (model_files != nullptr);
+
+  GstTensorFilterProperties prop = {
+    .fwname = "@EXT_NAME@",
+    .fw_opened = 0,
+    .model_files = const_cast<const char **>(model_files),
+    .num_models = (int) g_strv_length (model_files),
+  };
+
+  const GstTensorFilterFramework *sp = nnstreamer_filter_find ("@EXT_NAME@");
+  EXPECT_NE (sp, (void *) NULL);
+
+  if (sp->reloadModel) {
+    /** reload model without open */
+    ret = sp->reloadModel (&prop, &data);
+    EXPECT_NE (ret, 0);
+
+    ret = sp->open (&prop, &data);
+    EXPECT_EQ (ret, 0);
+
+    /** opening the same model again */
+    ret = sp->reloadModel (&prop, &data);
+    EXPECT_EQ (ret, 0);
+
+    output.type = input.type = _NNS_FLOAT32;
+    output.size = input.size = gst_tensor_get_element_size (input.type) * 10;
+    input.data = g_malloc(input.size);
+    output.data = g_malloc(output.size);
+
+    /** should not crash after reload */
+    ret = sp->invoke_NN (&prop, &data, &input, &output);
+
+    g_free (input.data);
+    g_free (output.data);
+
+    sp->close (&prop, &data);
+  }
+
+  g_strfreev (model_files);
+}
+
+/**
  * @brief Main gtest
  */
 int