Skip to content

Commit

Permalink
Filter/MLAgent: Add unit test cases for parsing MLAgent URIs
Browse files Browse the repository at this point in the history
This patch adds test cases for unit testing of parsing the MLAgent URIs.

Signed-off-by: Wook Song <[email protected]>
  • Loading branch information
wooksong committed Jan 2, 2024
1 parent 83295d4 commit 7be044d
Show file tree
Hide file tree
Showing 4 changed files with 514 additions and 0 deletions.
20 changes: 20 additions & 0 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,26 @@ if gtest_dep.found()
subdir('nnstreamer_datarepo')
endif

if ml_agent_support_is_available
ml_agent_lib_common_objs = nnstreamer_single_lib.extract_objects('ml_agent.c')

lib_mlagent_mock = static_library('mock_mlagentmock',
join_paths('unittest_mlagent', 'mock_mlagent.cc'),
dependencies: [glib_dep, json_glib_dep],
include_directories: nnstreamer_inc,
install: get_option('install-test'),
install_dir: nnstreamer_libdir
)

whole_dep = declare_dependency(link_whole: lib_mlagent_mock)

filter_mlagent = executable('unittest_mlagent',
join_paths('unittest_mlagent', 'unittest_mlagent.cc'),
objects:ml_agent_lib_common_objs,
dependencies: [nnstreamer_unittest_deps, nnstreamer_internal_deps, whole_dep],
)
endif

# ONNXRUNTIME unittest
if onnxruntime_support_is_available
unittest_filter_onnxruntime = executable('unittest_filter_onnxruntime',
Expand Down
114 changes: 114 additions & 0 deletions tests/unittest_mlagent/mock_mlagent.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/* SPDX-License-Identifier: LGPL-2.1-only */
/**
* @file mlagent_mock.cc
* @date 30 Nov 2023
* @brief A class that mocks the ML Agent instance
* @author Wook Song <[email protected]>
* @see http://github.com/nnstreamer/nnstreamer
* @bug No known bugs
*
*/

#include <glib.h>
#include <json-glib/json-glib.h>
#include <nnstreamer_util.h>

#include <functional>
#include <iostream>
#include <memory>

#include "mock_mlagent.h"

static std::unique_ptr<MockMLAgent> uptr_mock;

/**
* @brief Generate C-stringified JSON
*/
gchar *
MockModel::to_cstr_json ()
{
using json_member_name_to_cb_t
= std::pair<std::string, std::function<std::string ()>>;

const std::vector<json_member_name_to_cb_t> json_mem_to_cb_map{
json_member_name_to_cb_t (
"path", [this] () -> std::string { return path (); }),
json_member_name_to_cb_t (
"description", [this] () -> std::string { return desc (); }),
json_member_name_to_cb_t (
"app_info", [this] () -> std::string { return app_info (); }),
json_member_name_to_cb_t ("version",
[this] () -> std::string { return std::to_string (version ()); }),
json_member_name_to_cb_t ("active",
[this] () -> std::string { return (is_activated () ? "T" : "F"); }),
};

g_autoptr (JsonBuilder) builder = json_builder_new ();
g_autoptr (JsonGenerator) gen = NULL;

json_builder_begin_object (builder);
for (auto iter : json_mem_to_cb_map) {
json_builder_set_member_name (builder, iter.first.c_str ());
json_builder_add_string_value (builder, iter.second ().c_str ());
}
json_builder_end_object (builder);

{
g_autoptr (JsonNode) root = json_builder_get_root (builder);

gen = json_generator_new ();
json_generator_set_root (gen, root);
json_generator_set_pretty (gen, TRUE);
}

return json_generator_to_data (gen, NULL);
}

/**
* @brief Initialize the static unique_ptr of MockMLAgent
*/
void
ml_agent_mock_init ()
{
uptr_mock = std::make_unique<MockMLAgent> ();
}

/**
* @brief C-wrapper for the MockModel's method add_model.
*/
bool
ml_agent_mock_add_model (const gchar *name, const gchar *path, const gchar *app_info,
const bool is_activated, const char *desc, const guint version)
{
MockModel model{ name, path, app_info, is_activated, desc, version };

return uptr_mock->add_model (model);
}

/**
* @brief C-wrapper for the MockModel's method get_model.
*/
MockModel *
ml_agent_mock_model_get (const gchar *name, const guint version)
{
return uptr_mock->get_model (name, version);
}

/**
* @brief Pass the JSON c-string generated by the ML Agent mock class to the caller.
*/
gint
ml_agent_model_get (const gchar *name, const guint version, gchar **description, GError **err)
{
MockModel *model_ptr = ml_agent_mock_model_get (name, version);

g_return_val_if_fail (err == NULL || *err == NULL, -EINVAL);

if (model_ptr == nullptr) {
return -EINVAL;
}

*description = model_ptr->to_cstr_json ();

return 0;
}
225 changes: 225 additions & 0 deletions tests/unittest_mlagent/mock_mlagent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/**
* SPDX-License-Identifier: LGPL-2.1-only
*
* @file mlagent_mock.h
* @date 29 Nov 2023
* @brief Mocking ML Agent APIs
* @see https://github.com/nnstreamer/nnstreamer
* @author Wook Song <[email protected]>
* @bug No known bugs
*/
#ifndef __NNS_MLAGENT_MOCK_H__
#define __NNS_MLAGENT_MOCK_H__

#include <glib.h>

#include <unordered_map>
#include <vector>

struct MockModel;

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/**
* @brief Initialize the static unique_ptr of MockMLAgent
*/
void ml_agent_mock_init ();

/**
* @brief C-wrapper for the MockModel's method add_model.
* @param[in] name The model's name to add
* @param[in] path The model's name to add
* @param[in] app_info The model's name to add
* @param[in] is_activated The model's name to add
* @param[in] desc The model's version to add
* @param[in] version The JSON c-string containing the information of the model matching to the given name and version
* @return true if there is a matching model to the given name and version, otherwise a negative integer
* @note ML Agent provides the original implementation of this function and
* the following implementation is only for testing purposes.
*/
bool ml_agent_mock_add_model (const gchar *name, const gchar *path, const gchar *info,
const bool is_activated, const char *desc, const guint version);

/**
* @brief C-wrapper for the MockModel's method get_model.
* @param[in] name The model's name to retreive
* @param[in] version The model's version to retreive
* @return A pointer to the model matching the given information. If there is no model possible, NULL is returned.
*/
MockModel *ml_agent_mock_get_model (const gchar *name, const guint version);

/**
* @brief Pass the JSON c-string generated by the ML Agent mock class to the caller.
* @param[in] name The model's name to retreive
* @param[in] version The model's version to retreive
* @param[out] description The JSON c-string, containing the information of the model matching the given name and version
* @param[out] err The return location for a recoverable error. This can be NULL.
* @return 0 if there is a matching model to the given name and version otherwise a negative integer
* @note ML Agent provides the original implementation of this function and
* the following implementation is only for testing purposes.
*/
gint ml_agent_model_get (
const gchar *name, const guint version, gchar **description, GError **err);
#ifdef __cplusplus
}
#endif /* __cplusplus */

