[UnitTest] Add new unittest for libnpuhost
authorDongju Chae <dongju.chae@samsung.com>
Thu, 13 Aug 2020 06:02:00 +0000 (15:02 +0900)
committer송욱/On-Device Lab(SR)/Staff Engineer/삼성전자 <wook16.song@samsung.com>
Thu, 13 Aug 2020 23:58:13 +0000 (08:58 +0900)
This patch adds new unittest for libnpuhost.

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
meson.build
src/core/npu/NPUdrvAPI_emul.cc
tests/unittests/meson.build
tests/unittests/ne_libnpuhost_test.cc [new file with mode: 0644]

index 12649d1..e4ea57f 100644 (file)
@@ -66,9 +66,11 @@ ne_libdir = join_paths(ne_prefix, get_option('libdir'))
 ne_bindir = join_paths(ne_prefix, get_option('bindir'))
 ne_includedir = join_paths(ne_prefix, get_option('includedir'))
 ne_inidir = get_option('sysconfdir')
-add_project_arguments('-DNE_INIDIR="' + ne_inidir + '"', language: ['c', 'cpp'])
 ne_datadir = join_paths(ne_prefix, join_paths(get_option('datadir'), 'npu-engine'))
 
+add_project_arguments('-DNE_INIDIR="' + ne_inidir + '"', language: ['c', 'cpp'])
+add_project_arguments('-DNE_DATADIR="' + ne_datadir + '"', language: ['c', 'cpp'])
+
 ne_common_inc = include_directories('include/common')
 ne_host_inc = include_directories('include/host')
 
index dc86957..2f0ae8f 100644 (file)
@@ -139,6 +139,8 @@ ThreadSafeMap<int, EmulTask> TrinityEmulAPI::task_map_;
 std::atomic<int> EmulElement::global_id_ (0);
 /** @brief element's global id */
 std::atomic<int> TrinityEmulAPI::global_fd_ (0);
+/** @brief global lock for run */
+static std::mutex global_lock;
 
 /**
  * @brief constructor of emulation API driver
@@ -390,6 +392,8 @@ TrinityEmulAPI::runInput (input_config_t *input_config) const
   if ((dev_type_ & DEVICETYPE_MASK) == DEVICETYPE_TRIA)
     return 0;
 
+  std::unique_lock<std::mutex> lock (global_lock);
+
   /** either buffer or segment table */
   EmulElement *elem_input = elem_map_.find (input_config->dbuf_fd);
   if (elem_input == nullptr || elem_input->getAddr () == nullptr)
index b8d0ce2..122359e 100644 (file)
@@ -121,4 +121,14 @@ if gtest_dep.found()
     install_dir : join_paths(ne_bindir, 'unittests')
   )
   test('unittest_ne_core_inputservice', unittest_ne_core_inputservice)
+
+  unittest_ne_libnpuhost = executable('unittest_ne_libnpuhost',
+    ['ne_libnpuhost_test.cc'],
+    include_directories: [ne_host_inc, ne_common_inc],
+    dependencies: [gtest_dep, ne_core_dep, ne_test_utils_dep],
+    install : true,
+    install_rpath : ne_libdir,
+    install_dir : join_paths(ne_bindir, 'unittests')
+  )
+  test('unittest_ne_libnpuhost', unittest_ne_libnpuhost)
 endif
