From bc5e90200708148e297f5b3054e7e64e74ca2ed5 Mon Sep 17 00:00:00 2001 From: "Anastasiya(Asya) Pronina" Date: Wed, 7 Aug 2024 15:26:01 +0100 Subject: [PATCH] Added MockNPU/CPU, FakeNPU/CPU and functional tests (#25236) ### Details: - This PR adds mock plugins to test NPUW against - This PR adds 9 functional behavior tests for NPUW ### Tickets: - *ticket-id* --- .../intel_npu/tests/functional/CMakeLists.txt | 6 + .../behavior/npuw/behavior_tests.cpp | 261 +++++++++++++++ .../behavior/npuw/behavior_tests.hpp | 51 +++ .../behavior/npuw/mocks/mock_plugins.cpp | 299 ++++++++++++++++++ .../behavior/npuw/mocks/mock_plugins.hpp | 155 +++++++++ .../behavior/npuw/mocks/register_in_ov.cpp | 35 ++ .../behavior/npuw/mocks/register_in_ov.hpp | 30 ++ 7 files changed, 837 insertions(+) create mode 100644 src/plugins/intel_npu/tests/functional/behavior/npuw/behavior_tests.cpp create mode 100644 src/plugins/intel_npu/tests/functional/behavior/npuw/behavior_tests.hpp create mode 100644 src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/mock_plugins.cpp create mode 100644 src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/mock_plugins.hpp create mode 100644 src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/register_in_ov.cpp create mode 100644 src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/register_in_ov.hpp diff --git a/src/plugins/intel_npu/tests/functional/CMakeLists.txt b/src/plugins/intel_npu/tests/functional/CMakeLists.txt index a2c47933c317a0..1d8bf947e63420 100644 --- a/src/plugins/intel_npu/tests/functional/CMakeLists.txt +++ b/src/plugins/intel_npu/tests/functional/CMakeLists.txt @@ -5,6 +5,10 @@ if(ENABLE_LTO) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON) endif() +if(SUGGEST_OVERRIDE_SUPPORTED) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-suggest-override") +endif() + set(TARGET_NAME ov_npu_func_tests) set(EXCLUDED_FUNC_TESTS_DIR "") set(OPTIONAL_FUNC_TESTS_INCLUDES "") @@ -56,6 +60,8 @@ if(MSVC) target_compile_options(${TARGET_NAME} PRIVATE /Zc:preprocessor) endif() +target_compile_definitions(${TARGET_NAME} PRIVATE CI_BUILD_NUMBER=\"mock_version\") + install( TARGETS ${TARGET_NAME} RUNTIME DESTINATION tests diff --git a/src/plugins/intel_npu/tests/functional/behavior/npuw/behavior_tests.cpp b/src/plugins/intel_npu/tests/functional/behavior/npuw/behavior_tests.cpp new file mode 100644 index 00000000000000..3471d832d3bf36 --- /dev/null +++ b/src/plugins/intel_npu/tests/functional/behavior/npuw/behavior_tests.cpp @@ -0,0 +1,261 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "behavior_tests.hpp" +#include "openvino/opsets/opset11.hpp" +#include "openvino/runtime/exec_model_info.hpp" +#include "openvino/runtime/internal_properties.hpp" +#include "openvino/runtime/make_tensor.hpp" +#include "openvino/runtime/properties.hpp" +#include "openvino/util/file_util.hpp" +#include "openvino/util/shared_object.hpp" +#include "npuw_private_properties.hpp" + +using namespace testing; +using namespace ov::npuw::tests; + +std::shared_ptr BehaviorTestsNPUW::create_example_model() { + auto param = std::make_shared(ov::element::i64, ov::PartialShape{1, 3, 2, 2}); + param->set_friendly_name("input"); + auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1}); + const_value->set_friendly_name("const_val"); + auto add = std::make_shared(param, const_value); + add->set_friendly_name("add"); + auto result = std::make_shared(add); + result->set_friendly_name("res"); + return std::make_shared(ov::ResultVector{result}, ov::ParameterVector{param}); +} + +TEST_F(BehaviorTestsNPUW, TestInfrastructureIsCorrect) { + // Set expectations first: + EXPECT_CALL(*npu_plugin, get_property).Times(AnyNumber()); + EXPECT_CALL(*npu_plugin, get_property(std::string("AVAILABLE_DEVICES"), _)).Times(1); + EXPECT_CALL(*cpu_plugin, get_property).Times(AnyNumber()); + EXPECT_CALL(*cpu_plugin, get_property(std::string("AVAILABLE_DEVICES"), _)).Times(1); + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + std::vector mock_reference_dev = {{"MockNPU.0"}, {"MockNPU.1"}, {"MockNPU.2"}, + {"MockCPU.0"}, {"MockCPU.1"}}; + auto available_devices = core.get_available_devices(); + for (auto device : available_devices) { + auto it = std::find(mock_reference_dev.begin(), mock_reference_dev.end(), device); + if (it != mock_reference_dev.end()) { + mock_reference_dev.erase(it); + } + } + + // All devices should be found + EXPECT_TRUE(mock_reference_dev.empty()); +} + +TEST_F(BehaviorTestsNPUW, CompilationIsSuccessful) { + // Set expectations first: + EXPECT_CALL(*npu_plugin, + compile_model(A&>(), _)).Times(1); + EXPECT_CALL(*cpu_plugin, + compile_model(A&>(), _)).Times(0); + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + auto npuw_properties = + ov::AnyMap{ov::intel_npu::use_npuw(true), + ov::intel_npu::npuw::devices("MockNPU")}; + EXPECT_NO_THROW(core.compile_model(model, "NPU", npuw_properties)); +} + +TEST_F(BehaviorTestsNPUW, CompilationIsFailSafe) { + // Set expectations first: + { + InSequence s; + + EXPECT_CALL(*npu_plugin, compile_model(A&>(), _)) + .Times(1) + .WillOnce(Throw(std::runtime_error("Compilation on MockNPU is failed"))); + EXPECT_CALL(*cpu_plugin, compile_model(A&>(), _)).Times(1); + } + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + auto npuw_properties = + ov::AnyMap{ov::intel_npu::use_npuw(true), + ov::intel_npu::npuw::devices("MockNPU,MockCPU")}; + EXPECT_NO_THROW(core.compile_model(model, "NPU", npuw_properties)); +} + +TEST_F(BehaviorTestsNPUW, CompilationIsFailed) { + // Set expectations first: + EXPECT_CALL(*npu_plugin, compile_model(A&>(), _)) + .Times(1) + .WillOnce(Throw(std::runtime_error("Compilation on MockNPU is failed"))); + EXPECT_CALL(*cpu_plugin, compile_model(A&>(), _)).Times(0); + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + auto npuw_properties = + ov::AnyMap{ov::intel_npu::use_npuw(true), + ov::intel_npu::npuw::devices("MockNPU")}; + EXPECT_ANY_THROW(core.compile_model(model, "NPU", npuw_properties)); +} + +TEST_F(BehaviorTestsNPUW, InferRequestCreationIsSuccessful) { + // Set expectations first: + EXPECT_CALL(*npu_plugin, compile_model(A&>(), _)).Times(1); + EXPECT_CALL(*cpu_plugin, compile_model(A&>(), _)).Times(0); + npu_plugin->set_expectations_to_comp_models(0, [](MockCompiledModel& model) { + EXPECT_CALL(model, create_sync_infer_request()).Times(1); + }); + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + auto npuw_properties = + ov::AnyMap{ov::intel_npu::use_npuw(true), + ov::intel_npu::npuw::devices("MockNPU")}; + auto compiled_model = core.compile_model(model, "NPU", npuw_properties); + EXPECT_NO_THROW(compiled_model.create_infer_request()); +} + +TEST_F(BehaviorTestsNPUW, InferRequestCreationIsFailSafe) { + // Set expectations first: + { + InSequence s; + EXPECT_CALL(*npu_plugin, compile_model(A&>(), _)).Times(1); + EXPECT_CALL(*cpu_plugin, compile_model(A&>(), _)).Times(1); + } + npu_plugin->set_expectations_to_comp_models(0, [](MockCompiledModel& model) { + EXPECT_CALL(model, create_sync_infer_request()) + .Times(1) + .WillOnce(Throw(std::runtime_error("Infer request creation on MockNPU is failed"))); + }); + cpu_plugin->set_expectations_to_comp_models(0, [](MockCompiledModel& model) { + EXPECT_CALL(model, create_sync_infer_request()).Times(1); + }); + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + auto npuw_properties = + ov::AnyMap{ov::intel_npu::use_npuw(true), + ov::intel_npu::npuw::devices("MockNPU,MockCPU")}; + auto compiled_model = core.compile_model(model, "NPU", npuw_properties); + EXPECT_NO_THROW(compiled_model.create_infer_request()); +} + +TEST_F(BehaviorTestsNPUW, InferRequestCreationIsFailed) { + // Set expectations first: + EXPECT_CALL(*npu_plugin, compile_model(A&>(), _)).Times(1); + EXPECT_CALL(*cpu_plugin, compile_model(A&>(), _)).Times(0); + + npu_plugin->set_expectations_to_comp_models(0, [](MockCompiledModel& model) { + EXPECT_CALL(model, create_sync_infer_request()) + .Times(1) + .WillOnce(Throw(std::runtime_error("Infer request creation on MockNPU is failed"))); + }); + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + auto npuw_properties = + ov::AnyMap{ov::intel_npu::use_npuw(true), + ov::intel_npu::npuw::devices("MockNPU")}; + auto compiled_model = core.compile_model(model, "NPU", npuw_properties); + EXPECT_ANY_THROW(compiled_model.create_infer_request()); +} + +TEST_F(BehaviorTestsNPUW, InferIsSuccessful) { + // Set expectations first: + EXPECT_CALL(*npu_plugin, compile_model(A&>(), _)).Times(1); + EXPECT_CALL(*cpu_plugin, compile_model(A&>(), _)).Times(0); + npu_plugin->set_expectations_to_comp_models(0, [](MockCompiledModel& model) { + EXPECT_CALL(model, create_sync_infer_request()).Times(1); + }); + npu_plugin->set_expectations_to_infer_reqs(0, [](MockInferRequest& request) { + EXPECT_CALL(request, infer()).Times(1); + }); + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + auto npuw_properties = + ov::AnyMap{ov::intel_npu::use_npuw(true), + ov::intel_npu::npuw::devices("MockNPU")}; + auto compiled_model = core.compile_model(model, "NPU", npuw_properties); + auto infer_request = compiled_model.create_infer_request(); + EXPECT_NO_THROW(infer_request.infer()); +} + +TEST_F(BehaviorTestsNPUW, InferIsFailSafe) { + // Set expectations first: + { + InSequence seq; + EXPECT_CALL(*npu_plugin, compile_model(A&>(), _)).Times(1); + EXPECT_CALL(*cpu_plugin, compile_model(A&>(), _)).Times(1); + } + npu_plugin->set_expectations_to_comp_models(0, [](MockCompiledModel& model) { + EXPECT_CALL(model, create_sync_infer_request()).Times(1); + }); + npu_plugin->set_expectations_to_infer_reqs(0, [](MockInferRequest& request) { + EXPECT_CALL(request, infer()) + .Times(1) + .WillOnce(Throw(std::runtime_error("Infer on MockNPU is failed"))); + }); + cpu_plugin->set_expectations_to_comp_models(0, [](MockCompiledModel& model) { + EXPECT_CALL(model, create_sync_infer_request()).Times(1); + }); + cpu_plugin->set_expectations_to_infer_reqs(0, [](MockInferRequest& request) { + EXPECT_CALL(request, infer()).Times(1); + }); + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + auto npuw_properties = + ov::AnyMap{ov::intel_npu::use_npuw(true), + ov::intel_npu::npuw::devices("MockNPU,MockCPU")}; + auto compiled_model = core.compile_model(model, "NPU", npuw_properties); + auto infer_request = compiled_model.create_infer_request(); + EXPECT_NO_THROW(infer_request.infer()); +} + +TEST_F(BehaviorTestsNPUW, InferIsFailed) { + // Set expectations first: + { + InSequence seq; + EXPECT_CALL(*npu_plugin, compile_model(A&>(), _)).Times(1); + EXPECT_CALL(*cpu_plugin, compile_model(A&>(), _)).Times(0); + } + npu_plugin->set_expectations_to_comp_models(0, [](MockCompiledModel& model) { + EXPECT_CALL(model, create_sync_infer_request()).Times(1); + }); + npu_plugin->set_expectations_to_infer_reqs(0, [](MockInferRequest& request) { + EXPECT_CALL(request, infer()) + .Times(1) + .WillOnce(Throw(std::runtime_error("Infer on MockNPU is failed"))); + }); + + // Register mock objects as plugins in OpenVINO: + register_mock_plugins_in_ov(); + + // Do the actual test: + auto npuw_properties = + ov::AnyMap{ov::intel_npu::use_npuw(true), + ov::intel_npu::npuw::devices("MockNPU")}; + auto compiled_model = core.compile_model(model, "NPU", npuw_properties); + auto infer_request = compiled_model.create_infer_request(); + EXPECT_ANY_THROW(infer_request.infer()); +} diff --git a/src/plugins/intel_npu/tests/functional/behavior/npuw/behavior_tests.hpp b/src/plugins/intel_npu/tests/functional/behavior/npuw/behavior_tests.hpp new file mode 100644 index 00000000000000..eb2cce0673f1c0 --- /dev/null +++ b/src/plugins/intel_npu/tests/functional/behavior/npuw/behavior_tests.hpp @@ -0,0 +1,51 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#pragma once + +#include + +#include + +#include "openvino/runtime/core.hpp" +#include "openvino/runtime/iplugin.hpp" +#include "openvino/runtime/make_tensor.hpp" + +#include "mocks/mock_plugins.hpp" +#include "mocks/register_in_ov.hpp" + +namespace ov { +namespace npuw { +namespace tests { + +class BehaviorTestsNPUW : public ::testing::Test { +public: + ov::Core core; + std::shared_ptr npu_plugin; + std::shared_ptr cpu_plugin; + std::shared_ptr model; + + + void SetUp() override { + model = create_example_model(); + npu_plugin = std::make_shared(); + npu_plugin->create_implementation(); + cpu_plugin = std::make_shared(); + cpu_plugin->create_implementation(); + } + + // Make sure it is called after expectations are set! + void register_mock_plugins_in_ov() { + m_shared_objects.push_back(reg_plugin(core, npu_plugin)); + m_shared_objects.push_back(reg_plugin(core, cpu_plugin)); + } + + std::shared_ptr create_example_model(); + +private: + std::vector> m_shared_objects; +}; + +} // namespace tests +} // namespace npuw +} // namespace ov diff --git a/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/mock_plugins.cpp b/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/mock_plugins.cpp new file mode 100644 index 00000000000000..666776a22d6800 --- /dev/null +++ b/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/mock_plugins.cpp @@ -0,0 +1,299 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include + +#include +#include + +#include "mock_plugins.hpp" +#include "openvino/core/version.hpp" +#include "openvino/pass/serialize.hpp" +#include "openvino/runtime/make_tensor.hpp" + +#include "npu_private_properties.hpp" +#include "npuw_private_properties.hpp" + +namespace ov { +namespace npuw { +namespace tests { +// Need to also mock async infer request or use infer() call to indicate that start_async() was called. +MockInferRequestBase::MockInferRequestBase(const std::shared_ptr& compiled_model) + : ov::ISyncInferRequest(compiled_model) { + OPENVINO_ASSERT(compiled_model); +} + +void MockInferRequestBase::create_implementation() { + auto inputs = get_compiled_model()->inputs(); + auto outputs = get_compiled_model()->outputs(); + auto in_tensors = create_tensors(inputs); + auto out_tensors = create_tensors(outputs); + + for (std::size_t i = 0; i < inputs.size(); ++i) { + set_tensor(inputs[i], ov::get_tensor_impl(in_tensors[i])); + } + + for (std::size_t i = 0; i < outputs.size(); ++i) { + set_tensor(outputs[i], ov::get_tensor_impl(out_tensors[i])); + } + + + ON_CALL(*this, query_state()).WillByDefault([]() -> std::vector> { + OPENVINO_NOT_IMPLEMENTED; + }); + ON_CALL(*this, get_profiling_info()).WillByDefault([]() -> std::vector { + OPENVINO_NOT_IMPLEMENTED; + }); +} + + +MockCompiledModelBase::MockCompiledModelBase(const std::shared_ptr& model, + std::shared_ptr plugin, + const ov::AnyMap& config, + std::shared_ptr, bool>> infer_req_expectations_ptr) + : ov::ICompiledModel(model, plugin), + m_infer_req_expectations_ptr(infer_req_expectations_ptr), + m_model(model), + m_config(config) { + } + + +void MockCompiledModelBase::create_implementation() { + ON_CALL(*this, inputs()).WillByDefault(testing::ReturnRefOfCopy(m_model->inputs())); + ON_CALL(*this, outputs()).WillByDefault(testing::ReturnRefOfCopy(m_model->outputs())); + ON_CALL(*this, create_infer_request).WillByDefault([this]() { + auto syncRequestImpl = create_sync_infer_request(); + return std::make_shared(syncRequestImpl, get_task_executor(), get_callback_executor()); + }); + ON_CALL(*this, export_model(testing::_)).WillByDefault([](std::ostream& s) { + OPENVINO_NOT_IMPLEMENTED; + }); + ON_CALL(*this, get_runtime_model).WillByDefault(testing::Return(m_model)); + ON_CALL(*this, set_property).WillByDefault([](const ov::AnyMap& properties) { + OPENVINO_NOT_IMPLEMENTED; + }); + ON_CALL(*this, get_property).WillByDefault([this](const std::string& name) -> ov::Any { + if (name == ov::supported_properties) { + const std::vector supported_properties = {ov::num_streams.name(), + ov::enable_profiling.name()}; + return decltype(ov::supported_properties)::value_type(supported_properties); + } else if (name == ov::num_streams) { + if (this->m_config.count(ov::internal::exclusive_async_requests.name())) { + auto exclusive_async_requests = + m_config.at(ov::internal::exclusive_async_requests.name()).as(); + if (exclusive_async_requests) + return ov::streams::Num(1); + } + return this->m_config.count(ov::num_streams.name()) ? m_config.at(ov::num_streams.name()) + : ov::streams::Num(1); + } else if (name == ov::enable_profiling) { + return this->m_config.count(ov::enable_profiling.name()) ? m_config.at(ov::enable_profiling.name()) : false; + } else { + OPENVINO_THROW("get property: " + name); + } + }); + ON_CALL(*this, create_sync_infer_request).WillByDefault([this]() { + std::lock_guard lock(m_mock_creation_mutex); + auto mock_sync_infer_request = std::make_shared( + std::dynamic_pointer_cast(shared_from_this())); + mock_sync_infer_request->create_implementation(); + if (m_infer_req_expectations_ptr) { + auto& expectation = m_infer_req_expectations_ptr->first; + auto& status = m_infer_req_expectations_ptr->second; + expectation(*mock_sync_infer_request); + status = true; // Expectation will be checked + } + return mock_sync_infer_request; + }); +} + + +template +MockPluginBase::MockPluginBase() { + m_plugin_name = std::string("openvino_") + std::string(device_name) + std::string("_plugin"); + const ov::Version version = {CI_BUILD_NUMBER, m_plugin_name.c_str()}; + set_device_name(device_name); + set_version(version); +} + +namespace { + ov::PropertyName RO_property(const std::string& propertyName) { + return ov::PropertyName(propertyName, ov::PropertyMutability::RO); + }; + + ov::PropertyName RW_property(const std::string& propertyName) { + return ov::PropertyName(propertyName, ov::PropertyMutability::RW); + }; +} // anonymous namespace + +template +void MockPluginBase::create_implementation() { + ON_CALL(*this, compile_model(testing::A&>(), testing::_)) + .WillByDefault([this](const std::shared_ptr& model, const ov::AnyMap& properties) { + std::lock_guard lock(m_mock_creation_mutex); + + std::shared_ptr, bool>> infer_req_expectations; + if (m_infer_reqs_to_expectations.count(m_num_compiled_models)) { + infer_req_expectations = m_infer_reqs_to_expectations[m_num_compiled_models]; + } + + auto mock_compiled_model = std::make_shared( + model, shared_from_this(), properties, + infer_req_expectations); + + mock_compiled_model->create_implementation(); + + if (m_models_to_expectations.count(m_num_compiled_models)) { + auto& expectation_and_status = m_models_to_expectations[m_num_compiled_models]; + auto& expectation = expectation_and_status.first; + auto& status = expectation_and_status.second; + expectation(*mock_compiled_model); + status = true; // Expectation will be checked + } + + ++m_num_compiled_models; + + return mock_compiled_model; + }); + ON_CALL(*this, compile_model(testing::A(), testing::_)) + .WillByDefault([](const std::string& model_path, + const ov::AnyMap& properties) -> std::shared_ptr { + OPENVINO_NOT_IMPLEMENTED; + }); + ON_CALL(*this, compile_model(testing::A&>(), testing::_, testing::_)) + .WillByDefault([](const std::shared_ptr& model, const ov::AnyMap& properties, + const ov::SoPtr& context) -> std::shared_ptr { + OPENVINO_NOT_IMPLEMENTED; + }); + ON_CALL(*this, set_property).WillByDefault([this](const ov::AnyMap& properties) { + for (const auto& it : properties) { + if (it.first == ov::num_streams.name()) + num_streams = it.second.as(); + else if (it.first == ov::internal::exclusive_async_requests.name()) + exclusive_async_requests = it.second.as(); + else if (it.first == ov::device::id.name()) + continue; + else + OPENVINO_THROW(get_device_name(), " set config: " + it.first); + } + }); + ON_CALL(*this, get_property).WillByDefault([this](const std::string& name, const ov::AnyMap& arguments) -> ov::Any { + std::vector device_ids; + for (int i = 0; i < DeviceType::num_device_ids; ++i) { + device_ids.push_back(std::to_string(i)); + }; + const static std::vector roProperties{ + RO_property(ov::supported_properties.name()), RO_property(ov::available_devices.name()), + RO_property(ov::device::uuid.name()), RO_property(ov::device::capabilities.name()) + }; + // the whole config is RW before network is loaded. + const static std::vector rwProperties{ + RW_property(ov::num_streams.name()) + }; + + std::string device_id; + if (arguments.find(ov::device::id.name()) != arguments.end()) { + device_id = arguments.find(ov::device::id.name())->second.as(); + } + if (name == ov::supported_properties) { + std::vector supportedProperties; + supportedProperties.reserve(roProperties.size() + rwProperties.size()); + supportedProperties.insert(supportedProperties.end(), roProperties.begin(), roProperties.end()); + supportedProperties.insert(supportedProperties.end(), rwProperties.begin(), rwProperties.end()); + + return decltype(ov::supported_properties)::value_type(supportedProperties); + } else if (name == ov::internal::supported_properties) { + return decltype(ov::internal::supported_properties)::value_type( + {ov::PropertyName{ov::internal::exclusive_async_requests.name(), ov::PropertyMutability::RW}}); + } else if (name == ov::internal::exclusive_async_requests) { + return decltype(ov::internal::exclusive_async_requests)::value_type{exclusive_async_requests}; + } else if (name == ov::device::uuid) { + ov::device::UUID uuid; + for (size_t i = 0; i < uuid.MAX_UUID_SIZE; i++) { + for (int j = 0; j < DeviceType::num_device_ids; ++j) + if (device_id == device_ids[j]) + uuid.uuid[i] = static_cast(i * (j + 1)); + } + return decltype(ov::device::uuid)::value_type{uuid}; + } else if (name == ov::available_devices) { + return decltype(ov::available_devices)::value_type(device_ids); + } else if (name == ov::device::capabilities) { + std::vector capabilities; + capabilities.push_back(ov::device::capability::EXPORT_IMPORT); + return decltype(ov::device::capabilities)::value_type(capabilities); + } else if (name == "SUPPORTED_CONFIG_KEYS") { // TODO: Remove this key + std::vector configs; + for (const auto& property : rwProperties) { + configs.emplace_back(property); + } + return configs; + } else if (name == ov::streams::num.name()) { + return decltype(ov::streams::num)::value_type{num_streams}; + } + OPENVINO_THROW("Unsupported property: ", name); + }); + ON_CALL(*this, create_context) + .WillByDefault([](const ov::AnyMap& remote_properties) -> ov::SoPtr { + OPENVINO_NOT_IMPLEMENTED; + }); + ON_CALL(*this, get_default_context) + .WillByDefault([](const ov::AnyMap& remote_properties) -> ov::SoPtr { + OPENVINO_NOT_IMPLEMENTED; + }); + ON_CALL(*this, import_model(testing::_, testing::_)) + .WillByDefault([](std::istream& model, const ov::AnyMap& properties) + -> std::shared_ptr { + OPENVINO_NOT_IMPLEMENTED; + }); + ON_CALL(*this, import_model(testing::_, testing::_, testing::_)) + .WillByDefault([](std::istream& model, const ov::SoPtr& context, + const ov::AnyMap& properties) -> std::shared_ptr { + OPENVINO_NOT_IMPLEMENTED; + }); + ON_CALL(*this, query_model) + .WillByDefault([](const std::shared_ptr& model, const ov::AnyMap& properties) + -> ov::SupportedOpsMap{ + OPENVINO_NOT_IMPLEMENTED; + }); +} + +template +void MockPluginBase::set_expectations_to_comp_models(int model_idx, std::function expectations) { + m_models_to_expectations[model_idx] = std::pair, bool>(expectations, false); +} + +template +void MockPluginBase::set_expectations_to_infer_reqs(int req_idx, + std::function expectations) { + m_infer_reqs_to_expectations[req_idx] = + std::make_shared, bool>>(expectations, false); +} + +template +MockPluginBase::~MockPluginBase() { + for (auto const& idx_to_expectation_and_status : m_models_to_expectations) { + auto idx = idx_to_expectation_and_status.first; + auto expectation_and_status = idx_to_expectation_and_status.second; + if (!expectation_and_status.second) { + ADD_FAILURE() << DeviceType::name << ": Expectation for model with index " << idx + << " was set, but model with that idx was not compiled"; + } + } + for (auto const& idx_to_expectation_and_status : m_infer_reqs_to_expectations) { + auto idx = idx_to_expectation_and_status.first; + auto expectation_and_status = *idx_to_expectation_and_status.second; + if (!expectation_and_status.second) { + ADD_FAILURE() << DeviceType::name << ": Expectation for request with index " << idx + << " was set, but request with that idx was not created"; + } + } +} + +template class MockPluginBase; +template class MockPluginBase; + +} // namespace tests +} // namespace npuw +} // namespace ov diff --git a/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/mock_plugins.hpp b/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/mock_plugins.hpp new file mode 100644 index 00000000000000..5c02f96ebd581e --- /dev/null +++ b/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/mock_plugins.hpp @@ -0,0 +1,155 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#pragma once + +#include +#include + +#include +#include + +#include "openvino/runtime/core.hpp" +#include "openvino/runtime/iplugin.hpp" +#include "openvino/runtime/internal_properties.hpp" +#include "openvino/util/shared_object.hpp" +#include "openvino/runtime/iasync_infer_request.hpp" +#include "openvino/runtime/so_ptr.hpp" +#include "openvino/core/any.hpp" + +// Based on: openvino/src/inference/tests/functional/caching_test.cpp + +namespace ov { +namespace npuw { +namespace tests { + +class MockCompiledModelBase; +using MockCompiledModel = testing::NiceMock; + +// Need to also mock async infer request or use infer() call to indicate that start_async() was called. +class MockInferRequestBase : public ov::ISyncInferRequest { +public: + MockInferRequestBase(const std::shared_ptr& compiled_model); + ~MockInferRequestBase() = default; + + MOCK_METHOD(void, infer, (), (override)); + MOCK_METHOD(std::vector>, query_state, (), (const, override)); + MOCK_METHOD(std::vector, get_profiling_info, (), (const, override)); + + // This must be called *before* the custom ON_CALL() statements. + void create_implementation(); + +private: + ov::TensorVector create_tensors(std::vector> data) { + ov::TensorVector tensors; + for (const auto& el : data) { + const ov::Shape& shape = el.get_shape(); + const ov::element::Type& precision = el.get_element_type(); + tensors.emplace_back(precision, shape); + } + return tensors; + } +}; +using MockInferRequest = testing::NiceMock; + +class MockCompiledModelBase : public ov::ICompiledModel { +public: + MockCompiledModelBase( + const std::shared_ptr& model, std::shared_ptr plugin, + const ov::AnyMap& config, + std::shared_ptr, bool>> infer_req_expectations); + + // Methods from a base class ov::ICompiledModel + MOCK_METHOD(const std::vector>&, outputs, (), (const, override)); + MOCK_METHOD(const std::vector>&, inputs, (), (const, override)); + MOCK_METHOD(std::shared_ptr, create_infer_request, (), (const, override)); + MOCK_METHOD(void, export_model, (std::ostream& model), (const, override)); + MOCK_METHOD(std::shared_ptr, get_runtime_model, (), (const, override)); + MOCK_METHOD(void, set_property, (const ov::AnyMap& properties), (override)); + MOCK_METHOD(ov::Any, get_property, (const std::string& name), (const, override)); + MOCK_METHOD(std::shared_ptr, create_sync_infer_request, (), (const, override)); + + // This must be called *before* the custom ON_CALL() statements. + void create_implementation(); + +private: + std::mutex m_mock_creation_mutex; + std::shared_ptr, bool>> m_infer_req_expectations_ptr; + + std::shared_ptr m_model; + ov::AnyMap m_config; +}; + +template +class MockPluginBase : public ov::IPlugin { +private: + static constexpr const char* device_name = DeviceType::name; +public: + MockPluginBase(); + + // Normal mock method definitions using gMock. + // FIXME: There is an issue with mocking of multiple-arguments functions below with generic MOCK_METHOD macro, + // on Windows. So, old-style MOCK_METHODn macros are used instead. + MOCK_CONST_METHOD2_T(compile_model, + std::shared_ptr(const std::shared_ptr& model, + const ov::AnyMap& properties)); + MOCK_CONST_METHOD2_T(compile_model, + std::shared_ptr(const std::string& model_path, + const ov::AnyMap& properties)); + MOCK_CONST_METHOD3_T(compile_model, + std::shared_ptr(const std::shared_ptr& model, + const ov::AnyMap& properties, + const ov::SoPtr& context)); + MOCK_CONST_METHOD2_T(get_property, ov::Any(const std::string& name, const ov::AnyMap& arguments)); + MOCK_CONST_METHOD2_T(import_model, + std::shared_ptr(std::istream& model, + const ov::AnyMap& properties)); + MOCK_CONST_METHOD3_T(import_model, + std::shared_ptr(std::istream& model, + const ov::SoPtr& context, + const ov::AnyMap& properties)); + MOCK_CONST_METHOD2_T(query_model, + ov::SupportedOpsMap(const std::shared_ptr& model, + const ov::AnyMap& properties)); + + MOCK_METHOD(void, set_property, (const ov::AnyMap& properties), (override)); + MOCK_METHOD(ov::SoPtr, create_context, (const ov::AnyMap& remote_properties), (const, override)); + MOCK_METHOD(ov::SoPtr, get_default_context, (const ov::AnyMap& remote_properties), (const, override)); + + // This must be called *before* the custom ON_CALL() statements. + void create_implementation(); + void set_expectations_to_comp_models(int model_idx, std::function expectations); + void set_expectations_to_infer_reqs(int req_idx, std::function expectations); + + ~MockPluginBase() override; + +private: + std::string m_plugin_name; + std::mutex m_mock_creation_mutex; // Internal gmock object registration is not thread-safe + int m_num_compiled_models{}; + // TODO: Make thread-safe and simplify. + std::map, bool>> m_models_to_expectations; + std::map, bool>>> m_infer_reqs_to_expectations; + + // Properties + int32_t num_streams{0}; + bool exclusive_async_requests = false; +}; + +namespace mocks { +struct Npu { + static constexpr const char* name = "MockNPU"; + static constexpr int num_device_ids = 3; +}; +struct Cpu { + static constexpr const char* name = "MockCPU"; + static constexpr int num_device_ids = 2; +}; +} //namespace mocks + +using MockNpuPlugin = testing::NiceMock>; +using MockCpuPlugin = testing::NiceMock>; + +} // namespace tests +} // namespace npuw +} // namespace ov diff --git a/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/register_in_ov.cpp b/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/register_in_ov.cpp new file mode 100644 index 00000000000000..bc916e652fdce7 --- /dev/null +++ b/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/register_in_ov.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "register_in_ov.hpp" + +#include + +namespace ov { +namespace npuw { +namespace tests { + +std::shared_ptr reg_plugin(ov::Core& core, std::shared_ptr& plugin) { + std::string mock_engine_path = ov::util::make_plugin_library_name(ov::test::utils::getExecutableDirectory(), + std::string("mock_engine") + OV_BUILD_POSTFIX); + std::string plugin_path = ov::util::make_plugin_library_name( + ov::test::utils::getExecutableDirectory(), + std::string("mock_engine_") + plugin->get_device_name() + OV_BUILD_POSTFIX); + if (!std::filesystem::is_regular_file(plugin_path)) { + std::filesystem::copy(mock_engine_path, plugin_path); + } + + auto so = ov::util::load_shared_object(plugin_path.c_str()); + + std::function injectProxyEngine = + make_std_function(so, "InjectPlugin"); + + injectProxyEngine(plugin.get()); + core.register_plugin(plugin_path, plugin->get_device_name()); + + return so; +} +} +} // namespace npuw +} // namespace tests diff --git a/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/register_in_ov.hpp b/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/register_in_ov.hpp new file mode 100644 index 00000000000000..e29443f387db57 --- /dev/null +++ b/src/plugins/intel_npu/tests/functional/behavior/npuw/mocks/register_in_ov.hpp @@ -0,0 +1,30 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_test_utils/file_utils.hpp" + +#include "openvino/runtime/core.hpp" +#include "openvino/runtime/iplugin.hpp" +#include "openvino/util/shared_object.hpp" + +namespace ov { +namespace npuw { +namespace tests { + +template +std::function make_std_function(const std::shared_ptr so, const std::string& functionName) { + std::function ptr(reinterpret_cast(ov::util::get_symbol(so, functionName.c_str()))); + return ptr; +} + +std::shared_ptr reg_plugin(ov::Core& core, std::shared_ptr& plugin); + +template +std::shared_ptr reg_plugin(ov::Core& core, const std::shared_ptr& plugin) { + auto base_plugin_ptr = std::dynamic_pointer_cast(plugin); + return reg_plugin(core, base_plugin_ptr); +} +} +} // namespace npuw +} // namespace tests