/**
* @brief Class for mock Model object
*/
struct MockModel {
// Constructors
MockModel () = delete;
MockModel (std::string name, std::string path = {}, std::string app_info = {},
bool is_activated = false, std::string desc = {}, guint version = 0U)
: name_{ name }, path_{ path }, app_info_{ app_info },
is_activated_{ is_activated }, desc_{ desc }, version_{ version } {

};
// Copy constructor
MockModel (const MockModel &other)
: MockModel (other.name_, other.path_, other.app_info_,
other.is_activated_, other.desc_, other.version_){

};
// Move constructor
MockModel (MockModel &&other) noexcept
{
*this = std::move (other);
}
MockModel &operator= (const MockModel &other) = delete;
MockModel &operator= (MockModel &&other) noexcept
{
if (this == &other)
return *this;

this->name_ = std::move (other.name_);
this->path_ = std::move (other.path_);
this->app_info_ = std::move (other.app_info_);
this->is_activated_ = std::exchange (other.is_activated_, false);
this->desc_ = std::move (other.desc_);
this->version_ = std::exchange (other.version_, 0U);

return *this;
}

bool operator== (const MockModel &model) const
{
if (name_ == model.name_ && version_ == model.version_)
return true;
return false;
}

// Getters
std::string name () const
{
return name_;
}

std::string path () const
{
return path_;
}
std::string app_info () const
{
return app_info_;
}
bool is_activated () const
{
return is_activated_;
}

std::string desc () const
{
return desc_;
}

guint version () const
{
return version_;
}

// Setters
void path (const std::string &path)
{
path_ = path;
}

void is_activated (bool flag)
{
is_activated_ = flag;
}

void desc (const std::string &description)
{
desc_ = description;
}

void app_info (const std::string &info)
{
app_info_ = info;
}

void version (guint ver)
{
version_ = ver;
}

/**
* @brief Generate C-stringified JSON
* @return C-stringified JSON
*/
gchar *to_cstr_json ();

private:
std::string name_;
std::string path_{};
std::string app_info_{};
bool is_activated_{ false };
std::string desc_{};
guint version_{ 0U };
};

struct MockMLAgent {
MockMLAgent ()
{
}

bool add_model (MockModel model)
{
std::string key = model.name ();

{
auto range = models_dict_.equal_range (key);

for (auto it = range.first; it != range.second; ++it) {
if (model == it->second)
return false;
}
}

models_dict_.insert (std::make_pair (key, model));

return true;
}

MockModel *get_model (const gchar *name, guint version)
{
std::string key{ name };
auto range = models_dict_.equal_range (key);

for (auto it = range.first; it != range.second; ++it) {
if (version == it->second.version ())
return &it->second;
}

return nullptr;
}

private:
std::unordered_multimap<std::string, MockModel> models_dict_;
};

#endif /* __NNS_MLAGENT_MOCK_H__ */
Loading

0 comments on commit 7be044d

Please sign in to comment.