From 0450a2ef8cc7828b48e555c424ca0901dcb62188 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sun, 20 Oct 2024 07:44:46 +0200 Subject: [PATCH 01/20] expose save model --- src/bindings/js/node/include/addon.hpp | 15 +++++++++ src/bindings/js/node/lib/addon.ts | 3 ++ src/bindings/js/node/src/addon.cpp | 31 +++++++++++++++++++ src/bindings/js/node/tests/unit/basic.test.js | 7 +++++ 4 files changed, 56 insertions(+) diff --git a/src/bindings/js/node/include/addon.hpp b/src/bindings/js/node/include/addon.hpp index ee4864876ea73a..9a74efc8997bb2 100644 --- a/src/bindings/js/node/include/addon.hpp +++ b/src/bindings/js/node/include/addon.hpp @@ -18,6 +18,7 @@ struct AddonData { Napi::FunctionReference partial_shape; Napi::FunctionReference ppp; Napi::FunctionReference tensor; + Napi::FunctionReference save_model; }; void init_class(Napi::Env env, @@ -26,4 +27,18 @@ void init_class(Napi::Env env, Prototype func, Napi::FunctionReference& reference); +template +void init_function(Napi::Env env, + Napi::Object exports, + std::string func_name, + Callable func, + Napi::FunctionReference& reference) { + + const auto& napi_func = Napi::Function::New(env, func, func_name); + reference = Napi::Persistent(napi_func); + + exports.Set(func_name, napi_func); +} + + Napi::Object init_module(Napi::Env env, Napi::Object exports); diff --git a/src/bindings/js/node/lib/addon.ts b/src/bindings/js/node/lib/addon.ts index 24c9d780aa9f7e..c37ece37708cdc 100644 --- a/src/bindings/js/node/lib/addon.ts +++ b/src/bindings/js/node/lib/addon.ts @@ -675,6 +675,9 @@ export interface NodeAddon { resizeAlgorithm: typeof resizeAlgorithm; PrePostProcessor: PrePostProcessorConstructor; }; + + saveModel: (model:Model, path:string, compressToFp16?:boolean) => void; + element: typeof element; } diff --git a/src/bindings/js/node/src/addon.cpp b/src/bindings/js/node/src/addon.cpp index 4d43c8f27e3d8e..5d7d4856692db9 100644 --- a/src/bindings/js/node/src/addon.cpp +++ b/src/bindings/js/node/src/addon.cpp @@ -8,12 +8,15 @@ #include "node/include/compiled_model.hpp" #include "node/include/core_wrap.hpp" #include "node/include/element_type.hpp" +#include "node/include/errors.hpp" +#include "node/include/helper.hpp" #include "node/include/infer_request.hpp" #include "node/include/model_wrap.hpp" #include "node/include/node_output.hpp" #include "node/include/partial_shape_wrap.hpp" #include "node/include/preprocess/preprocess.hpp" #include "node/include/tensor.hpp" +#include "node/include/type_validation.hpp" #include "openvino/openvino.hpp" void init_class(Napi::Env env, @@ -27,6 +30,32 @@ void init_class(Napi::Env env, exports.Set(class_name, prototype); } +// void save_model(const std::shared_ptr& m, +// const std::string& output_model, +// bool compress_to_fp16) { +Napi::Value save_model(const Napi::CallbackInfo& info) { + std::vector allowed_signatures; + try { + if (ov::js::validate(info, allowed_signatures)) { + const auto& model = info[0].ToObject(); + const auto m = Napi::ObjectWrap::Unwrap(model); + auto path = js_to_cpp(info, 1); + ov::save_model(m->get_model(), path); + } else if (ov::js::validate(info, allowed_signatures)) { + const auto& model = info[0].ToObject(); + const auto m = Napi::ObjectWrap::Unwrap(model); + auto path = js_to_cpp(info, 1); + auto compress_to_fp16 = js_to_cpp(info, 2); + ov::save_model(m->get_model(), path, compress_to_fp16); + } else { + OPENVINO_THROW("'saveModel'", ov::js::get_parameters_error_msg(info, allowed_signatures)); + } + } catch (const std::exception& e) { + reportError(info.Env(), e.what()); + } + return info.Env().Undefined(); +} + /** @brief Initialize native add-on */ Napi::Object init_module(Napi::Env env, Napi::Object exports) { auto addon_data = new AddonData(); @@ -41,6 +70,8 @@ Napi::Object init_module(Napi::Env env, Napi::Object exports) { init_class(env, exports, "ConstOutput", &Output::get_class, addon_data->const_output); init_class(env, exports, "PartialShape", &PartialShapeWrap::get_class, addon_data->partial_shape); + init_function(env, exports, "saveModel", save_model, addon_data->save_model); + preprocess::init(env, exports); element::init(env, exports); diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 0717b430ae002d..1e095c4be18a67 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -7,6 +7,7 @@ const assert = require('assert'); const { describe, it, before, beforeEach } = require('node:test'); const { testModels, getModelPath, isModelAvailable } = require('./utils.js'); const epsilon = 0.5; +const outDir = './tests/out'; describe('ov basic tests.', () => { let testXml = null; @@ -331,5 +332,11 @@ describe('ov basic tests.', () => { /'importModel' method called with incorrect parameters./, ); }); + + it('Test saveModel(model, path)', () => { + const path = `${outDir}/saved_model.xml`; + + ov.saveModel(modelLike[0], path); + }); }); }); From e0897570c7b862074f86d798a807b7035724d83a Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sun, 20 Oct 2024 11:32:33 +0200 Subject: [PATCH 02/20] add bool type validation --- src/bindings/js/node/include/type_validation.hpp | 6 ++++++ src/bindings/js/node/src/type_validation.cpp | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/bindings/js/node/include/type_validation.hpp b/src/bindings/js/node/include/type_validation.hpp index 4d5e4d3ba988b0..3633134c267c0d 100644 --- a/src/bindings/js/node/include/type_validation.hpp +++ b/src/bindings/js/node/include/type_validation.hpp @@ -29,6 +29,9 @@ const char* get_attr_type(); template <> const char* get_attr_type(); +template <> +const char* get_attr_type(); + template <> const char* get_attr_type>(); @@ -52,6 +55,9 @@ bool validate_value(const Napi::Env& env, const Napi::Value& value template <> bool validate_value(const Napi::Env& env, const Napi::Value& value); +template <> +bool validate_value(const Napi::Env& env, const Napi::Value& value); + template <> bool validate_value>(const Napi::Env& env, const Napi::Value& value); diff --git a/src/bindings/js/node/src/type_validation.cpp b/src/bindings/js/node/src/type_validation.cpp index 4852e0e4051c2a..3d1ba5f7a0f103 100644 --- a/src/bindings/js/node/src/type_validation.cpp +++ b/src/bindings/js/node/src/type_validation.cpp @@ -85,6 +85,11 @@ const char* get_attr_type() { return NapiArg::get_type_name(napi_object); } +template <> +const char* get_attr_type() { + return NapiArg::get_type_name(napi_boolean); +} + template <> const char* get_attr_type>() { return BindingTypename::BUFFER; @@ -115,6 +120,11 @@ bool validate_value(const Napi::Env& env, const Napi::Value& value return napi_object == value.Type(); } +template <> +bool validate_value(const Napi::Env& env, const Napi::Value& value) { + return napi_boolean == value.Type(); +} + template <> bool validate_value>(const Napi::Env& env, const Napi::Value& value) { return value.IsBuffer(); From 6d427286ff46de09e8f192f679542eb4b2c9d376 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sun, 20 Oct 2024 11:33:49 +0200 Subject: [PATCH 03/20] add docs, simple tests, and type conversion --- src/bindings/js/node/include/addon.hpp | 6 +++- src/bindings/js/node/lib/addon.ts | 9 +++++- src/bindings/js/node/src/addon.cpp | 10 +++---- src/bindings/js/node/tests/unit/basic.test.js | 28 +++++++++++++------ src/bindings/js/node/tests/unit/utils.js | 25 +++++++++++++++++ 5 files changed, 62 insertions(+), 16 deletions(-) diff --git a/src/bindings/js/node/include/addon.hpp b/src/bindings/js/node/include/addon.hpp index 9a74efc8997bb2..6e3d711a91dfe8 100644 --- a/src/bindings/js/node/include/addon.hpp +++ b/src/bindings/js/node/include/addon.hpp @@ -40,5 +40,9 @@ void init_function(Napi::Env env, exports.Set(func_name, napi_func); } - Napi::Object init_module(Napi::Env env, Napi::Object exports); + +/** + * @brief Saves model in a specified path. +*/ +Napi::Value save_model(const Napi::CallbackInfo& info); diff --git a/src/bindings/js/node/lib/addon.ts b/src/bindings/js/node/lib/addon.ts index c37ece37708cdc..704114f9c7db07 100644 --- a/src/bindings/js/node/lib/addon.ts +++ b/src/bindings/js/node/lib/addon.ts @@ -676,7 +676,14 @@ export interface NodeAddon { PrePostProcessor: PrePostProcessorConstructor; }; - saveModel: (model:Model, path:string, compressToFp16?:boolean) => void; + /** + * It saves model in a specified path. + * @param [model] Model that will be saved. + * @param [path] Path for saving the model. + * @param [compressToFp16] [OPTIONAL] Whether to compress the model + * to float16. Default is set to `true`. + */ + saveModel(model:Model, path:string, compressToFp16?:boolean) : void; element: typeof element; } diff --git a/src/bindings/js/node/src/addon.cpp b/src/bindings/js/node/src/addon.cpp index 5d7d4856692db9..3b2c8b2a0cd8c2 100644 --- a/src/bindings/js/node/src/addon.cpp +++ b/src/bindings/js/node/src/addon.cpp @@ -30,22 +30,19 @@ void init_class(Napi::Env env, exports.Set(class_name, prototype); } -// void save_model(const std::shared_ptr& m, -// const std::string& output_model, -// bool compress_to_fp16) { Napi::Value save_model(const Napi::CallbackInfo& info) { std::vector allowed_signatures; try { if (ov::js::validate(info, allowed_signatures)) { const auto& model = info[0].ToObject(); const auto m = Napi::ObjectWrap::Unwrap(model); - auto path = js_to_cpp(info, 1); + const auto path = js_to_cpp(info, 1); ov::save_model(m->get_model(), path); } else if (ov::js::validate(info, allowed_signatures)) { const auto& model = info[0].ToObject(); const auto m = Napi::ObjectWrap::Unwrap(model); - auto path = js_to_cpp(info, 1); - auto compress_to_fp16 = js_to_cpp(info, 2); + const auto path = js_to_cpp(info, 1); + const auto compress_to_fp16 = info[2].ToBoolean(); ov::save_model(m->get_model(), path, compress_to_fp16); } else { OPENVINO_THROW("'saveModel'", ov::js::get_parameters_error_msg(info, allowed_signatures)); @@ -53,6 +50,7 @@ Napi::Value save_model(const Napi::CallbackInfo& info) { } catch (const std::exception& e) { reportError(info.Env(), e.what()); } + return info.Env().Undefined(); } diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 1e095c4be18a67..12d22768631665 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -5,9 +5,9 @@ const { addon: ov } = require('../..'); const assert = require('assert'); const { describe, it, before, beforeEach } = require('node:test'); -const { testModels, getModelPath, isModelAvailable } = require('./utils.js'); +const { testModels, compareModels, getModelPath, isModelAvailable } = require('./utils.js'); const epsilon = 0.5; -const outDir = './tests/out'; +const outDir = 'tests/unit/out'; describe('ov basic tests.', () => { let testXml = null; @@ -34,6 +34,24 @@ describe('ov basic tests.', () => { assert.ok(devices.includes('CPU')); }); + describe('ov.saveModel()', () => { + it('saveModel(model:Model, path:string, compressToFp16:bool=true)', () => { + const xmlPath = `${outDir}/${modelLike[0].getName()}_fp16.xml`; + assert.doesNotThrow(() => ov.saveModel(modelLike[0], xmlPath)); + + const savedModel = core.readModelSync(xmlPath); + assert.doesNotThrow(() => compareModels(modelLike[0], savedModel)); + }); + + it('saveModel(model:Model, path:string, compressToFp16:bool)', () => { + const xmlPath = `${outDir}/${modelLike[0].getName()}_fp32.xml`; + assert.doesNotThrow(() => ov.saveModel(modelLike[0], xmlPath, false)); + + const savedModel = core.readModelSync(xmlPath); + assert.doesNotThrow(() => compareModels(modelLike[0], savedModel)); + }); + }); + describe('Core.getVersions()', () => { it('getVersions(validDeviceName: string)', () => { const deviceVersion = core.getVersions('CPU'); @@ -332,11 +350,5 @@ describe('ov basic tests.', () => { /'importModel' method called with incorrect parameters./, ); }); - - it('Test saveModel(model, path)', () => { - const path = `${outDir}/saved_model.xml`; - - ov.saveModel(modelLike[0], path); - }); }); }); diff --git a/src/bindings/js/node/tests/unit/utils.js b/src/bindings/js/node/tests/unit/utils.js index c2089e30b4cdc8..124a37f52a2910 100644 --- a/src/bindings/js/node/tests/unit/utils.js +++ b/src/bindings/js/node/tests/unit/utils.js @@ -2,6 +2,7 @@ // Copyright (C) 2018-2024 Intel Corporation // SPDX-License-Identifier: Apache-2.0 +const { addon: ov } = require('../..'); const path = require('path'); const fs = require('node:fs/promises'); const { @@ -22,12 +23,36 @@ const testModels = { }; module.exports = { + compareModels, getModelPath, downloadTestModel, isModelAvailable, testModels, }; +function compareModels(model1, model2, compareNames=true) { + let msg = ''; + if (compareNames && model1.getFriendlyName() !== model2.getFriendlyName()) { + msg += 'Friendly names of models are not equal '; + msg += `model_one: ${model1.getFriendlyName()}, + model_two: ${model2.getFriendlyName()}`; + } + + if (model1.inputs.length !== model2.inputs.length) { + msg += 'Number of models\' inputs are not equal '; + msg += `model_one: ${model1.inputs.length}, `; + msg += `model_two: ${model2.inputs.length}`; + throw new Error(msg); + } + + if (model1.outputs.length !== model2.outputs.length) { + msg += 'Number of models\' outputs are not equal '; + msg += `model_one: ${model1.outputs.length}, + model_two: ${model2.outputs.length}`; + throw new Error(msg); + } +} + function getModelPath(isFP16 = false) { const modelName = `test_model_fp${isFP16 ? 16 : 32}`; From f3a057d1c50e250f3c695702590f1d9684134560 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 26 Oct 2024 13:43:16 +0200 Subject: [PATCH 04/20] add sync postfix and remove save_model from class prototypes --- src/bindings/js/node/include/addon.hpp | 8 ++------ src/bindings/js/node/src/addon.cpp | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/bindings/js/node/include/addon.hpp b/src/bindings/js/node/include/addon.hpp index 6e3d711a91dfe8..ca963e971b5a00 100644 --- a/src/bindings/js/node/include/addon.hpp +++ b/src/bindings/js/node/include/addon.hpp @@ -18,7 +18,6 @@ struct AddonData { Napi::FunctionReference partial_shape; Napi::FunctionReference ppp; Napi::FunctionReference tensor; - Napi::FunctionReference save_model; }; void init_class(Napi::Env env, @@ -31,11 +30,8 @@ template void init_function(Napi::Env env, Napi::Object exports, std::string func_name, - Callable func, - Napi::FunctionReference& reference) { - + Callable func) { const auto& napi_func = Napi::Function::New(env, func, func_name); - reference = Napi::Persistent(napi_func); exports.Set(func_name, napi_func); } @@ -45,4 +41,4 @@ Napi::Object init_module(Napi::Env env, Napi::Object exports); /** * @brief Saves model in a specified path. */ -Napi::Value save_model(const Napi::CallbackInfo& info); +Napi::Value save_model_sync(const Napi::CallbackInfo& info); diff --git a/src/bindings/js/node/src/addon.cpp b/src/bindings/js/node/src/addon.cpp index 3b2c8b2a0cd8c2..ac3e3170ba68e5 100644 --- a/src/bindings/js/node/src/addon.cpp +++ b/src/bindings/js/node/src/addon.cpp @@ -30,7 +30,7 @@ void init_class(Napi::Env env, exports.Set(class_name, prototype); } -Napi::Value save_model(const Napi::CallbackInfo& info) { +Napi::Value save_model_sync(const Napi::CallbackInfo& info) { std::vector allowed_signatures; try { if (ov::js::validate(info, allowed_signatures)) { @@ -45,7 +45,7 @@ Napi::Value save_model(const Napi::CallbackInfo& info) { const auto compress_to_fp16 = info[2].ToBoolean(); ov::save_model(m->get_model(), path, compress_to_fp16); } else { - OPENVINO_THROW("'saveModel'", ov::js::get_parameters_error_msg(info, allowed_signatures)); + OPENVINO_THROW("'saveModelSync'", ov::js::get_parameters_error_msg(info, allowed_signatures)); } } catch (const std::exception& e) { reportError(info.Env(), e.what()); @@ -68,7 +68,7 @@ Napi::Object init_module(Napi::Env env, Napi::Object exports) { init_class(env, exports, "ConstOutput", &Output::get_class, addon_data->const_output); init_class(env, exports, "PartialShape", &PartialShapeWrap::get_class, addon_data->partial_shape); - init_function(env, exports, "saveModel", save_model, addon_data->save_model); + init_function(env, exports, "saveModelSync", save_model_sync); preprocess::init(env, exports); element::init(env, exports); From 95c180aea908b71cc16ee23fc30f4a09e0cb853c Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 26 Oct 2024 13:45:13 +0200 Subject: [PATCH 05/20] update doc strings in addon.ts --- src/bindings/js/node/lib/addon.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/bindings/js/node/lib/addon.ts b/src/bindings/js/node/lib/addon.ts index 704114f9c7db07..a5bcb76ce90c87 100644 --- a/src/bindings/js/node/lib/addon.ts +++ b/src/bindings/js/node/lib/addon.ts @@ -677,13 +677,18 @@ export interface NodeAddon { }; /** - * It saves model in a specified path. - * @param [model] Model that will be saved. - * @param [path] Path for saving the model. - * @param [compressToFp16] [OPTIONAL] Whether to compress the model - * to float16. Default is set to `true`. - */ - saveModel(model:Model, path:string, compressToFp16?:boolean) : void; + * It saves a model into IR files (xml and bin). + * Floating point weights are compressed to FP16 by default. + * This method saves a model to IR applying all necessary transformations + * that usually applied in model conversion flow provided by mo tool. + * Particularly, floating point weights are compressed to FP16, + * debug information in model nodes are cleaned up, etc. + * @param model The model which will be converted to IR representation and saved. + * @param path The path for saving the model. + * @param compressToFp16 Whether to compress + * floating point weights to FP16. Default is set to `true`. + */ + saveModelSync(model: Model, path: string, compressToFp16?: boolean) : void; element: typeof element; } From 20aef41c85a08fe6bdf24ee6a247045eb98c6e49 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 26 Oct 2024 13:45:54 +0200 Subject: [PATCH 06/20] refacotor compareModels func --- src/bindings/js/node/tests/unit/utils.js | 31 ++++++++++++------------ 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/bindings/js/node/tests/unit/utils.js b/src/bindings/js/node/tests/unit/utils.js index 124a37f52a2910..fffe2706a0bcc7 100644 --- a/src/bindings/js/node/tests/unit/utils.js +++ b/src/bindings/js/node/tests/unit/utils.js @@ -2,7 +2,6 @@ // Copyright (C) 2018-2024 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -const { addon: ov } = require('../..'); const path = require('path'); const fs = require('node:fs/promises'); const { @@ -30,26 +29,28 @@ module.exports = { testModels, }; -function compareModels(model1, model2, compareNames=true) { - let msg = ''; - if (compareNames && model1.getFriendlyName() !== model2.getFriendlyName()) { - msg += 'Friendly names of models are not equal '; - msg += `model_one: ${model1.getFriendlyName()}, - model_two: ${model2.getFriendlyName()}`; +function compareModels(model1, model2) { + const differences = []; + if (model1.getFriendlyName() !== model2.getFriendlyName()) { + differences.push('Friendly names of models are not equal ' + + `model_one: ${model1.getFriendlyName()},` + + `model_two: ${model2.getFriendlyName()}`) } if (model1.inputs.length !== model2.inputs.length) { - msg += 'Number of models\' inputs are not equal '; - msg += `model_one: ${model1.inputs.length}, `; - msg += `model_two: ${model2.inputs.length}`; - throw new Error(msg); + differences.push('Number of models\' inputs are not equal ' + + `model_one: ${model1.inputs.length}, ` + + `model_two: ${model2.inputs.length}`); } if (model1.outputs.length !== model2.outputs.length) { - msg += 'Number of models\' outputs are not equal '; - msg += `model_one: ${model1.outputs.length}, - model_two: ${model2.outputs.length}`; - throw new Error(msg); + differences.push('Number of models\' outputs are not equal ' + + `model_one: ${model1.outputs.length}, ` + + `model_two: ${model2.outputs.length}`) + } + + if (differences.length) { + throw new Error(differences.join('\n')); } } From 2ba653ebe51a8dc1043fa631bd96099265aadc33 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 26 Oct 2024 13:47:12 +0200 Subject: [PATCH 07/20] update outDir path --- src/bindings/js/node/tests/unit/basic.test.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 12d22768631665..fcac303bde5eb7 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -7,7 +7,7 @@ const assert = require('assert'); const { describe, it, before, beforeEach } = require('node:test'); const { testModels, compareModels, getModelPath, isModelAvailable } = require('./utils.js'); const epsilon = 0.5; -const outDir = 'tests/unit/out'; +const outDir = 'tests/unit/out/'; describe('ov basic tests.', () => { let testXml = null; @@ -36,16 +36,23 @@ describe('ov basic tests.', () => { describe('ov.saveModel()', () => { it('saveModel(model:Model, path:string, compressToFp16:bool=true)', () => { - const xmlPath = `${outDir}/${modelLike[0].getName()}_fp16.xml`; - assert.doesNotThrow(() => ov.saveModel(modelLike[0], xmlPath)); + const xmlPath = `${outDir}${modelLike[0].getName()}_fp16.xml`; + assert.doesNotThrow(() => ov.saveModelSync(modelLike[0], xmlPath, true)); const savedModel = core.readModelSync(xmlPath); assert.doesNotThrow(() => compareModels(modelLike[0], savedModel)); }); it('saveModel(model:Model, path:string, compressToFp16:bool)', () => { - const xmlPath = `${outDir}/${modelLike[0].getName()}_fp32.xml`; - assert.doesNotThrow(() => ov.saveModel(modelLike[0], xmlPath, false)); + const xmlPath = `${outDir}${modelLike[0].getName()}_fp32.xml`; + assert.doesNotThrow(() => ov.saveModelSync(modelLike[0], xmlPath)); + + const savedModel = core.readModelSync(xmlPath); + assert.doesNotThrow(() => compareModels(modelLike[0], savedModel)); + }); + it('saveModel(model:Model, path:string, compressToFp16:bool=false)', () => { + const xmlPath = `${outDir}${modelLike[0].getName()}_fp32.xml`; + assert.doesNotThrow(() => ov.saveModelSync(modelLike[0], xmlPath, false)); const savedModel = core.readModelSync(xmlPath); assert.doesNotThrow(() => compareModels(modelLike[0], savedModel)); From 29fb27f1eb8f64cbf28b8ec7630fda796fcee30d Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 26 Oct 2024 13:54:25 +0200 Subject: [PATCH 08/20] fix eslint --- src/bindings/js/node/tests/unit/basic.test.js | 7 ++++++- src/bindings/js/node/tests/unit/utils.js | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index fcac303bde5eb7..bd3f68b0a35571 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -5,7 +5,12 @@ const { addon: ov } = require('../..'); const assert = require('assert'); const { describe, it, before, beforeEach } = require('node:test'); -const { testModels, compareModels, getModelPath, isModelAvailable } = require('./utils.js'); +const { + testModels, + compareModels, + getModelPath, + isModelAvailable, +} = require('./utils.js'); const epsilon = 0.5; const outDir = 'tests/unit/out/'; diff --git a/src/bindings/js/node/tests/unit/utils.js b/src/bindings/js/node/tests/unit/utils.js index fffe2706a0bcc7..6396fea3babfeb 100644 --- a/src/bindings/js/node/tests/unit/utils.js +++ b/src/bindings/js/node/tests/unit/utils.js @@ -34,7 +34,7 @@ function compareModels(model1, model2) { if (model1.getFriendlyName() !== model2.getFriendlyName()) { differences.push('Friendly names of models are not equal ' + `model_one: ${model1.getFriendlyName()},` + - `model_two: ${model2.getFriendlyName()}`) + `model_two: ${model2.getFriendlyName()}`); } if (model1.inputs.length !== model2.inputs.length) { @@ -46,7 +46,7 @@ function compareModels(model1, model2) { if (model1.outputs.length !== model2.outputs.length) { differences.push('Number of models\' outputs are not equal ' + `model_one: ${model1.outputs.length}, ` + - `model_two: ${model2.outputs.length}`) + `model_two: ${model2.outputs.length}`); } if (differences.length) { From c6080a0df7c8b5c366b600e0e4e72bf0f08bbde9 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 26 Oct 2024 14:26:25 +0200 Subject: [PATCH 09/20] add tests with invalid parameters and fix eslint --- src/bindings/js/node/lib/addon.ts | 3 +- src/bindings/js/node/tests/unit/basic.test.js | 52 ++++++++++++++----- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/bindings/js/node/lib/addon.ts b/src/bindings/js/node/lib/addon.ts index a5bcb76ce90c87..e6f1b3c4041948 100644 --- a/src/bindings/js/node/lib/addon.ts +++ b/src/bindings/js/node/lib/addon.ts @@ -683,7 +683,8 @@ export interface NodeAddon { * that usually applied in model conversion flow provided by mo tool. * Particularly, floating point weights are compressed to FP16, * debug information in model nodes are cleaned up, etc. - * @param model The model which will be converted to IR representation and saved. + * @param model The model which will be + * converted to IR representation and saved. * @param path The path for saving the model. * @param compressToFp16 Whether to compress * floating point weights to FP16. Default is set to `true`. diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index bd3f68b0a35571..2e80890d0bf831 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -39,28 +39,54 @@ describe('ov basic tests.', () => { assert.ok(devices.includes('CPU')); }); - describe('ov.saveModel()', () => { - it('saveModel(model:Model, path:string, compressToFp16:bool=true)', () => { - const xmlPath = `${outDir}${modelLike[0].getName()}_fp16.xml`; - assert.doesNotThrow(() => ov.saveModelSync(modelLike[0], xmlPath, true)); + describe('ov.saveModelSync()', () => { + it('saveModelSync(model, path, compressToFp16=true)', () => { + const xmlPath = `${outDir}${model.getName()}_fp16.xml`; + assert.doesNotThrow(() => ov.saveModelSync(model, xmlPath, true)); const savedModel = core.readModelSync(xmlPath); - assert.doesNotThrow(() => compareModels(modelLike[0], savedModel)); + assert.doesNotThrow(() => compareModels(model, savedModel)); }); - it('saveModel(model:Model, path:string, compressToFp16:bool)', () => { - const xmlPath = `${outDir}${modelLike[0].getName()}_fp32.xml`; - assert.doesNotThrow(() => ov.saveModelSync(modelLike[0], xmlPath)); + it('saveModelSync(model, path, compressToFp16)', () => { + const xmlPath = `${outDir}${model.getName()}_fp32.xml`; + assert.doesNotThrow(() => ov.saveModelSync(model, xmlPath)); const savedModel = core.readModelSync(xmlPath); - assert.doesNotThrow(() => compareModels(modelLike[0], savedModel)); + assert.doesNotThrow(() => compareModels(model, savedModel)); }); - it('saveModel(model:Model, path:string, compressToFp16:bool=false)', () => { - const xmlPath = `${outDir}${modelLike[0].getName()}_fp32.xml`; - assert.doesNotThrow(() => ov.saveModelSync(modelLike[0], xmlPath, false)); + it('saveModelSync(model, path, compressToFp16=false)', () => { + const xmlPath = `${outDir}${model.getName()}_fp32.xml`; + assert.doesNotThrow(() => ov.saveModelSync(model, xmlPath, false)); const savedModel = core.readModelSync(xmlPath); - assert.doesNotThrow(() => compareModels(modelLike[0], savedModel)); + assert.doesNotThrow(() => compareModels(model, savedModel)); + }); + + it('saveModelSync(model) throws', () => { + const expectedMsg = ( + '\'saveModelSync\' method called with incorrect parameters.\n' + + 'Provided signature: (object) \n' + + 'Allowed signatures:\n' + + '- (Model, string)\n' + + '- (Model, string, boolean)\n' + ).replace(/[()]/g, '\\$&'); + + assert.throws( + () => ov.saveModelSync(model), + new RegExp(expectedMsg)); + }); + + it('saveModelSync(model, path) throws with incorrect path', () => { + const expectedMsg = ( + 'Path for xml file doesn\'t ' + + 'contains file name with \'xml\' extension' + ).replace(/[()]/g, '\\$&'); + + const noXmlPath = `${outDir}${model.getName()}_fp32`; + assert.throws( + () => ov.saveModelSync(model, noXmlPath), + new RegExp(expectedMsg)); }); }); From ce4bbc1969dc64c6e7337e13ae9c7de5da9f00e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20B=C5=82aszczyk?= <56601011+hub-bla@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:42:59 +0100 Subject: [PATCH 10/20] Update src/bindings/js/node/lib/addon.ts Co-authored-by: Vishniakov Nikolai --- src/bindings/js/node/lib/addon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings/js/node/lib/addon.ts b/src/bindings/js/node/lib/addon.ts index e6f1b3c4041948..6e57e10c4173b6 100644 --- a/src/bindings/js/node/lib/addon.ts +++ b/src/bindings/js/node/lib/addon.ts @@ -689,7 +689,7 @@ export interface NodeAddon { * @param compressToFp16 Whether to compress * floating point weights to FP16. Default is set to `true`. */ - saveModelSync(model: Model, path: string, compressToFp16?: boolean) : void; + saveModelSync(model: Model, path: string, compressToFp16?: boolean): void; element: typeof element; } From c8621f1e253ebb612c01a48e7f49be976b321ac5 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Wed, 30 Oct 2024 20:55:27 +0100 Subject: [PATCH 11/20] generate temp directory --- src/bindings/js/node/tests/unit/basic.test.js | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 2e80890d0bf831..339b8a6c98f260 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -4,7 +4,10 @@ const { addon: ov } = require('../..'); const assert = require('assert'); -const { describe, it, before, beforeEach } = require('node:test'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { after, describe, it, before, beforeEach } = require('node:test'); const { testModels, compareModels, @@ -12,7 +15,6 @@ const { isModelAvailable, } = require('./utils.js'); const epsilon = 0.5; -const outDir = 'tests/unit/out/'; describe('ov basic tests.', () => { let testXml = null; @@ -20,8 +22,16 @@ describe('ov basic tests.', () => { let model = null; let compiledModel = null; let modelLike = null; + let outDir = null; before(async () => { + fs.mkdtemp(path.join(os.tmpdir(), 'ov_js_out_'), (err, directory) => { + if (err) { + throw err; + } + outDir = directory; + }); + await isModelAvailable(testModels.testModelFP32); testXml = getModelPath().xml; }); @@ -33,6 +43,10 @@ describe('ov basic tests.', () => { modelLike = [model, compiledModel]; }); + after(() => { + fs.rmSync(outDir, { recursive: true }); + }); + it('Core.getAvailableDevices()', () => { const devices = core.getAvailableDevices(); @@ -41,7 +55,7 @@ describe('ov basic tests.', () => { describe('ov.saveModelSync()', () => { it('saveModelSync(model, path, compressToFp16=true)', () => { - const xmlPath = `${outDir}${model.getName()}_fp16.xml`; + const xmlPath = path.join(outDir, `${model.getName()}_fp16.xml`); assert.doesNotThrow(() => ov.saveModelSync(model, xmlPath, true)); const savedModel = core.readModelSync(xmlPath); @@ -49,14 +63,14 @@ describe('ov basic tests.', () => { }); it('saveModelSync(model, path, compressToFp16)', () => { - const xmlPath = `${outDir}${model.getName()}_fp32.xml`; + const xmlPath = path.join(outDir, `${model.getName()}_fp32.xml`); assert.doesNotThrow(() => ov.saveModelSync(model, xmlPath)); const savedModel = core.readModelSync(xmlPath); assert.doesNotThrow(() => compareModels(model, savedModel)); }); it('saveModelSync(model, path, compressToFp16=false)', () => { - const xmlPath = `${outDir}${model.getName()}_fp32.xml`; + const xmlPath = path.join(outDir, `${model.getName()}_fp32.xml`); assert.doesNotThrow(() => ov.saveModelSync(model, xmlPath, false)); const savedModel = core.readModelSync(xmlPath); From 03015d0a24b8345895f5e680b235667d8c0c00d6 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 9 Nov 2024 15:13:12 +0100 Subject: [PATCH 12/20] precede mkdtemp with await keyword --- src/bindings/js/node/tests/unit/basic.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 339b8a6c98f260..8c817b65725bf0 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -25,7 +25,7 @@ describe('ov basic tests.', () => { let outDir = null; before(async () => { - fs.mkdtemp(path.join(os.tmpdir(), 'ov_js_out_'), (err, directory) => { + await fs.mkdtemp(path.join(os.tmpdir(), 'ov_js_out_'), (err, directory) => { if (err) { throw err; } From 3a169005fd3ddb8818ba221b1f467422a20eac16 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 9 Nov 2024 15:26:08 +0100 Subject: [PATCH 13/20] Revert "precede mkdtemp with await keyword" This reverts commit 03015d0a24b8345895f5e680b235667d8c0c00d6. --- src/bindings/js/node/tests/unit/basic.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 8c817b65725bf0..339b8a6c98f260 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -25,7 +25,7 @@ describe('ov basic tests.', () => { let outDir = null; before(async () => { - await fs.mkdtemp(path.join(os.tmpdir(), 'ov_js_out_'), (err, directory) => { + fs.mkdtemp(path.join(os.tmpdir(), 'ov_js_out_'), (err, directory) => { if (err) { throw err; } From 677d6fda5cfc23f86ec54474c6a1cf383308792f Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 9 Nov 2024 15:41:22 +0100 Subject: [PATCH 14/20] remove console.log --- src/bindings/js/node/tests/unit/basic.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 339b8a6c98f260..51ae6ca9c8b619 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -24,14 +24,16 @@ describe('ov basic tests.', () => { let modelLike = null; let outDir = null; - before(async () => { + before(() => { fs.mkdtemp(path.join(os.tmpdir(), 'ov_js_out_'), (err, directory) => { if (err) { throw err; } outDir = directory; }); + }); + before(async () => { await isModelAvailable(testModels.testModelFP32); testXml = getModelPath().xml; }); From e813198ed08b9d95e248085ba92579b4c770d829 Mon Sep 17 00:00:00 2001 From: hub-bla Date: Sat, 16 Nov 2024 18:28:10 +0100 Subject: [PATCH 15/20] use async version of mkdtemp --- src/bindings/js/node/tests/unit/basic.test.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 51ae6ca9c8b619..4f3d06bbf9d404 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -24,16 +24,8 @@ describe('ov basic tests.', () => { let modelLike = null; let outDir = null; - before(() => { - fs.mkdtemp(path.join(os.tmpdir(), 'ov_js_out_'), (err, directory) => { - if (err) { - throw err; - } - outDir = directory; - }); - }); - before(async () => { + outDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'ov_js_out_')); await isModelAvailable(testModels.testModelFP32); testXml = getModelPath().xml; }); From e1fe2f0be36994522aeda23dad7c2dafd46a3d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20B=C5=82aszczyk?= <56601011+hub-bla@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:56:12 +0100 Subject: [PATCH 16/20] add sleep func Co-authored-by: Vishniakov Nikolai --- src/bindings/js/node/tests/unit/utils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bindings/js/node/tests/unit/utils.js b/src/bindings/js/node/tests/unit/utils.js index 6396fea3babfeb..7fb92c9ac5841e 100644 --- a/src/bindings/js/node/tests/unit/utils.js +++ b/src/bindings/js/node/tests/unit/utils.js @@ -53,7 +53,9 @@ function compareModels(model1, model2) { throw new Error(differences.join('\n')); } } - +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} function getModelPath(isFP16 = false) { const modelName = `test_model_fp${isFP16 ? 16 : 32}`; From 8cb2e42cab02c5d2af6f42794cf17b2d89d22248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20B=C5=82aszczyk?= <56601011+hub-bla@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:57:06 +0100 Subject: [PATCH 17/20] export sleep Co-authored-by: Vishniakov Nikolai --- src/bindings/js/node/tests/unit/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bindings/js/node/tests/unit/utils.js b/src/bindings/js/node/tests/unit/utils.js index 7fb92c9ac5841e..228cbb51634d56 100644 --- a/src/bindings/js/node/tests/unit/utils.js +++ b/src/bindings/js/node/tests/unit/utils.js @@ -23,6 +23,7 @@ const testModels = { module.exports = { compareModels, + sleep, getModelPath, downloadTestModel, isModelAvailable, From 7cbd82554247926811035cdbee93d73d7978f1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20B=C5=82aszczyk?= <56601011+hub-bla@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:57:28 +0100 Subject: [PATCH 18/20] import sleep Co-authored-by: Vishniakov Nikolai --- src/bindings/js/node/tests/unit/basic.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 4f3d06bbf9d404..8db7fed645b1dc 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -13,6 +13,7 @@ const { compareModels, getModelPath, isModelAvailable, + sleep, } = require('./utils.js'); const epsilon = 0.5; From 8d302e6cc9ec6fc29fd18141acf237da8a310257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20B=C5=82aszczyk?= <56601011+hub-bla@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:57:45 +0100 Subject: [PATCH 19/20] refactor after Co-authored-by: Vishniakov Nikolai --- src/bindings/js/node/tests/unit/basic.test.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bindings/js/node/tests/unit/basic.test.js b/src/bindings/js/node/tests/unit/basic.test.js index 8db7fed645b1dc..6b27b1fcdbdd63 100644 --- a/src/bindings/js/node/tests/unit/basic.test.js +++ b/src/bindings/js/node/tests/unit/basic.test.js @@ -38,8 +38,10 @@ describe('ov basic tests.', () => { modelLike = [model, compiledModel]; }); - after(() => { - fs.rmSync(outDir, { recursive: true }); + after(async () => { + // Wait to ensure the model file is released + await sleep(1); + await fs.promises.rm(outDir, { recursive: true }); }); it('Core.getAvailableDevices()', () => { From 5773d06c509a158331ec8a6c1fb8ac764e2526d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20B=C5=82aszczyk?= <56601011+hub-bla@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:54:44 +0100 Subject: [PATCH 20/20] format sleep Co-authored-by: Vishniakov Nikolai --- src/bindings/js/node/tests/unit/utils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bindings/js/node/tests/unit/utils.js b/src/bindings/js/node/tests/unit/utils.js index 228cbb51634d56..d971d7e5804472 100644 --- a/src/bindings/js/node/tests/unit/utils.js +++ b/src/bindings/js/node/tests/unit/utils.js @@ -54,9 +54,11 @@ function compareModels(model1, model2) { throw new Error(differences.join('\n')); } } + function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } + function getModelPath(isFP16 = false) { const modelName = `test_model_fp${isFP16 ? 16 : 32}`;