[Dataset] Add test for the random dataproducer
authorJihoon Lee <jhoon.it.lee@samsung.com>
Mon, 12 Jul 2021 06:47:44 +0000 (15:47 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Tue, 27 Jul 2021 11:58:32 +0000 (20:58 +0900)
**Changes proposed in this PR:**
- Add random data producer tests
- Add abstract test for more data producers

**Self evaluation:**
1. Build test: [X]Passed [ ]Failed [ ]Skipped
2. Run test: [X]Passed [ ]Failed [ ]Skipped

Signed-off-by: Jihoon Lee <jhoon.it.lee@samsung.com>
test/unittest/datasets/data_producer_common_tests.cpp [new file with mode: 0644]
test/unittest/datasets/data_producer_common_tests.h [new file with mode: 0644]
test/unittest/datasets/meson.build [new file with mode: 0644]
test/unittest/datasets/unittest_random_data_producers.cpp [new file with mode: 0644]
test/unittest/meson.build

diff --git a/test/unittest/datasets/data_producer_common_tests.cpp b/test/unittest/datasets/data_producer_common_tests.cpp
new file mode 100644 (file)
index 0000000..fae2756
--- /dev/null
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright (C) 2021 Jihoon Lee <jhoon.it.lee@samsung.com>
+ *
+ * @file data_producer_common_tests.cpp
+ * @date 12 July 2021
+ * @brief Common test for nntrainer data producers (Param Tests)
+ * @see        https://github.com/nnstreamer/nntrainer
+ * @author Jihoon Lee <jhoon.it.lee@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+#include <gtest/gtest.h>
+
+#include <data_producer_common_tests.h>
+
+void DataProducerSemantics::SetUp() {
+  auto [producerFactory, properties, input_dims_, label_dims_, validator_,
+        result_] = GetParam();
+
+  producer = producerFactory(properties);
+  input_dims = std::move(input_dims_);
+  label_dims = std::move(label_dims_);
+  result = result_;
+  validator = std::move(validator_);
+
+  if (result != DataProducerSemanticsExpectedResult::SUCCESS) {
+    ASSERT_EQ(validator, nullptr)
+      << "Given expected result of not success, validator must be empty!";
+  }
+}
+
+void DataProducerSemantics::TearDown() {}
+
+TEST_P(DataProducerSemantics, finalize_pn) {
+  if (result == DataProducerSemanticsExpectedResult::FAIL_AT_FINALIZE) {
+    EXPECT_ANY_THROW(producer->finalize(input_dims, label_dims));
+  } else {
+    EXPECT_NO_THROW(producer->finalize(input_dims, label_dims));
+  }
+}
+
+TEST_P(DataProducerSemantics, fetch_one_epoch_or_10_iteration_pn) {
+  if (result != DataProducerSemanticsExpectedResult::SUCCESS) {
+    return; // skip this test
+  }
+
+  auto generator = producer->finalize(input_dims, label_dims);
+  auto sz = producer->size(input_dims, label_dims);
+  bool has_fixed_size = sz != nntrainer::DataProducer::SIZE_UNDEFINED;
+
+  if (!has_fixed_size) {
+    sz = 5;
+  }
+
+  for (unsigned i = 0; i < sz; ++i) {
+    auto [last, ins, labels] = generator();
+
+    ASSERT_FALSE(last) << " reached last at iteration: " << i << '\n';
+    if (validator) {
+      ASSERT_TRUE(validator(ins, labels))
+        << " failed validation for iteration: " << i << '\n';
+    }
+  }
+
+  if (has_fixed_size) {
+    {
+      auto [last, ins, labels] = generator();
+      EXPECT_TRUE(last);
+    }
+
+    {
+      auto [last, ins, labels] = generator();
+      EXPECT_TRUE(validator(ins, labels))
+        << "failed last validation after one epoch\n";
+      EXPECT_FALSE(last);
+    }
+  }
+}
diff --git a/test/unittest/datasets/data_producer_common_tests.h b/test/unittest/datasets/data_producer_common_tests.h
new file mode 100644 (file)
index 0000000..b4565dc
--- /dev/null
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright (C) 2021 Jihoon Lee <jhoon.it.lee@samsung.com>
+ *
+ * @file data_producer_common_tests.h
+ * @date 12 July 2021
+ * @brief Common test for nntrainer data producers (Param Tests)
+ * @see        https://github.com/nnstreamer/nntrainer
+ * @author Jihoon Lee <jhoon.it.lee@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+#ifndef __DATA_PRODUCER_COMMON_TESTS_H__
+#define __DATA_PRODUCER_COMMON_TESTS_H__
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include <data_producers.h>
+#include <tensor.h>
+#include <tensor_dim.h>
+
+using DataProducerFactoryType =
+  std::function<std::unique_ptr<nntrainer::DataProducer>(
+    const std::vector<std::string> &)>;
+
+using DataProducerValidatorType =
+  std::function<bool(const std::vector<nntrainer::Tensor> &,
+                     const std::vector<nntrainer::Tensor> &)>;
+
+/**
+ * @brief Data Producer Semantics expected result
+ *
+ */
+enum class DataProducerSemanticsExpectedResult {
+  SUCCESS = 0,          /**< SUCCESS */
+  FAIL_AT_FINALIZE = 1, /**< FAIL AT FINALIZE */
+};
+
+using DataProducerSemanticsParamType =
+  std::tuple<DataProducerFactoryType /**< layer factory */,
+             std::vector<std::string> /**< properties */,
+             std::vector<nntrainer::TensorDim> /**< input dimensions */,
+             std::vector<nntrainer::TensorDim> /**< label dimensions */,
+             DataProducerValidatorType /**< validator if any */,
+             DataProducerSemanticsExpectedResult /**< expected result */>;
+
+/**
+ * @brief Dataset Producer Semantics Tests
+ *
+ */
+class DataProducerSemantics
+  : public ::testing::TestWithParam<DataProducerSemanticsParamType> {
+public:
+  /**
+   * @brief SetUp test cases here
+   *
+   */
+  virtual void SetUp();
+
+  /**
+   * @brief do here if any memory needs to be released
+   *
+   */
+  virtual void TearDown();
+
+protected:
+  std::unique_ptr<nntrainer::DataProducer>
+    producer;                                   /**< producer to be tested */
+  std::vector<nntrainer::TensorDim> input_dims; /**< input dims */
+  std::vector<nntrainer::TensorDim> label_dims; /**< output dims */
+  DataProducerValidatorType validator;          /**< result validator */
+  DataProducerSemanticsExpectedResult result;   /**< expected result */
+};
+
+/**
+ * @brief Create a Data Producer object
+ *
+ * @tparam T inherited class of data producer
+ * @param props properties
+ * @return std::unique_ptr<nntrainer::DataProducer> created producer object
+ */
+template <typename T,
+          std::enable_if_t<std::is_base_of<nntrainer::DataProducer, T>::value,
+                           T> * = nullptr>
+std::unique_ptr<nntrainer::DataProducer>
+createDataProducer(const std::vector<std::string> &props = {}) {
+  std::unique_ptr<nntrainer::DataProducer> ptr = std::make_unique<T>();
+  ptr->setProperty(props);
+  return ptr;
+}
+
+#endif // __DATA_PRODUCER_COMMON_TESTS_H__
diff --git a/test/unittest/datasets/meson.build b/test/unittest/datasets/meson.build
new file mode 100644 (file)
index 0000000..2d94d49
--- /dev/null
@@ -0,0 +1,23 @@
+test_name = 'unittest_datasets'
+
+test_target = []
+
+producer_targets = [
+  'data_producer_common_tests.cpp',
+  'unittest_random_data_producers.cpp'
+]
+
+test_target += producer_targets
+exe = executable(
+  test_name,
+  test_target,
+  dependencies: [
+    nntrainer_test_main_deps,
+  ],
+  install: get_option('enable-test'),
+  install_dir: application_install_dir
+)
+
+test(test_name, exe,
+  args: '--gtest_output=xml:@0@/@1@.xml'.format(meson.build_root(), test_name)
+)
diff --git a/test/unittest/datasets/unittest_random_data_producers.cpp b/test/unittest/datasets/unittest_random_data_producers.cpp
new file mode 100644 (file)
index 0000000..3e076ef
--- /dev/null
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright (C) 2021 Jihoon Lee <jhoon.it.lee@samsung.com>
+ *
+ * @file data_producer_common_tests.cpp
+ * @date 12 July 2021
+ * @brief Common test for nntrainer dataset producers (Param Tests)
+ * @see        https://github.com/nnstreamer/nntrainer
+ * @author Jihoon Lee <jhoon.it.lee@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+#include <gtest/gtest.h>
+
+#include <algorithm>
+
+#include <data_producer_common_tests.h>
+#include <random_data_producers.h>
+
+DataProducerValidatorType random_onehot_validator(float min, float max) {
+  /// input validator: every value is in range of min, max
+  auto input_valid = [min, max](const nntrainer::Tensor &t) {
+    auto data = t.getData();
+    for (unsigned int i = 0; i < t.length(); ++i) {
+      if (*data < min || max < *data) {
+        return false;
+      }
+      data++;
+    }
+    return true;
+  };
+
+  /// label validator: sum of all is equal to batch
+  auto label_valid = [](const nntrainer::Tensor &t) {
+    /// @todo better to check batch by batch
+    return fabs(t.batch() - t.sum({0, 1, 2, 3}).getValue(0, 0, 0, 0) < 1e-7);
+  };
+
+  auto f = [input_valid,
+            label_valid](const std::vector<nntrainer::Tensor> &inputs,
+                         const std::vector<nntrainer::Tensor> &labels) -> bool {
+    bool is_inputs_valid =
+      std::all_of(inputs.begin(), inputs.end(), input_valid);
+    bool is_labels_valid =
+      std::all_of(labels.begin(), labels.end(), label_valid);
+    return is_inputs_valid && is_labels_valid;
+  };
+
+  return f;
+}
+auto random_onehot_success = DataProducerSemanticsParamType(
+  createDataProducer<nntrainer::RandomDataOneHotProducer>,
+  {"min=0", "max=1", "size=10"}, {{3, 2, 4, 5}}, {{3, 1, 1, 10}},
+  random_onehot_validator(0, 1), DataProducerSemanticsExpectedResult::SUCCESS);
+
+auto random_onehot_min_over_max = DataProducerSemanticsParamType(
+  createDataProducer<nntrainer::RandomDataOneHotProducer>,
+  {"min=2", "max=1", "size=10"}, {{3, 2, 4, 5}}, {{3, 1, 1, 10}}, nullptr,
+  DataProducerSemanticsExpectedResult::FAIL_AT_FINALIZE);
+
+auto random_onehot_invalid_label_shape = DataProducerSemanticsParamType(
+  createDataProducer<nntrainer::RandomDataOneHotProducer>, {}, {{3, 2, 4, 5}},
+  {{3, 1, 2, 10}}, nullptr,
+  DataProducerSemanticsExpectedResult::FAIL_AT_FINALIZE);
+
+INSTANTIATE_TEST_CASE_P(RandomOneHot, DataProducerSemantics,
+                        ::testing::Values(random_onehot_success,
+                                          random_onehot_min_over_max));
index 5757d37..ee9bde1 100644 (file)
@@ -58,3 +58,4 @@ unittest_inc = include_directories('.')
 
 subdir('compiler')
 subdir('layers')
+subdir('datasets')