[single/caffe] Enable armnn in single-shot API
authorParichay Kapoor <pk.kapoor@samsung.com>
Wed, 29 Jan 2020 10:32:05 +0000 (19:32 +0900)
committerjaeyun-jung <39614140+jaeyun-jung@users.noreply.github.com>
Wed, 5 Feb 2020 01:58:45 +0000 (10:58 +0900)
Enable support armnn for in single-shot API
Added test with caffe and tflite models

resolves #1817

Signed-off-by: Parichay Kapoor <pk.kapoor@samsung.com>
api/capi/include/nnstreamer.h
api/capi/src/nnstreamer-capi-single.c
api/capi/src/nnstreamer-capi-util.c
ext/nnstreamer/tensor_filter/meson.build
meson.build
tests/nnstreamer_filter_armnn/unittest_filter_armnn.cc
tests/tizen_capi/unittest_tizen_capi.cc

index bd1d0ac..5737806 100644 (file)
@@ -128,6 +128,7 @@ typedef enum {
   ML_NNFW_TYPE_OPENVINO = 6,          /**< Intel openVINO. */
   ML_NNFW_TYPE_VIVANTE = 7,           /**< VeriSilicon's Vivante (TBD) */
   ML_NNFW_TYPE_EDGE_TPU = 8,          /**< Google Coral edge TPU (USB) */
+  ML_NNFW_TYPE_ARMNN = 9,             /**< Arm Neural Network framework (support for caffe and tensorflow-lite) */
   ML_NNFW_TYPE_SNAP = 0x2001,         /**< SNAP (Samsung Neural Acceleration Platform), only for Android. */
 } ml_nnfw_type_e;
 
index 405f4e3..4c5cc99 100644 (file)
@@ -518,6 +518,18 @@ ml_single_open_custom (ml_single_h * single, ml_single_preset * info)
       status = ML_ERROR_INVALID_PARAMETER;
       goto error;
     }
+  } else if (nnfw == ML_NNFW_TYPE_ARMNN) {
+    /* set input and output tensors information, if available */
+    if (in_tensors_info) {
+      status = ml_single_set_inout_tensors_info (filter_obj, TRUE, in_tensors_info);
+      if (status != ML_ERROR_NONE)
+        goto error;
+    }
+    if (out_tensors_info) {
+      status = ml_single_set_inout_tensors_info (filter_obj, FALSE, out_tensors_info);
+      if (status != ML_ERROR_NONE)
+        goto error;
+    }
   }
 
   switch (nnfw) {
@@ -542,6 +554,9 @@ ml_single_open_custom (ml_single_h * single, ml_single_preset * info)
     case ML_NNFW_TYPE_SNAP:
       g_object_set (filter_obj, "framework", "snap", NULL);
       break;
+    case ML_NNFW_TYPE_ARMNN:
+      g_object_set (filter_obj, "framework", "armnn", NULL);
+      break;
     default:
       /** @todo Add other fw later. */
       ml_loge ("The given nnfw is not supported.");
@@ -562,6 +577,7 @@ ml_single_open_custom (ml_single_h * single, ml_single_preset * info)
     status = ML_ERROR_INVALID_PARAMETER;
     goto error;
   }
