Added Import/Export functions to C_API (#8353)
* initial commit * [GNA] added export/import for c_api (need fixes) * [GNA] import/export with file * [GNA] fixed tests and function with memory * deleted unnecessary testing changes * fixed bug with const * fixed review comments * [GNA] changed testing model * Put memory buffer to istream directly * Replaced blob with array of bytes * Make config optional * Reverted load_network change * Fixed tests with null parameters * Changed path to models * Made config optional for other methods * Update ie_c_api.h * Deleted core param from export * Changed export signature * Reverted import changes(not needed) * fixed test Co-authored-by: mryzhov <mikhail.ryzhov@intel.com> Co-authored-by: Andrey Zaytsev <andrey.zaytsev@intel.com>
This commit is contained in:
parent
9b52f64445
commit
e7b28e83ac
@ -372,7 +372,7 @@ INFERENCE_ENGINE_C_API(void) ie_core_versions_free(ie_core_versions_t *vers);
|
||||
/**
|
||||
* @brief Reads the model from the .xml and .bin files of the IR. Use the ie_network_free() method to free memory.
|
||||
* @ingroup Core
|
||||
* @param core A pointer to ie_core_t instance.
|
||||
* @param core A pointer to the ie_core_t instance.
|
||||
* @param xml .xml file's path of the IR.
|
||||
* @param weights_file .bin file's path of the IR, if path is empty, will try to read bin file with the same name as xml and
|
||||
* if bin file with the same name was not found, will load IR without weights.
|
||||
@ -384,7 +384,7 @@ INFERENCE_ENGINE_C_API(IE_NODISCARD IEStatusCode) ie_core_read_network(ie_core_t
|
||||
/**
|
||||
* @brief Reads the model from an xml string and a blob of the bin part of the IR. Use the ie_network_free() method to free memory.
|
||||
* @ingroup Core
|
||||
* @param core A pointer to ie_core_t instance.
|
||||
* @param core A pointer to the ie_core_t instance.
|
||||
* @param xml_content Xml content of the IR.
|
||||
* @param xml_content_size Number of bytes in the xml content of the IR.
|
||||
* @param weight_blob Blob containing the bin part of the IR.
|
||||
@ -395,12 +395,50 @@ INFERENCE_ENGINE_C_API(IE_NODISCARD IEStatusCode) ie_core_read_network_from_memo
|
||||
const ie_blob_t *weight_blob, ie_network_t **network);
|
||||
|
||||
/**
|
||||
* @brief Creates an executable network from a network object. Users can create as many networks as they need and use
|
||||
* @brief Creates an executable network from a network previously exported to a file. Users can create as many networks as they need and use
|
||||
* them simultaneously (up to the limitation of the hardware resources). Use the ie_exec_network_free() method to free memory.
|
||||
* @ingroup Core
|
||||
* @param core A pointer to the ie_core_t instance.
|
||||
* @param file_name A path to the location of the exported file.
|
||||
* @param device_name A name of the device to load the network to.
|
||||
* @param config Device configuration.
|
||||
* @param exe_network A pointer to the newly created executable network.
|
||||
* @return Status code of the operation: OK(0) for success.
|
||||
*/
|
||||
INFERENCE_ENGINE_C_API(IE_NODISCARD IEStatusCode) ie_core_import_network(ie_core_t *core, const char *file_name, const char *device_name, \
|
||||
const ie_config_t *config, ie_executable_network_t **exe_network);
|
||||
|
||||
/**
|
||||
* @brief Creates an executable network from a network previously exported to memory. Users can create as many networks as they need and use
|
||||
* them simultaneously (up to the limitation of the hardware resources). Use the ie_exec_network_free() method to free memory.
|
||||
* @ingroup Core
|
||||
* @param core A pointer to the ie_core_t instance.
|
||||
* @param content A pointer to content of the exported network.
|
||||
* @param content_size Number of bytes in the exported network.
|
||||
* @param device_name A name of the device to load the network to.
|
||||
* @param config Device configuration.
|
||||
* @param exe_network A pointer to the newly created executable network.
|
||||
* @return Status code of the operation: OK(0) for success.
|
||||
*/
|
||||
INFERENCE_ENGINE_C_API(IE_NODISCARD IEStatusCode) ie_core_import_network_from_memory(ie_core_t *core, const uint8_t *content, size_t content_size,
|
||||
const char *device_name, const ie_config_t *config, ie_executable_network_t **exe_network);
|
||||
|
||||
/**
|
||||
* @brief Exports an executable network to a .bin file.
|
||||
* @ingroup Core
|
||||
* @param exe_network A pointer to the newly created executable network.
|
||||
* @param file_name Path to the file to export the network to.
|
||||
* @return Status code of the operation: OK(0) for success.
|
||||
*/
|
||||
INFERENCE_ENGINE_C_API(IE_NODISCARD IEStatusCode) ie_core_export_network(ie_executable_network_t *exe_network, const char *file_name);
|
||||
|
||||
/**
|
||||
* @brief Creates an executable network from a given network object. Users can create as many networks as they need and use
|
||||
* them simultaneously (up to the limitation of the hardware resources). Use the ie_exec_network_free() method to free memory.
|
||||
* @ingroup Core
|
||||
* @param core A pointer to ie_core_t instance.
|
||||
* @param network A pointer to ie_network instance.
|
||||
* @param device_name Name of device to load network to.
|
||||
* @param core A pointer to the ie_core_t instance.
|
||||
* @param network A pointer to the input ie_network instance to create the executable network from.
|
||||
* @param device_name Name of the device to load the network to.
|
||||
* @param config Device configuration.
|
||||
* @param exe_network A pointer to the newly created executable network.
|
||||
* @return Status code of the operation: OK(0) for success.
|
||||
@ -412,7 +450,7 @@ INFERENCE_ENGINE_C_API(IE_NODISCARD IEStatusCode) ie_core_load_network(ie_core_t
|
||||
* @brief Reads model and creates an executable network from IR or ONNX file. Users can create as many networks as they need and use
|
||||
* them simultaneously (up to the limitation of the hardware resources). Use the ie_exec_network_free() method to free memory.
|
||||
* @ingroup Core
|
||||
* @param core A pointer to ie_core_t instance.
|
||||
* @param core A pointer to the ie_core_t instance.
|
||||
* @param xml .xml file's path of the IR. Weights file name will be calculated automatically
|
||||
* @param device_name Name of device to load network to.
|
||||
* @param config Device configuration.
|
||||
|
@ -9,10 +9,11 @@
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
#include <streambuf>
|
||||
#include <istream>
|
||||
#include <ie_extension.h>
|
||||
#include "inference_engine.hpp"
|
||||
#include "ie_compound_blob.h"
|
||||
@ -60,6 +61,47 @@ struct ie_network {
|
||||
IE::CNNNetwork object;
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct mem_stringbuf
|
||||
* @brief This struct puts memory buffer to stringbuf.
|
||||
*/
|
||||
struct mem_stringbuf : std::streambuf {
|
||||
mem_stringbuf(const char *buffer, size_t sz) {
|
||||
char * bptr(const_cast<char *>(buffer));
|
||||
setg(bptr, bptr, bptr + sz);
|
||||
}
|
||||
|
||||
pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which = std::ios_base::in) override {
|
||||
switch (dir) {
|
||||
case std::ios_base::beg:
|
||||
setg(eback(), eback() + off, egptr());
|
||||
break;
|
||||
case std::ios_base::end:
|
||||
setg(eback(), egptr() + off, egptr());
|
||||
break;
|
||||
case std::ios_base::cur:
|
||||
setg(eback(), gptr() + off, egptr());
|
||||
break;
|
||||
default:
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
return (gptr() < eback() || gptr() > egptr()) ? pos_type(off_type(-1)) : pos_type(gptr() - eback());
|
||||
}
|
||||
|
||||
pos_type seekpos(pos_type pos, std::ios_base::openmode which) override {
|
||||
return seekoff(pos, std::ios_base::beg, which);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct mem_istream
|
||||
* @brief This struct puts stringbuf buffer to istream.
|
||||
*/
|
||||
struct mem_istream: virtual mem_stringbuf, std::istream {
|
||||
mem_istream(const char * buffer, size_t sz) : mem_stringbuf(buffer, sz), std::istream(static_cast<std::streambuf *>(this)) {
|
||||
}
|
||||
};
|
||||
|
||||
std::map<IE::StatusCode, IEStatusCode> status_map = {{IE::StatusCode::GENERAL_ERROR, IEStatusCode::GENERAL_ERROR},
|
||||
{IE::StatusCode::INFER_NOT_STARTED, IEStatusCode::INFER_NOT_STARTED},
|
||||
{IE::StatusCode::NETWORK_NOT_LOADED, IEStatusCode::NETWORK_NOT_LOADED},
|
||||
@ -143,7 +185,6 @@ std::map<IE::ColorFormat, colorformat_e> colorformat_map = {{IE::ColorFormat::RA
|
||||
std::map<std::string, std::string> config2Map(const ie_config_t *config) {
|
||||
std::map<std::string, std::string> m;
|
||||
const ie_config_t *tmp = config;
|
||||
|
||||
while (tmp && tmp->name && tmp->value) {
|
||||
m[tmp->name] = tmp->value;
|
||||
tmp = tmp->next;
|
||||
@ -335,18 +376,71 @@ IEStatusCode ie_core_read_network_from_memory(ie_core_t *core, const uint8_t *xm
|
||||
return status;
|
||||
}
|
||||
|
||||
IEStatusCode ie_core_load_network(ie_core_t *core, const ie_network_t *network, const char *device_name, \
|
||||
IEStatusCode ie_core_import_network(ie_core_t *core, const char *file_name, const char *device_name,
|
||||
const ie_config_t *config, ie_executable_network_t **exe_network) {
|
||||
IEStatusCode status = IEStatusCode::OK;
|
||||
|
||||
if (core == nullptr || network == nullptr || device_name == nullptr || config == nullptr || exe_network == nullptr) {
|
||||
if (core == nullptr || file_name == nullptr || device_name == nullptr || exe_network == nullptr) {
|
||||
status = IEStatusCode::GENERAL_ERROR;
|
||||
return status;
|
||||
}
|
||||
|
||||
try {
|
||||
std::map<std::string, std::string> conf_map;
|
||||
conf_map = config2Map(config);
|
||||
std::map<std::string, std::string> conf_map = config2Map(config);
|
||||
std::unique_ptr<ie_executable_network_t> exe_net(new ie_executable_network_t);
|
||||
|
||||
exe_net->object = core->object.ImportNetwork(file_name, device_name, conf_map);
|
||||
*exe_network = exe_net.release();
|
||||
} CATCH_IE_EXCEPTIONS
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
IEStatusCode ie_core_import_network_from_memory(ie_core_t *core, const uint8_t *content, size_t content_size, const char *device_name,
|
||||
const ie_config_t *config, ie_executable_network_t **exe_network) {
|
||||
if (core == nullptr || content == nullptr || device_name == nullptr || exe_network == nullptr) {
|
||||
return IEStatusCode::GENERAL_ERROR;
|
||||
}
|
||||
|
||||
IEStatusCode status = IEStatusCode::OK;
|
||||
try {
|
||||
mem_istream model_stream(reinterpret_cast<const char*>(content), content_size);
|
||||
|
||||
std::map<std::string, std::string> conf_map = config2Map(config);
|
||||
std::unique_ptr<ie_executable_network_t> exe_net(new ie_executable_network_t);
|
||||
|
||||
exe_net->object = core->object.ImportNetwork(model_stream, device_name, conf_map);
|
||||
*exe_network = exe_net.release();
|
||||
} CATCH_IE_EXCEPTIONS
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
IEStatusCode ie_core_export_network(ie_executable_network_t *exe_network, const char *file_name) {
|
||||
IEStatusCode status = IEStatusCode::OK;
|
||||
|
||||
if (file_name == nullptr || exe_network == nullptr) {
|
||||
status = IEStatusCode::GENERAL_ERROR;
|
||||
return status;
|
||||
}
|
||||
try {
|
||||
exe_network->object.Export(file_name);
|
||||
} CATCH_IE_EXCEPTIONS
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
IEStatusCode ie_core_load_network(ie_core_t *core, const ie_network_t *network, const char *device_name, \
|
||||
const ie_config_t *config, ie_executable_network_t **exe_network) {
|
||||
IEStatusCode status = IEStatusCode::OK;
|
||||
|
||||
if (core == nullptr || network == nullptr || device_name == nullptr || exe_network == nullptr) {
|
||||
status = IEStatusCode::GENERAL_ERROR;
|
||||
return status;
|
||||
}
|
||||
|
||||
try {
|
||||
std::map<std::string, std::string> conf_map = config2Map(config);
|
||||
std::unique_ptr<ie_executable_network_t> exe_net(new ie_executable_network_t);
|
||||
|
||||
// create plugin in the registery and then create ExecutableNetwork.
|
||||
@ -361,14 +455,13 @@ IEStatusCode ie_core_load_network_from_file(ie_core_t *core, const char *xml, co
|
||||
const ie_config_t *config, ie_executable_network_t **exe_network) {
|
||||
IEStatusCode status = IEStatusCode::OK;
|
||||
|
||||
if (core == nullptr || xml == nullptr || device_name == nullptr || config == nullptr || exe_network == nullptr) {
|
||||
if (core == nullptr || xml == nullptr || device_name == nullptr || exe_network == nullptr) {
|
||||
status = IEStatusCode::GENERAL_ERROR;
|
||||
return status;
|
||||
}
|
||||
|
||||
try {
|
||||
std::map<std::string, std::string> conf_map;
|
||||
conf_map = config2Map(config);
|
||||
std::map<std::string, std::string> conf_map = config2Map(config);
|
||||
std::unique_ptr<ie_executable_network_t> exe_net(new ie_executable_network_t);
|
||||
|
||||
exe_net->object = core->object.LoadNetwork(xml, device_name, conf_map);
|
||||
|
@ -312,7 +312,7 @@ TEST(ie_core_read_network_from_memory, networkReadFromMemory) {
|
||||
|
||||
if (weights_blob != nullptr) {
|
||||
std::vector<uint8_t> xml_content(content_from_file(xml, false));
|
||||
|
||||
|
||||
ie_network_t *network = nullptr;
|
||||
IE_EXPECT_OK(ie_core_read_network_from_memory(core, xml_content.data(), xml_content.size(), weights_blob, &network));
|
||||
EXPECT_NE(nullptr, network);
|
||||
@ -321,6 +321,115 @@ TEST(ie_core_read_network_from_memory, networkReadFromMemory) {
|
||||
}
|
||||
ie_blob_free(&weights_blob);
|
||||
}
|
||||
ie_core_free(&core);
|
||||
}
|
||||
|
||||
TEST(ie_core_export_network_to_file, exportNetworktoFile) {
|
||||
ie_core_t *core = nullptr;
|
||||
IE_ASSERT_OK(ie_core_create("", &core));
|
||||
ASSERT_NE(nullptr, core);
|
||||
|
||||
ie_config_t config = {nullptr, nullptr, nullptr};
|
||||
ie_executable_network_t *exe_network = nullptr;
|
||||
|
||||
IE_EXPECT_OK(ie_core_load_network_from_file(core, xml, "HETERO:CPU", &config, &exe_network));
|
||||
EXPECT_NE(nullptr, exe_network);
|
||||
|
||||
std::string export_path = TestDataHelpers::generate_model_path("test_model", "exported_model.blob");
|
||||
IE_EXPECT_OK(ie_core_export_network(exe_network, export_path.c_str()));
|
||||
std::ifstream file(export_path.c_str());
|
||||
EXPECT_NE(file.peek(), std::ifstream::traits_type::eof());
|
||||
|
||||
EXPECT_NE(nullptr, exe_network);
|
||||
ie_exec_network_free(&exe_network);
|
||||
ie_core_free(&core);
|
||||
}
|
||||
|
||||
TEST(ie_core_import_network_from_memory, importNetworkFromMem) {
|
||||
ie_core_t *core = nullptr;
|
||||
IE_ASSERT_OK(ie_core_create("", &core));
|
||||
ASSERT_NE(nullptr, core);
|
||||
|
||||
ie_executable_network_t *exe_network = nullptr;
|
||||
|
||||
IE_EXPECT_OK(ie_core_load_network_from_file(core, xml, "HETERO:CPU", nullptr, &exe_network));
|
||||
EXPECT_NE(nullptr, exe_network);
|
||||
|
||||
std::string export_path = TestDataHelpers::generate_model_path("test_model", "exported_model.blob");
|
||||
IE_EXPECT_OK(ie_core_export_network(exe_network, export_path.c_str()));
|
||||
|
||||
std::vector<uchar> buffer(content_from_file(export_path.c_str(), true));
|
||||
ie_executable_network_t *network = nullptr;
|
||||
|
||||
IE_EXPECT_OK(ie_core_import_network_from_memory(core, buffer.data(), buffer.size(), "HETERO:CPU", nullptr, &network));
|
||||
EXPECT_NE(nullptr, network);
|
||||
if (network != nullptr) {
|
||||
ie_exec_network_free(&network);
|
||||
}
|
||||
if (exe_network != nullptr) {
|
||||
ie_exec_network_free(&exe_network);
|
||||
}
|
||||
ie_core_free(&core);
|
||||
}
|
||||
|
||||
TEST(ie_core_import_network_from_file, importNetworkFromFile) {
|
||||
ie_core_t *core = nullptr;
|
||||
IE_ASSERT_OK(ie_core_create("", &core));
|
||||
ASSERT_NE(nullptr, core);
|
||||
|
||||
ie_config_t conf = {nullptr, nullptr, nullptr};
|
||||
|
||||
ie_executable_network_t *exe_network = nullptr;
|
||||
IE_EXPECT_OK(ie_core_load_network_from_file(core, xml, "HETERO:CPU", &conf, &exe_network));
|
||||
EXPECT_NE(nullptr, exe_network);
|
||||
|
||||
std::string exported_model = TestDataHelpers::generate_model_path("test_model", "exported_model.blob");
|
||||
IE_EXPECT_OK(ie_core_export_network(exe_network, exported_model.c_str()));
|
||||
std::ifstream file(exported_model);
|
||||
EXPECT_NE(file.peek(), std::ifstream::traits_type::eof());
|
||||
|
||||
IE_EXPECT_OK(ie_core_import_network(core, exported_model.c_str(), "HETERO:CPU", &conf, &exe_network));
|
||||
EXPECT_NE(nullptr, exe_network);
|
||||
ie_exec_network_free(&exe_network);
|
||||
ie_core_free(&core);
|
||||
}
|
||||
|
||||
TEST(ie_core_import_network_from_file, importNetwork_errorHandling) {
|
||||
ie_core_t *core = nullptr;
|
||||
IE_ASSERT_OK(ie_core_create("", &core));
|
||||
ASSERT_NE(nullptr, core);
|
||||
|
||||
ie_config_t config = {nullptr, nullptr, nullptr};
|
||||
|
||||
ie_executable_network_t *network = nullptr;
|
||||
IE_EXPECT_OK(ie_core_load_network_from_file(core, xml, "HETERO:CPU", &config, &network));
|
||||
EXPECT_NE(nullptr, network);
|
||||
|
||||
std::string exported_model = TestDataHelpers::generate_model_path("test_model", "exported_model.blob");
|
||||
IE_EXPECT_OK(ie_core_export_network(network, exported_model.c_str()));
|
||||
|
||||
ie_executable_network_t *exe_network = nullptr;
|
||||
IE_EXPECT_NOT_OK(ie_core_import_network(core, nullptr, "HETERO:CPU", &config, &exe_network));
|
||||
EXPECT_EQ(nullptr, exe_network);
|
||||
|
||||
IE_EXPECT_NOT_OK(ie_core_import_network(core, exported_model.c_str(), nullptr, &config, &exe_network));
|
||||
EXPECT_EQ(nullptr, exe_network);
|
||||
|
||||
IE_EXPECT_NOT_OK(ie_core_import_network(core, exported_model.c_str(), "HETERO:CPU", &config, nullptr));
|
||||
EXPECT_EQ(nullptr, exe_network);
|
||||
|
||||
IE_EXPECT_NOT_OK(ie_core_import_network(core, exported_model.c_str(), "UnregisteredDevice", &config, &exe_network));
|
||||
EXPECT_EQ(nullptr, exe_network);
|
||||
|
||||
IE_EXPECT_OK(ie_core_import_network(core, exported_model.c_str(), "HETERO:CPU", nullptr, &exe_network));
|
||||
EXPECT_NE(nullptr, exe_network);
|
||||
|
||||
if (network != nullptr) {
|
||||
ie_exec_network_free(&network);
|
||||
}
|
||||
if (exe_network != nullptr) {
|
||||
ie_exec_network_free(&exe_network);
|
||||
}
|
||||
|
||||
ie_core_free(&core);
|
||||
}
|
||||
@ -366,6 +475,24 @@ TEST(ie_core_load_network, loadNetworkNoConfig) {
|
||||
ie_core_free(&core);
|
||||
}
|
||||
|
||||
TEST(ie_core_load_network, loadNetworkNullConfig) {
|
||||
ie_core_t *core = nullptr;
|
||||
IE_ASSERT_OK(ie_core_create("", &core));
|
||||
ASSERT_NE(nullptr, core);
|
||||
|
||||
ie_network_t *network = nullptr;
|
||||
IE_EXPECT_OK(ie_core_read_network(core, xml, bin, &network));
|
||||
EXPECT_NE(nullptr, network);
|
||||
|
||||
ie_executable_network_t *exe_network = nullptr;
|
||||
IE_EXPECT_OK(ie_core_load_network(core, network, "CPU", nullptr, &exe_network));
|
||||
EXPECT_NE(nullptr, exe_network);
|
||||
|
||||
ie_exec_network_free(&exe_network);
|
||||
ie_network_free(&network);
|
||||
ie_core_free(&core);
|
||||
}
|
||||
|
||||
TEST(ie_core_load_network_from_file, loadNetworkNoConfig) {
|
||||
ie_core_t *core = nullptr;
|
||||
IE_ASSERT_OK(ie_core_create("", &core));
|
||||
@ -380,6 +507,20 @@ TEST(ie_core_load_network_from_file, loadNetworkNoConfig) {
|
||||
ie_core_free(&core);
|
||||
}
|
||||
|
||||
TEST(ie_core_load_network_from_file, loadNetworkNullConfig) {
|
||||
ie_core_t *core = nullptr;
|
||||
IE_ASSERT_OK(ie_core_create("", &core));
|
||||
ASSERT_NE(nullptr, core);
|
||||
|
||||
ie_executable_network_t *exe_network = nullptr;
|
||||
IE_EXPECT_OK(ie_core_load_network_from_file(core, xml, "CPU", nullptr, &exe_network));
|
||||
EXPECT_NE(nullptr, exe_network);
|
||||
|
||||
ie_exec_network_free(&exe_network);
|
||||
ie_core_free(&core);
|
||||
}
|
||||
|
||||
|
||||
TEST(ie_core_load_network_from_file, loadNetwork_errorHandling) {
|
||||
ie_core_t *core = nullptr;
|
||||
IE_ASSERT_OK(ie_core_create("", &core));
|
||||
@ -396,9 +537,6 @@ TEST(ie_core_load_network_from_file, loadNetwork_errorHandling) {
|
||||
IE_EXPECT_NOT_OK(ie_core_load_network_from_file(core, xml, nullptr, &config, &exe_network));
|
||||
EXPECT_EQ(nullptr, exe_network);
|
||||
|
||||
IE_EXPECT_NOT_OK(ie_core_load_network_from_file(core, xml, "CPU", nullptr, &exe_network));
|
||||
EXPECT_EQ(nullptr, exe_network);
|
||||
|
||||
IE_EXPECT_NOT_OK(ie_core_load_network_from_file(core, xml, "CPU", &config, nullptr));
|
||||
EXPECT_EQ(nullptr, exe_network);
|
||||
|
||||
|
@ -50,4 +50,4 @@ std::string generate_image_path(std::string dir, std::string filename) {
|
||||
std::string generate_ieclass_xml_path(std::string filename) {
|
||||
return getModelPathNonFatal() + kPathSeparator + "ie_class" + kPathSeparator + filename;
|
||||
}
|
||||
} // namespace TestDataHelpers
|
||||
} // namespace TestDataHelpers
|
||||
|
Loading…
Reference in New Issue
Block a user