diff --git a/tests/unittests/ne_libnpuhost_test.cc b/tests/unittests/ne_libnpuhost_test.cc
new file mode 100644 (file)
index 0000000..2dd4367
--- /dev/null
@@ -0,0 +1,367 @@
+/**
+ * Proprietary
+ * Copyright (C) 2020 Samsung Electronics
+ * Copyright (C) 2020 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file ne_libnpuhost_test.cc
+ * @date 12 Aug 2020
+ * @brief UnitTests to test functionality of libnpuhost APIs
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <errno.h>
+#include <dirent.h>
+#include <linux/limits.h>
+#include <pthread.h>
+
+#include <libnpuhost.h>
+#include <ne_test_utils.h>
+
+#include "ne_unittest_utils.h"
+
+/**
+ * @brief test APIs to check number of devices
+ */
+TEST (ne_libnpuhost_test, get_num_devices)
+{
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIV_CONN_SOCIP), 0);
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIV2_CONN_SOCIP), 0);
+
+  /* other aliases also work */
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_UNKNOWN), 0);
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_USB), 0);
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_SOCIP), 0);
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_PCIE), 0);
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_FILESYS), 0);
+
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIV_CONN_UNKNOWN), 0);
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIV2_CONN_UNKNOWN), 0);
+
+  /* TRIA is not supported in FastModel */
+#if defined(ENABLE_EMUL)
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIA_CONN_SOCIP), 0);
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIA_CONN_UNKNOWN), 0);
+
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_ASR_CONN_SOCIP), 0);
+  EXPECT_GT (getnumNPUdeviceByType (NPUCOND_ASR_CONN_UNKNOWN), 0);
+#endif
+}
+
+/**
+ * @brief test APIs to check number of devices (negative cases)
+ */
+TEST (ne_libnpuhost_test, get_num_devices_n)
+{
+  EXPECT_EQ (getnumNPUdeviceByType ((dev_type) 0), 0);
+  EXPECT_EQ (getnumNPUdeviceByType ((dev_type) 0x1234), 0);
+  EXPECT_EQ (getnumNPUdeviceByType ((dev_type) 0xdeadbeef), 0);
+  EXPECT_EQ (getnumNPUdeviceByType ((dev_type) 0xffffffff), 0);
+}
+
+static dev_type test_types[] = {
+  NPUCOND_TRIV_CONN_SOCIP,
+#if defined(ENABLE_EMUL)
+  NPUCOND_TRIA_CONN_SOCIP,
+#endif
+  NPUCOND_TRIV2_CONN_SOCIP
+};
+
+/**
+ * @brief test APIs to get a device instance
+ */
+TEST (ne_libnpuhost_test, get_device_instance)
+{
+  npudev_h dev;
+  int num_devices;
+
+  for (int i = 0; i < sizeof(test_types) / sizeof(dev_type); i++) {
+    dev_type type = test_types[i];
+    num_devices = getnumNPUdeviceByType (type);
+    ASSERT_GT (num_devices, 0);
+
+    for (int j = 0; j < num_devices; j++) {
+      EXPECT_EQ (getNPUdeviceByType (&dev, type, j), 0);
+      putNPUdevice (dev);
+    }
+  }
+}
+
+/**
+ * @brief test APIs to get a device instance (negative cases)
+ */
+TEST (ne_libnpuhost_test, get_device_instance_n)
+{
+  npudev_h dev;
+  int num_devices;
+
+  for (int i = 0; i < sizeof(test_types) / sizeof(dev_type); i++) {
+    dev_type type = test_types[i];
+    num_devices = getnumNPUdeviceByType (type);
+    ASSERT_GT (num_devices, 0);
+
+    for (int j = 0; j < num_devices; j++) {
+      EXPECT_NE (getNPUdeviceByType (&dev, (dev_type) 0x0, j), 0);
+      EXPECT_NE (getNPUdeviceByType (&dev, (dev_type) 0x1234, j), 0);
+      EXPECT_NE (getNPUdeviceByType (&dev, (dev_type) 0xdeadbeef, j), 0);
+      EXPECT_NE (getNPUdeviceByType (&dev, (dev_type) 0xffffffff, j), 0);
+    }
+  }
+}
+
+class TesterTrinity {
+  public:
+    TesterTrinity () : done_ (0), repeat_ (0) {}
+
+    void wait_runs () {
+      std::unique_lock<std::mutex> lock (m_);
+      cv_.wait (lock, [this]() { return done_ == repeat_; });
+    }
+
+    void setRepeat (uint32_t repeat) { repeat_ = repeat; }
+
+    static void callback (output_buffers *output, uint64_t sequence,
+        void *data) {
+      TesterTrinity *tester = static_cast<TesterTrinity *>(data);
+      tester->finish (output);
+    }
+
+  private:
+    void finish (output_buffers * output) {
+      for (uint32_t idx = 0; idx < output->num_buffers; idx++) {
+        char * output_data = static_cast<char*> (output->bufs[idx].addr);
+        /* do nothing */
+        free (output_data);
+
+      }
+
+      std::unique_lock<std::mutex> lock (m_);
+      done_++;
+      cv_.notify_one ();
+    }
+
+    uint32_t done_;
+    uint32_t repeat_;
+
+    std::mutex m_;
+    std::condition_variable cv_;
+};
+
+class TesterTRIA : public TesterTrinity, public UtilTRIA {
+  public:
+    TesterTRIA () {}
+};
+
+class TesterTRIV : public TesterTrinity, public UtilTRIV {
+  public:
+    TesterTRIV () {}
+};
+
+class TesterTRIV2 : public TesterTrinity, public UtilTRIV2 {
+  public:
+    TesterTRIV2 () {}
+};
+
+/**
+ * @brief test APIs to run model with input data (TRIV)
+ */
+TEST (ne_libnpuhost_test, run_input_triv_binfmt_v1)
+{
+  TesterTRIV tester;
+
+  ASSERT_EQ (tester.init (), 0);
+
+  std::string model_dir (NE_DATADIR);
+  model_dir += "/testdata/npubinfmt_v1/testcase1";
+
+  tester.setModelDir (model_dir);
+  ASSERT_EQ (tester.loadModel (), 0);
+
+  const npubin_meta *meta = tester.getMetadata ();
+  ASSERT_NE (meta, nullptr);
+
+  /* prepare input */
+  std::string input_path = model_dir + "/input_fmap.bin";
+
+  input_buffers input;
+  input.num_buffers = 1;
+
+  input.bufs[0].size = get_file_size (input_path.c_str ());
+  input.bufs[0].filepath = input_path.c_str ();
+  input.bufs[0].type = BUFFER_FILE;
+
+  /* run input */
+  output_buffers output;
+  EXPECT_EQ (tester.run (&input, &output), 0);
+
+  tester.setRepeat (3);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  tester.wait_runs ();
+}
+
+/**
+ * @brief test APIs to run model with input data (TRIV)
+ */
+TEST (ne_libnpuhost_test, run_input_triv_binfmt_v2)
+{
+  TesterTRIV tester;
+
+  ASSERT_EQ (tester.init (), 0);
+
+  std::string model_dir (NE_DATADIR);
+  model_dir += "/testdata/npubinfmt_v2/testcase1";
+
+  tester.setModelDir (model_dir);
+  ASSERT_EQ (tester.loadModel (), 0);
+
+  const npubin_meta *meta = tester.getMetadata ();
+  ASSERT_NE (meta, nullptr);
+
+  /* prepare input */
+  std::vector<std::string> input_path;
+  input_buffers input;
+
+  input_path.resize (meta->input_num);
+  input.num_buffers = meta->input_num;
+  for (uint32_t idx = 0; idx < meta->input_num; idx++) {
+    input_path[idx] = model_dir + "/input_fmap_" + std::to_string (idx) + ".bin";
+    input.bufs[idx].size = get_file_size (input_path[idx].c_str ());
+    input.bufs[idx].filepath = input_path[idx].c_str ();
+    input.bufs[idx].type = BUFFER_FILE;
+  }
+
+  /* run input */
+  output_buffers output;
+  EXPECT_EQ (tester.run (&input, &output), 0);
+
+  tester.setRepeat (3);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  tester.wait_runs ();
+}
+
+#if defined(ENABLE_EMUL)
+/**
+ * @brief test APIs to run model with input data (TRIA)
+ */
+TEST (ne_libnpuhost_test, run_input_tria)
+{
+  TesterTRIA tester;
+
+  ASSERT_EQ (tester.init (), 0);
+
+  /* run input */
+  input_buffers input;
+  input.num_buffers = 1;
+  input.bufs[0].size = 4096;
+  input.bufs[0].type = BUFFER_MAPPED;
+
+  ASSERT_EQ (allocNPU_inputBuffers (tester.getDeviceHandle (), &input), 0);
+
+  output_buffers output;
+  EXPECT_EQ (tester.run (&input, &output), 0);
+
+  tester.setRepeat (3);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  tester.wait_runs ();
+
+  cleanNPU_inputBuffers (tester.getDeviceHandle (), &input);
+}
+#endif
+
+/**
+ * @brief test APIs to run model with input data (TRIV2)
+ */
+TEST (ne_libnpuhost_test, run_input_triv2_binfmt_v3)
+{
+  TesterTRIV2 tester;
+
+  ASSERT_EQ (tester.init (), 0);
+
+  std::string model_dir (NE_DATADIR);
+  model_dir += "/testdata/npubinfmt_v3/CONV_I8_008";
+
+  tester.setModelDir (model_dir);
+  ASSERT_EQ (tester.loadModel (), 0);
+
+  const npubin_meta *meta = tester.getMetadata ();
+  ASSERT_NE (meta, nullptr);
+
+  /* prepare input */
+  std::vector<std::string> input_path;
+  input_buffers input;
+
+  input_path.resize (meta->input_seg_num);
+  input.num_buffers = meta->input_seg_num;
+  for (uint32_t idx = 0; idx < meta->input_seg_num; idx++) {
+    input_path[idx] = model_dir + "/input_fmap_" + std::to_string (idx) + ".bin";
+    input.bufs[idx].size = get_file_size (input_path[idx].c_str ());
+    input.bufs[idx].filepath = input_path[idx].c_str ();
+    input.bufs[idx].type = BUFFER_FILE;
+  }
+
+  /* run input */
+  output_buffers output;
+  EXPECT_EQ (tester.run (&input, &output), 0);
+
+  tester.setRepeat (3);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
+        dynamic_cast<TesterTrinity *>(&tester)), 0);
+
+  tester.wait_runs ();
+}
+
+/**
+ * @brief test APIs to run model with internal input (TRIV2)
+ */
+TEST (ne_libnpuhost_test, run_internal_triv2_binfmt_v3)
+{
+  TesterTRIV2 tester;
+
+  ASSERT_EQ (tester.init (), 0);
+
+  std::string model_dir (NE_DATADIR);
+  model_dir += "/testdata/npubinfmt_v3/CONV_I8_008";
+
+  tester.setModelDir (model_dir);
+  ASSERT_EQ (tester.loadModel (), 0);
+
+  const npubin_meta *meta = tester.getMetadata ();
+  ASSERT_NE (meta, nullptr);
+
+  /* run internal */
+  int id = tester.run ("/dev/dummy");
+  EXPECT_GT (id, 0);
+
+  usleep (TEST_SLEEP_MS);
+
+  EXPECT_EQ (tester.stop (id), 0);
+}
+
+/**
+ * @brief main function for unit test
+ */
+int
+main (int argc, char **argv)
+{
+  return start_gtest (argc, argv);
+}