+
   if (klass->start (single_h->filter) == FALSE) {
     status = ML_ERROR_INVALID_PARAMETER;
     goto error;
index 5106e0e..647ea9f 100644 (file)
@@ -978,6 +978,15 @@ ml_validate_model_file (const char *model, ml_nnfw_type_e * nnfw)
     case ML_NNFW_TYPE_SNAP:
       /* SNAP requires multiple files, set supported if model file exists. */
       break;
+    case ML_NNFW_TYPE_ARMNN:
+      if (!g_str_has_suffix (path_down, ".caffemodel") &&
+          !g_str_has_suffix (path_down, ".tflite") &&
+          !g_str_has_suffix (path_down, ".pb") &&
+          !g_str_has_suffix (path_down, ".prototxt")) {
+        ml_loge ("The given model [%s] has invalid extension.", model);
+        status = ML_ERROR_INVALID_PARAMETER;
+      }
+      break;
     default:
       status = ML_ERROR_INVALID_PARAMETER;
       break;
@@ -1059,6 +1068,9 @@ ml_check_nnfw_availability (ml_nnfw_type_e nnfw, ml_nnfw_hw_e hw,
     case ML_NNFW_TYPE_SNAP:
       fw_name = g_strdup ("snap");
       break;
+    case ML_NNFW_TYPE_ARMNN:
+      fw_name = g_strdup ("armnn");
+      break;
     default:
       /* Default = "Not available!" */
       break;
index 615483b..916dda0 100644 (file)
@@ -41,7 +41,6 @@ if get_option('enable-armnn')
     nnstreamer_filter_armnn_sources += join_paths(meson.current_source_dir(), s)
   endforeach
 
-  armnn_dep = dependency('armnn', required: true)
   nnstreamer_filter_armnn_deps = [glib_dep, gst_dep, nnstreamer_dep, armnn_dep]
 
   armnn_plugin_lib = shared_library('nnstreamer_filter_armnn',
index f3f8a7c..363e417 100644 (file)
@@ -244,6 +244,12 @@ if get_option('enable-tflite-nnapi-delegation')
   # For tf-lite/nnapi, enable-nnfw allows to use nnfw::tflite::nnapi as a backend.
 endif
 
+# ArmNN
+if get_option('enable-armnn')
+  armnn_dep = dependency('armnn', required: true)
+  add_project_arguments('-DENABLE_ARMNN=1', language: ['c', 'cpp'])
+endif
+
 # Set configuration to install .ini
 nnstreamer_install_conf = configuration_data()
 nnstreamer_install_conf.merge_from(nnstreamer_conf)
index 9167e57..82e78af 100644 (file)
@@ -394,7 +394,7 @@ TEST (nnstreamer_filter_armnn, invoke_01)
   prop.input_meta.num_tensors = 1;
   prop.input_meta.info[0].name = g_strdup ("data");
 
-  EXPECT_TRUE (g_file_get_contents (data_file, (guint **) &input_uint8.data,
+  EXPECT_TRUE (g_file_get_contents (data_file, (gchar **) &input_uint8.data,
         &input_uint8.size, NULL));
 
   /** Convert the data from uint8 to float */
index a376987..be4b5d8 100644 (file)
@@ -3022,6 +3022,555 @@ TEST (nnstreamer_capi_singleshot, invoke_05)
 }
 #endif  /* ENABLE_NNFW_RUNTIME */
 
+#ifdef ENABLE_ARMNN
+/**
+ * @brief Test NNStreamer single shot (caffe/armnn)
+ * @detail Run pipeline with caffe lenet model.
+ */
+TEST (nnstreamer_capi_singleshot, invoke_06)
+{
+  ml_single_h single;
+  ml_tensors_info_h in_info, out_info;
+  ml_tensors_info_h in_res, out_res;
+  ml_tensors_data_h input, output;
+  ml_tensor_dimension in_dim, out_dim, res_dim;
+  ml_tensor_type_e type = ML_TENSOR_TYPE_UNKNOWN;
+  unsigned int count = 0;
+  char *name = NULL;
+  int status, max_score_index;
+  float score, max_score;
+  void *data_ptr;
+  size_t data_size;
+
+  const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
+  gchar *test_model, *test_file;
+  guint8 *contents_uint8 = NULL;
+  gfloat *contents_float = NULL;
+  gsize len = 0;
+
+  /* supposed to run test in build directory */
+  if (root_path == NULL)
+    root_path = "..";
+
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "lenet_iter_9000.caffemodel", NULL);
+  ASSERT_TRUE (g_file_test (test_model, G_FILE_TEST_EXISTS));
+
+  test_file = g_build_filename (root_path, "tests", "test_models", "data",
+      "9.raw", NULL);
+  ASSERT_TRUE (g_file_test (test_file, G_FILE_TEST_EXISTS));
+
+  ml_tensors_info_create (&in_info);
+  ml_tensors_info_create (&out_info);
+  ml_tensors_info_create (&in_res);
+  ml_tensors_info_create (&out_res);
+
+  in_dim[0] = 28;
+  in_dim[1] = 28;
+  in_dim[2] = 1;
+  in_dim[3] = 1;
+  ml_tensors_info_set_count (in_info, 1);
+  ml_tensors_info_set_tensor_name (in_info, 0, "data");
+  ml_tensors_info_set_tensor_type (in_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (in_info, 0, in_dim);
+
+  out_dim[0] = 10;
+  out_dim[1] = 1;
+  out_dim[2] = 1;
+  out_dim[3] = 1;
+  ml_tensors_info_set_count (out_info, 1);
+  ml_tensors_info_set_tensor_name (out_info, 0, "prob");
+  ml_tensors_info_set_tensor_type (out_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (out_info, 0, out_dim);
+
+  ASSERT_TRUE (g_file_get_contents (test_file, (gchar **) &contents_uint8, &len,
+        NULL));
+  status = ml_tensors_info_get_tensor_size (in_info, 0, &data_size);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  ASSERT_TRUE (len == data_size / sizeof (float));
+
+  /** Convert uint8 data with range [0, 255] to float with range [-1, 1] */
+  contents_float = (gfloat *) g_malloc (data_size);
+  for (unsigned int idx=0; idx < len; idx ++) {
+    contents_float[idx] = static_cast<float> (contents_uint8[idx]);
+    contents_float[idx] -= 127.5;
+    contents_float[idx] /= 127.5;
+  }
+
+  status = ml_single_open (&single, test_model, in_info, out_info,
+      ML_NNFW_TYPE_ARMNN, ML_NNFW_HW_ANY);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  /* input tensor in filter */
+  status = ml_single_get_input_info (single, &in_res);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_tensors_info_get_count (in_res, &count);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (count, 1U);
+
+  status = ml_tensors_info_get_tensor_name (in_res, 0, &name);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (g_str_equal (name, "data"));
+
+  status = ml_tensors_info_get_tensor_type (in_res, 0, &type);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (type, ML_TENSOR_TYPE_FLOAT32);
+
+  ml_tensors_info_get_tensor_dimension (in_res, 0, res_dim);
+  EXPECT_TRUE (in_dim[0] == res_dim[0]);
+  EXPECT_TRUE (in_dim[1] == res_dim[1]);
+  EXPECT_TRUE (in_dim[2] == res_dim[2]);
+  EXPECT_TRUE (in_dim[3] == res_dim[3]);
+
+  /* output tensor in filter */
+  status = ml_single_get_output_info (single, &out_res);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_tensors_info_get_count (out_res, &count);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (count, 1U);
+
+  status = ml_tensors_info_get_tensor_name (out_res, 0, &name);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (g_str_equal (name, "prob"));
+
+  status = ml_tensors_info_get_tensor_type (out_res, 0, &type);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (type, ML_TENSOR_TYPE_FLOAT32);
+
+  ml_tensors_info_get_tensor_dimension (out_res, 0, res_dim);
+  EXPECT_TRUE (out_dim[0] == res_dim[0]);
+  EXPECT_TRUE (out_dim[1] == res_dim[1]);
+  EXPECT_TRUE (out_dim[2] == res_dim[2]);
+  EXPECT_TRUE (out_dim[3] == res_dim[3]);
+
+  input = output = NULL;
+
+  /* generate input data */
+  status = ml_tensors_data_create (in_info, &input);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (input != NULL);
+
+  status = ml_tensors_data_set_tensor_data (input, 0, contents_float, data_size);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_single_set_timeout (single, SINGLE_DEF_TIMEOUT_MSEC);
+  EXPECT_TRUE (status == ML_ERROR_NOT_SUPPORTED || status == ML_ERROR_NONE);
+
+  status = ml_single_invoke (single, input, &output);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (output != NULL);
+
+  status = ml_tensors_data_get_tensor_data (output, 1, &data_ptr, &data_size);
+  EXPECT_EQ (status, ML_ERROR_INVALID_PARAMETER);
+
+  status = ml_tensors_data_get_tensor_data (output, 0, &data_ptr, &data_size);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  max_score = .0;
+  max_score_index = 0;
+  for (gint i = 0; i < 10; i++) {
+    score = ((float *) data_ptr)[i];
+    if (score > max_score) {
+      max_score = score;
+      max_score_index = i;
+    }
+  }
+
+  EXPECT_EQ (max_score_index, 9);
+
+  ml_tensors_data_destroy (output);
+  ml_tensors_data_destroy (input);
+
+  status = ml_single_close (single);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  g_free (test_model);
+  g_free (test_file);
+  g_free (contents_uint8);
+  g_free (contents_float);
+  ml_tensors_info_destroy (in_info);
+  ml_tensors_info_destroy (out_info);
+  ml_tensors_info_destroy (in_res);
+  ml_tensors_info_destroy (out_res);
+}
+
+/**
+ * @brief Test NNStreamer single shot (tflite/armnn)
+ * @detail Run pipeline with tflite basic model.
+ */
+TEST (nnstreamer_capi_singleshot, invoke_07)
+{
+  ml_single_h single;
+  ml_tensors_info_h in_info, out_info;
+  ml_tensors_info_h in_res, out_res;
+  ml_tensors_data_h input, output;
+  ml_tensor_dimension in_dim, out_dim, res_dim;
+  ml_tensor_type_e type = ML_TENSOR_TYPE_UNKNOWN;
+  unsigned int count = 0;
+  char *name = NULL;
+  int status;
+  void *data_ptr;
+  size_t data_size;
+
+  const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
+  gchar *test_model;
+
+  /* supposed to run test in build directory */
+  if (root_path == NULL)
+    root_path = "..";
+
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "add.tflite", NULL);
+  ASSERT_TRUE (g_file_test (test_model, G_FILE_TEST_EXISTS));
+
+  ml_tensors_info_create (&in_info);
+  ml_tensors_info_create (&out_info);
+  ml_tensors_info_create (&in_res);
+  ml_tensors_info_create (&out_res);
+
+  in_dim[0] = 1;
+  in_dim[1] = 1;
+  in_dim[2] = 1;
+  in_dim[3] = 1;
+  ml_tensors_info_set_count (in_info, 1);
+  ml_tensors_info_set_tensor_type (in_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (in_info, 0, in_dim);
+
+  out_dim[0] = 1;
+  out_dim[1] = 1;
+  out_dim[2] = 1;
+  out_dim[3] = 1;
+  ml_tensors_info_set_count (out_info, 1);
+  ml_tensors_info_set_tensor_type (out_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (out_info, 0, out_dim);
+
+  status = ml_single_open (&single, test_model, NULL, NULL,
+      ML_NNFW_TYPE_ARMNN, ML_NNFW_HW_ANY);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  /* input tensor in filter */
+  status = ml_single_get_input_info (single, &in_res);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_tensors_info_get_count (in_res, &count);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (count, 1U);
+
+  status = ml_tensors_info_get_tensor_name (in_res, 0, &name);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (name != NULL);
+
+  status = ml_tensors_info_get_tensor_type (in_res, 0, &type);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (type, ML_TENSOR_TYPE_FLOAT32);
+
+  ml_tensors_info_get_tensor_dimension (in_res, 0, res_dim);
+  EXPECT_TRUE (in_dim[0] == res_dim[0]);
+  EXPECT_TRUE (in_dim[1] == res_dim[1]);
+  EXPECT_TRUE (in_dim[2] == res_dim[2]);
+  EXPECT_TRUE (in_dim[3] == res_dim[3]);
+
+  /* output tensor in filter */
+  status = ml_single_get_output_info (single, &out_res);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_tensors_info_get_count (out_res, &count);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (count, 1U);
+
+  status = ml_tensors_info_get_tensor_name (out_res, 0, &name);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (name != NULL);
+
+  status = ml_tensors_info_get_tensor_type (out_res, 0, &type);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (type, ML_TENSOR_TYPE_FLOAT32);
+
+  ml_tensors_info_get_tensor_dimension (out_res, 0, res_dim);
+  EXPECT_TRUE (out_dim[0] == res_dim[0]);
+  EXPECT_TRUE (out_dim[1] == res_dim[1]);
+  EXPECT_TRUE (out_dim[2] == res_dim[2]);
+  EXPECT_TRUE (out_dim[3] == res_dim[3]);
+
+  input = output = NULL;
+
+  /* generate dummy data */
+  status = ml_tensors_data_create (in_info, &input);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (input != NULL);
+
+  status = ml_tensors_data_get_tensor_data (input, 0, &data_ptr, &data_size);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  ((float *) data_ptr)[0] = 10.0;
+
+  status = ml_single_set_timeout (single, SINGLE_DEF_TIMEOUT_MSEC);
+  EXPECT_TRUE (status == ML_ERROR_NOT_SUPPORTED || status == ML_ERROR_NONE);
+
+  status = ml_single_invoke (single, input, &output);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (output != NULL);
+
+  status = ml_tensors_data_get_tensor_data (output, 0, &data_ptr, &data_size);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (((float *) data_ptr)[0], 12.0);
+
+  ml_tensors_data_destroy (output);
+  ml_tensors_data_destroy (input);
+
+  status = ml_single_close (single);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  g_free (test_model);
+  ml_tensors_info_destroy (in_info);
+  ml_tensors_info_destroy (out_info);
+  ml_tensors_info_destroy (in_res);
+  ml_tensors_info_destroy (out_res);
+}
+
+/**
+ * @brief Test NNStreamer single shot (caffe/armnn)
+ * @detail Failure open with invalid param.
+ */
+TEST (nnstreamer_capi_singleshot, open_fail_03_n)
+{
+  ml_single_h single;
+  ml_tensors_info_h in_info, out_info;
+  ml_tensor_dimension in_dim, out_dim;
+  int status;
+  const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
+  gchar *test_model;
+
+  /* supposed to run test in build directory */
+  if (root_path == NULL)
+    root_path = "..";
+
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "lenet_iter_9000.caffemodel", NULL);
+  ASSERT_TRUE (g_file_test (test_model, G_FILE_TEST_EXISTS));
+
+  ml_tensors_info_create (&in_info);
+  ml_tensors_info_create (&out_info);
+
+  /** Set the correct input/output info */
+  in_dim[0] = 28;
+  in_dim[1] = 28;
+  in_dim[2] = 1;
+  in_dim[3] = 1;
+  ml_tensors_info_set_count (in_info, 1);
+  ml_tensors_info_set_tensor_name (in_info, 0, "data");
+  ml_tensors_info_set_tensor_type (in_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (in_info, 0, in_dim);
+
+  out_dim[0] = 10;
+  out_dim[1] = 1;
+  out_dim[2] = 1;
+  out_dim[3] = 1;
+  ml_tensors_info_set_count (out_info, 1);
+  ml_tensors_info_set_tensor_name (out_info, 0, "prob");
+  ml_tensors_info_set_tensor_type (out_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (out_info, 0, out_dim);
+
+  /** Modify the input or output name to be wrong and open */
+  ml_tensors_info_set_tensor_name (in_info, 0, "data1");
+  status = ml_single_open (&single, test_model, in_info, out_info,
+      ML_NNFW_TYPE_ARMNN, ML_NNFW_HW_ANY);
+  EXPECT_NE (status, ML_ERROR_NONE);
+  ml_tensors_info_set_tensor_name (in_info, 0, "data");
+
+  ml_tensors_info_set_tensor_name (out_info, 0, "prob1");
+  status = ml_single_open (&single, test_model, in_info, out_info,
+      ML_NNFW_TYPE_ARMNN, ML_NNFW_HW_ANY);
+  EXPECT_NE (status, ML_ERROR_NONE);
+  ml_tensors_info_set_tensor_name (out_info, 0, "prob");
+
+  /**
+   * Modify the input dim to be wrong and open
+   * output dim is not used for caffe, so wrong output dim will pass open
+   * but will fail at invoke (check nnstreamer_capi_singleshot.invoke_07_n)
+   */
+  ml_tensors_info_set_tensor_dimension (in_info, 0, out_dim);
+  status = ml_single_open (&single, test_model, in_info, out_info,
+      ML_NNFW_TYPE_ARMNN, ML_NNFW_HW_ANY);
+  EXPECT_NE (status, ML_ERROR_NONE);
+  ml_tensors_info_set_tensor_dimension (in_info, 0, in_dim);
+
+  g_free (test_model);
+  ml_tensors_info_destroy (in_info);
+  ml_tensors_info_destroy (out_info);
+}
+
+/**
+ * @brief Test NNStreamer single shot (caffe/armnn)
+ * @detail Failure invoke with invalid param.
+ */
+TEST (nnstreamer_capi_singleshot, invoke_08_n)
+{
+  ml_single_h single;
+  ml_tensors_info_h in_info, out_info;
+  ml_tensors_data_h input, output;
+  ml_tensor_dimension in_dim, out_dim;
+  int status;
+  size_t data_size;
+
+  const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
+  gchar *test_model;
+  gfloat *contents_float = NULL;
+
+  /* supposed to run test in build directory */
+  if (root_path == NULL)
+    root_path = "..";
+
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "lenet_iter_9000.caffemodel", NULL);
+  ASSERT_TRUE (g_file_test (test_model, G_FILE_TEST_EXISTS));
+
+  ml_tensors_info_create (&in_info);
+  ml_tensors_info_create (&out_info);
+
+  in_dim[0] = 28;
+  in_dim[1] = 28;
+  in_dim[2] = 1;
+  in_dim[3] = 1;
+  ml_tensors_info_set_count (in_info, 1);
+  ml_tensors_info_set_tensor_name (in_info, 0, "data");
+  ml_tensors_info_set_tensor_type (in_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (in_info, 0, in_dim);
+
+  out_dim[0] = 10;
+  out_dim[1] = 1;
+  out_dim[2] = 1;
+  out_dim[3] = 1;
+  ml_tensors_info_set_count (out_info, 1);
+  ml_tensors_info_set_tensor_name (out_info, 0, "prob");
+  ml_tensors_info_set_tensor_type (out_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (out_info, 0, out_dim);
+  ml_tensors_info_set_tensor_dimension (out_info, 0, in_dim);
+
+  status = ml_single_open (&single, test_model, in_info, out_info,
+      ML_NNFW_TYPE_ARMNN, ML_NNFW_HW_ANY);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  input = output = NULL;
+
+  /* generate input data with wrong info */
+  status = ml_tensors_data_create (in_info, &input);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (input != NULL);
+
+  status = ml_tensors_info_get_tensor_size (in_info, 0, &data_size);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  contents_float = (gfloat *) g_malloc (data_size);
+  status = ml_tensors_data_set_tensor_data (input, 0, contents_float, data_size);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_single_set_timeout (single, SINGLE_DEF_TIMEOUT_MSEC);
+  EXPECT_TRUE (status == ML_ERROR_NOT_SUPPORTED || status == ML_ERROR_NONE);
+
+  status = ml_single_invoke (single, input, &output);
+  EXPECT_NE (status, ML_ERROR_NONE);
+  EXPECT_TRUE (output == NULL);
+
+  ml_tensors_data_destroy (input);
+
+  status = ml_single_close (single);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  g_free (test_model);
+  g_free (contents_float);
+  ml_tensors_info_destroy (in_info);
+  ml_tensors_info_destroy (out_info);
+}
+
+/**
+ * @brief Test NNStreamer single shot (caffe/armnn)
+ * @detail Failure invoke with invalid param.
+ */
+TEST (nnstreamer_capi_singleshot, invoke_09_n)
+{
+  ml_single_h single;
+  ml_tensors_info_h in_info, out_info;
+  ml_tensors_info_h in_res, out_res;
+  ml_tensors_data_h input, output;
+  ml_tensor_dimension in_dim, out_dim;
+  int status;
+  size_t data_size;
+
+  const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
+  gchar *test_model;
+  gfloat *contents_float = NULL;
+
+  /* supposed to run test in build directory */
+  if (root_path == NULL)
+    root_path = "..";
+
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "lenet_iter_9000.caffemodel", NULL);
+  ASSERT_TRUE (g_file_test (test_model, G_FILE_TEST_EXISTS));
+
+  ml_tensors_info_create (&in_info);
+  ml_tensors_info_create (&out_info);
+  ml_tensors_info_create (&in_res);
+  ml_tensors_info_create (&out_res);
+
+  in_dim[0] = 28;
+  in_dim[1] = 28;
+  in_dim[2] = 1;
+  in_dim[3] = 1;
+  ml_tensors_info_set_count (in_info, 1);
+  ml_tensors_info_set_tensor_name (in_info, 0, "data");
+  ml_tensors_info_set_tensor_type (in_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (in_info, 0, in_dim);
+
+  out_dim[0] = 10;
+  out_dim[1] = 1;
+  out_dim[2] = 1;
+  out_dim[3] = 1;
+  ml_tensors_info_set_count (out_info, 1);
+  ml_tensors_info_set_tensor_name (out_info, 0, "prob");
+  ml_tensors_info_set_tensor_type (out_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (out_info, 0, out_dim);
+
+  status = ml_single_open (&single, test_model, in_info, out_info,
+      ML_NNFW_TYPE_ARMNN, ML_NNFW_HW_ANY);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  input = output = NULL;
+
+  /* generate input data with wrong info */
+  status = ml_tensors_data_create (out_info, &input);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (input != NULL);
+
+  status = ml_tensors_info_get_tensor_size (out_info, 0, &data_size);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  contents_float = (gfloat *) g_malloc (data_size);
+  status = ml_tensors_data_set_tensor_data (input, 0, contents_float, data_size);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_single_set_timeout (single, SINGLE_DEF_TIMEOUT_MSEC);
+  EXPECT_TRUE (status == ML_ERROR_NOT_SUPPORTED || status == ML_ERROR_NONE);
+
+  status = ml_single_invoke (single, input, &output);
+  EXPECT_NE (status, ML_ERROR_NONE);
+  EXPECT_TRUE (output == NULL);
+
+  ml_tensors_data_destroy (input);
+
+  status = ml_single_close (single);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  g_free (test_model);
+  g_free (contents_float);
+  ml_tensors_info_destroy (in_info);
+  ml_tensors_info_destroy (out_info);
+  ml_tensors_info_destroy (in_res);
+  ml_tensors_info_destroy (out_res);
+}
+#endif  /* ENABLE_ARMNN */
+
 /**
  * @brief Test NNStreamer single shot (custom filter)
  * @detail Change the number of input tensors, run the model and verify output