Skip to content

Commit

Permalink
[OV JS] Add more InferRequest tests and validation (openvinotoolkit#2…
Browse files Browse the repository at this point in the history
…3747)

### Details:
 - Create functions for validation in a separate file (_validation.hpp_)
 - New overload to _cast_to_tensor_ function
- Test for scenarios when an object passed to `setInputTensor()`
`infer()` `setOutputTensor()` `setTensor()` is not a Tensor.
- Remove `Test setInputTensor() - pass number as a single arg` -
overlaps with `Test setInputTensor throws when passed object is not a
Tensor.`

### Tickets:
 - *120315*
  • Loading branch information
almilosz authored and alvoron committed Apr 29, 2024
1 parent ba4fe0d commit 972c411
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 35 deletions.
1 change: 1 addition & 0 deletions src/bindings/js/node/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ add_library(${PROJECT_NAME} SHARED
${CMAKE_CURRENT_SOURCE_DIR}/src/preprocess/resize_algorithm.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/errors.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/helper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/type_validation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/tensor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/infer_request.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/compiled_model.cpp
Expand Down
3 changes: 3 additions & 0 deletions src/bindings/js/node/include/helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ ov::Tensor get_request_tensor(ov::InferRequest& infer_request, const size_t idx)
/** @brief Creates ov::tensor from TensorWrap Object */
ov::Tensor cast_to_tensor(const Napi::Value& value);

/** @brief Creates ov::tensor from Napi::CallbackInfo value at specified index. */
ov::Tensor cast_to_tensor(const Napi::CallbackInfo& info, int index);

/** @brief Creates ov::tensor from TypedArray using given shape and element type*/
ov::Tensor cast_to_tensor(const Napi::TypedArray& data, const ov::Shape& shape, const ov::element::Type_t& type);

Expand Down
11 changes: 11 additions & 0 deletions src/bindings/js/node/include/type_validation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#pragma once
#include <napi.h>

#include "node/include/addon.hpp"
#include "openvino/openvino.hpp"

/** @brief Checks if Napi::Value is a TensorWrap.*/
bool is_tensor(const Napi::Env& env, const Napi::Value& value);
9 changes: 9 additions & 0 deletions src/bindings/js/node/src/helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "node/include/helper.hpp"

#include "node/include/tensor.hpp"
#include "node/include/type_validation.hpp"

const std::vector<std::string>& get_supported_types() {
static const std::vector<std::string> supported_element_types =
Expand Down Expand Up @@ -303,6 +304,14 @@ ov::Tensor cast_to_tensor(const Napi::Value& value) {
}
}

ov::Tensor cast_to_tensor(const Napi::CallbackInfo& info, int index) {
if (!is_tensor(info.Env(), info[index])) {
OPENVINO_THROW(std::string("Argument #" + std::to_string(index) + " must be a Tensor."));
}
const auto tensor_wrap = Napi::ObjectWrap<TensorWrap>::Unwrap(info[index].ToObject());
return tensor_wrap->get_tensor();
}

ov::Tensor cast_to_tensor(const Napi::TypedArray& typed_array,
const ov::Shape& shape,
const ov::element::Type_t& type) {
Expand Down
56 changes: 31 additions & 25 deletions src/bindings/js/node/src/infer_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,39 +53,45 @@ Napi::Object InferRequestWrap::wrap(Napi::Env env, ov::InferRequest infer_reques
}

void InferRequestWrap::set_tensor(const Napi::CallbackInfo& info) {
if (info.Length() != 2 || !info[0].IsString() || !info[1].IsObject()) {
reportError(info.Env(), "InferRequest.setTensor() invalid argument.");
} else {
std::string name = info[0].ToString();
auto tensorWrap = Napi::ObjectWrap<TensorWrap>::Unwrap(info[1].ToObject());
_infer_request.set_tensor(name, tensorWrap->get_tensor());
try {
if (info.Length() != 2 || !info[0].IsString() || !info[1].IsObject()) {
OPENVINO_THROW(std::string("InferRequest.setTensor() invalid argument."));
} else {
const std::string& name = info[0].ToString();
_infer_request.set_tensor(name, cast_to_tensor(info, 1));
}
} catch (std::exception& e) {
reportError(info.Env(), e.what());
}
}

void InferRequestWrap::set_input_tensor(const Napi::CallbackInfo& info) {
if (info.Length() == 1 && info[0].IsObject()) {
auto tensorWrap = Napi::ObjectWrap<TensorWrap>::Unwrap(info[0].ToObject());
_infer_request.set_input_tensor(tensorWrap->get_tensor());
} else if (info.Length() == 2 && info[0].IsNumber() && info[1].IsObject()) {
auto idx = info[0].ToNumber().Int32Value();
auto tensorWrap = Napi::ObjectWrap<TensorWrap>::Unwrap(info[1].ToObject());
_infer_request.set_input_tensor(idx, tensorWrap->get_tensor());
} else {
reportError(info.Env(), "InferRequest.setInputTensor() invalid argument.");
try {
if (info.Length() == 1 && info[0].IsObject()) {
_infer_request.set_input_tensor(cast_to_tensor(info, 0));
} else if (info.Length() == 2 && info[0].IsNumber() && info[1].IsObject()) {
const auto idx = info[0].ToNumber().Int32Value();
_infer_request.set_input_tensor(idx, cast_to_tensor(info, 1));
} else {
OPENVINO_THROW(std::string("InferRequest.setInputTensor() invalid argument."));
}
} catch (std::exception& e) {
reportError(info.Env(), e.what());
}
}

void InferRequestWrap::set_output_tensor(const Napi::CallbackInfo& info) {
if (info.Length() == 1) {
auto tensorWrap = Napi::ObjectWrap<TensorWrap>::Unwrap(info[0].ToObject());
auto t = tensorWrap->get_tensor();
_infer_request.set_output_tensor(t);
} else if (info.Length() == 2 && info[0].IsNumber() && info[1].IsObject()) {
auto idx = info[0].ToNumber().Int32Value();
auto tensorWrap = Napi::ObjectWrap<TensorWrap>::Unwrap(info[1].ToObject());
_infer_request.set_output_tensor(idx, tensorWrap->get_tensor());
} else {
reportError(info.Env(), "InferRequest.setOutputTensor() invalid argument.");
try {
if (info.Length() == 1 && info[0].IsObject()) {
_infer_request.set_output_tensor(cast_to_tensor(info, 0));
} else if (info.Length() == 2 && info[0].IsNumber() && info[1].IsObject()) {
const auto idx = info[0].ToNumber().Int32Value();
_infer_request.set_output_tensor(idx, cast_to_tensor(info, 1));
} else {
OPENVINO_THROW(std::string("InferRequest.setOutputTensor() invalid argument."));
}
} catch (std::exception& e) {
reportError(info.Env(), e.what());
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/bindings/js/node/src/type_validation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#include "node/include/type_validation.hpp"

bool is_tensor(const Napi::Env& env, const Napi::Value& value) {
const auto& prototype = env.GetInstanceData<AddonData>()->tensor;
return value.ToObject().InstanceOf(prototype.Value().As<Napi::Function>());
}
76 changes: 66 additions & 10 deletions src/bindings/js/node/tests/infer_request.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ describe('InferRequest', () => {
['string', 'Cannot create a tensor from the passed Napi::Value.'],
[tensorData.slice(-10), 'Memory allocated using shape and element::type mismatch passed data\'s size'],
[new Float32Array(buffer, 4), 'TypedArray.byteOffset has to be equal to zero.'],
[{}, /Invalid argument/], // Test for object that is not Tensor
];

inputMessagePairs.forEach( ([tl, msg]) => {
Expand Down Expand Up @@ -114,22 +115,39 @@ describe('InferRequest', () => {
assert.deepStrictEqual(tensor.data[0], t1.data[0]);
});

it('Test setInputTensor(object) throws when passed object is not a Tensor.', () => {
assert.throws(
() => inferRequest.setInputTensor({}),
{message: /Argument #[0-9]+ must be a Tensor./}
);
});

it('Test setInputTensor(idx, tensor)', () => {
inferRequest.setInputTensor(0, tensor);
const t1 = inferRequest.getInputTensor();
assert.deepStrictEqual(tensor.data[0], t1.data[0]);
});

it('Test setInputTensor() - pass two tensors', () => {
it('Test setInputTensor(idx, tensor) throws', () => {
const testIdx = 10;
assert.throws (
() => inferRequest.setInputTensor(testIdx, tensor),
{message: /Input port for index [0-9]+ was not found!/}
);
});

it('Test setInputTensor(idx, object) throws when passed object is not a Tensor.', () => {
assert.throws(
() => inferRequest.setInputTensor(resTensor, tensor),
{message: 'InferRequest.setInputTensor() invalid argument.'});
() => inferRequest.setInputTensor(0, {}),
{message: /Argument #[0-9]+ must be a Tensor./}
);
});

it('Test setInputTensor() - pass number as a single arg', () => {
it('Test setInputTensor(tensor, tensor) throws', () => {
assert.throws(
() => inferRequest.setInputTensor(123),
{message: 'InferRequest.setInputTensor() invalid argument.'});
() => inferRequest.setInputTensor(resTensor, tensor),
{message: / invalid argument./}
);
});

it('Test setOutputTensor(tensor)', () => {
Expand All @@ -138,16 +156,38 @@ describe('InferRequest', () => {
assert.deepStrictEqual(resTensor.data[0], res2.data[0]);
});

it('Test setOutputTensor(object) throws when passed object is not a Tensor.', () => {
assert.throws(
() => inferRequest.setOutputTensor({}),
{message: /Argument #[0-9]+ must be a Tensor./}
);
});

it('Test setOutputTensor(idx, tensor) throws', () => {
const testIdx = 10;
assert.throws (
() => inferRequest.setOutputTensor(testIdx, tensor),
{message: /Output port for index [0-9]+ was not found!/}
);
});

it('Test setOutputTensor(idx, tensor)', () => {
inferRequest.setOutputTensor(0, resTensor);
const res2 = inferRequest.getOutputTensor();
assert.deepStrictEqual(resTensor.data[0], res2.data[0]);
});

it('Test setOutputTensor(idx, tensor) throws when passed object is not a Tensor.', () => {
assert.throws(
() => inferRequest.setOutputTensor(0, {}),
{message: /Argument #[0-9]+ must be a Tensor./}
);
});

it('Test setOutputTensor() - pass two tensors', () => {
assert.throws(
() => inferRequest.setOutputTensor(resTensor, tensor),
{message: 'InferRequest.setOutputTensor() invalid argument.'});
{message: / invalid argument./});
});

it('Test setTensor(string, tensor)', () => {
Expand All @@ -157,22 +197,38 @@ describe('InferRequest', () => {
assert.deepStrictEqual(resTensor.data[0], res2.data[0]);
});

it('Test setTensor(string, object) - throws', () => {
const testName = 'testName';
assert.throws(
() => inferRequest.setTensor(testName, tensor),
{message: /Port for tensor name testName was not found./});
});

it('Test setTensor(string, object) - throws', () => {
assert.throws(
() => inferRequest.setTensor('fc_out', {}),
{message: /Argument #[0-9]+ must be a Tensor./});
});

it('Test setTensor(string, tensor) - pass one arg', () => {
assert.throws(
() => inferRequest.setTensor('fc_out'),
{message: 'InferRequest.setTensor() invalid argument.'});
{message: / invalid argument./}
);
});

it('Test setTensor(string, tensor) - pass args in wrong order', () => {
assert.throws(
() => inferRequest.setTensor(resTensor, 'fc_out'),
{message: 'InferRequest.setTensor() invalid argument.'});
{message: / invalid argument./}
);
});

it('Test setTensor(string, tensor) - pass number as first arg', () => {
assert.throws(
() => inferRequest.setTensor(123, 'fc_out'),
{message: 'InferRequest.setTensor() invalid argument.'});
{message: / invalid argument/}
);
});

const irGetters = compiledModel.createInferRequest();
Expand Down

0 comments on commit 972c411

Please sign in to comment.