diff --git a/src/plugins/intel_npu/src/common/include/intel_npu/common/blob_container.hpp b/src/plugins/intel_npu/src/common/include/intel_npu/common/blob_container.hpp index 13f833ddfccc88..ed5bb92f473002 100644 --- a/src/plugins/intel_npu/src/common/include/intel_npu/common/blob_container.hpp +++ b/src/plugins/intel_npu/src/common/include/intel_npu/common/blob_container.hpp @@ -47,19 +47,17 @@ class BlobContainerVector : public BlobContainer { class BlobContainerAlignedBuffer : public BlobContainer { public: BlobContainerAlignedBuffer(const std::shared_ptr& blobSO, - size_t ovHeaderOffset, - size_t metadataSize) - : _ownershipBlob(blobSO), + size_t ovHeaderOffset, uint64_t blobSize) + : _blobSize(blobSize), _ovHeaderOffset(ovHeaderOffset), - _metadataSize(metadataSize) {} + _ownershipBlob(blobSO) {} void* get_ptr() override { return _ownershipBlob->get_ptr(_ovHeaderOffset); } size_t size() const override { - // remove OV header offset and metadata from blob size - return _ownershipBlob->size() - _ovHeaderOffset - _metadataSize; + return _blobSize; } bool release_from_memory() override { @@ -67,9 +65,9 @@ class BlobContainerAlignedBuffer : public BlobContainer { } private: - std::shared_ptr _ownershipBlob; + uint64_t _blobSize; size_t _ovHeaderOffset; - size_t _metadataSize; + std::shared_ptr _ownershipBlob; }; } // namespace intel_npu diff --git a/src/plugins/intel_npu/src/common/include/intel_npu/common/igraph.hpp b/src/plugins/intel_npu/src/common/include/intel_npu/common/igraph.hpp index 37447859e42b27..86d6902a79bff8 100644 --- a/src/plugins/intel_npu/src/common/include/intel_npu/common/igraph.hpp +++ b/src/plugins/intel_npu/src/common/include/intel_npu/common/igraph.hpp @@ -24,7 +24,7 @@ class IGraph : public std::enable_shared_from_this { const Config& config, std::unique_ptr blobPtr); - virtual void export_blob(std::ostream& stream) const = 0; + virtual size_t export_blob(std::ostream& stream) const = 0; virtual std::vector process_profiling_output(const std::vector& profData, const Config& config) const = 0; diff --git a/src/plugins/intel_npu/src/compiler_adapter/include/driver_graph.hpp b/src/plugins/intel_npu/src/compiler_adapter/include/driver_graph.hpp index 25af83a82f2490..2c845227e8aac7 100644 --- a/src/plugins/intel_npu/src/compiler_adapter/include/driver_graph.hpp +++ b/src/plugins/intel_npu/src/compiler_adapter/include/driver_graph.hpp @@ -23,7 +23,7 @@ class DriverGraph final : public IGraph { const Config& config, std::unique_ptr blob); - void export_blob(std::ostream& stream) const override; + size_t export_blob(std::ostream& stream) const override; std::vector process_profiling_output(const std::vector& profData, const Config& config) const override; diff --git a/src/plugins/intel_npu/src/compiler_adapter/include/plugin_graph.hpp b/src/plugins/intel_npu/src/compiler_adapter/include/plugin_graph.hpp index 5d0ab241bcd9c8..0d006f820c199d 100644 --- a/src/plugins/intel_npu/src/compiler_adapter/include/plugin_graph.hpp +++ b/src/plugins/intel_npu/src/compiler_adapter/include/plugin_graph.hpp @@ -26,7 +26,7 @@ class PluginGraph final : public IGraph { std::unique_ptr blobPtr, const Config& config); - void export_blob(std::ostream& stream) const override; + size_t export_blob(std::ostream& stream) const override; std::vector process_profiling_output(const std::vector& profData, const Config& config) const override; diff --git a/src/plugins/intel_npu/src/compiler_adapter/src/driver_graph.cpp b/src/plugins/intel_npu/src/compiler_adapter/src/driver_graph.cpp index 5ce5cc9f1b39ff..20c38a73a67fdf 100644 --- a/src/plugins/intel_npu/src/compiler_adapter/src/driver_graph.cpp +++ b/src/plugins/intel_npu/src/compiler_adapter/src/driver_graph.cpp @@ -32,9 +32,9 @@ DriverGraph::DriverGraph(const std::shared_ptr& zeGraphExt, initialize(config); } -void DriverGraph::export_blob(std::ostream& stream) const { +size_t DriverGraph::export_blob(std::ostream& stream) const { const uint8_t* blobPtr = nullptr; - size_t blobSize = -1; + size_t blobSize; std::vector blob; if (_blobIsReleased) { @@ -47,7 +47,7 @@ void DriverGraph::export_blob(std::ostream& stream) const { if (!stream) { _logger.error("Write blob to stream failed. Blob is broken!"); - return; + return 0; } if (_logger.level() >= ov::log::Level::INFO) { @@ -61,6 +61,7 @@ void DriverGraph::export_blob(std::ostream& stream) const { _logger.info(str.str().c_str()); } _logger.info("Write blob to stream successfully."); + return blobSize; } std::vector DriverGraph::process_profiling_output(const std::vector& profData, diff --git a/src/plugins/intel_npu/src/compiler_adapter/src/plugin_graph.cpp b/src/plugins/intel_npu/src/compiler_adapter/src/plugin_graph.cpp index 4d48dbe89efaa3..90e531d5b940c7 100644 --- a/src/plugins/intel_npu/src/compiler_adapter/src/plugin_graph.cpp +++ b/src/plugins/intel_npu/src/compiler_adapter/src/plugin_graph.cpp @@ -30,12 +30,12 @@ PluginGraph::PluginGraph(const std::shared_ptr& zeGraphExt, initialize(config); } -void PluginGraph::export_blob(std::ostream& stream) const { +size_t PluginGraph::export_blob(std::ostream& stream) const { stream.write(reinterpret_cast(_blobPtr->get_ptr()), _blobPtr->size()); if (!stream) { _logger.error("Write blob to stream failed. Blob is broken!"); - return; + return 0; } if (_logger.level() >= ov::log::Level::INFO) { @@ -51,6 +51,7 @@ void PluginGraph::export_blob(std::ostream& stream) const { _logger.info(str.str().c_str()); } _logger.info("Write blob to stream successfully."); + return _blobPtr->size(); } std::vector PluginGraph::process_profiling_output(const std::vector& profData, diff --git a/src/plugins/intel_npu/src/plugin/include/metadata.hpp b/src/plugins/intel_npu/src/plugin/include/metadata.hpp new file mode 100644 index 00000000000000..78af3f9e5c5bef --- /dev/null +++ b/src/plugins/intel_npu/src/plugin/include/metadata.hpp @@ -0,0 +1,178 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace intel_npu { + +/** + * @brief Magic bytes used for identifying NPU blobs. + */ +constexpr std::string_view MAGIC_BYTES = "OVNPU"; + +/** + * @brief Returns a uint32_t value which represents two uint16_t values concatenated. + * @details Convention for bumping the metadata version: + * - Increment Major in case of: removing a current field OR adding a new field in between fields. + * - Increment Minor in case of: adding a new field at the end. + * + * @return Major and minor versions concatenated into a single uint32_t value. + */ +constexpr uint32_t make_version(uint16_t major, uint16_t minor) { + return major << 16 | (minor & 0x0000ffff); +} + +/** + * @brief Gets the major version. + * + * @return Major version. + */ +constexpr uint16_t get_major(uint32_t version) { + return static_cast(version >> 16); +} + +/** + * @brief Gets the minor version. + * + * @return Minor version. + */ +constexpr uint16_t get_minor(uint32_t version) { + return static_cast(version); +} + +/** + * @brief List of supported version formats. + */ +constexpr uint32_t METADATA_VERSION_1_0{make_version(1, 0)}; + +/** + * @brief Current metadata version. + */ +constexpr uint32_t CURRENT_METADATA_VERSION{METADATA_VERSION_1_0}; + +struct OpenvinoVersion { +private: + std::string _version; + uint32_t _size; + +public: + OpenvinoVersion(std::string_view version); + + /** + * @brief Reads version data from a stream. + */ + void read(std::istream& stream); + + /** + * @brief Gets the version string. + */ + std::string get_version(); + + /** + * @brief Gets the size of version string as reference. + * @return Reference to the size. + * + * @note Needed as reference for reading its binary representation in memory. + * @see Metadata::write() + */ + uint32_t& get_size(); +}; + +struct MetadataBase { + /** + * @brief Reads metadata from a stream. + */ + virtual void read(std::istream& stream) = 0; + + /** + * @brief Writes metadata to a stream. + */ + virtual void write(std::ostream& stream) = 0; + + virtual bool is_compatible() = 0; + + virtual uint64_t get_blob_size() const = 0; + + virtual size_t get_ov_header_offset() const = 0; + + virtual ~MetadataBase() = default; +}; + +/** + * @brief Template for metadata class handling. + * + * @attention It's a must to have metadata version as first field in any metadata specialization. + */ +template +struct Metadata : public MetadataBase {}; + +/** + * @brief Template specialization for metadata version 1.0. + */ +template <> +struct Metadata : public MetadataBase { +private: + uint64_t _blobDataSize; + uint32_t _version; + size_t _ovHeaderOffset; + OpenvinoVersion _ovVersion; + +public: + Metadata(); + + Metadata(size_t ovHeaderOffset, uint64_t blobDataSize); + + void read(std::istream& stream) override; + + void write(std::ostream& stream) override; + + /** + * @brief Checks if metadata is supported. + * + * @return Returns: + * - false: + * - if blob metadata does not match current metadata. + * - if blob OpenVINO version does not match current one. + * + * - true: if all versions match. + * + * @note The version check can be disabled if the "NPU_DISABLE_VERSION_CHECK" environment variable is set to '1'. + */ + bool is_compatible() override; + + void set_version(uint32_t newVersion); + + void set_ov_version(const OpenvinoVersion& newVersion); + + uint64_t get_blob_size() const override; + + size_t get_ov_header_offset() const override; +}; + +/** + * @brief Creates a Metadata object. + * + * @return Unique pointer to the created MetadataBase object if the major version is supported; otherwise, returns + * 'nullptr'. + */ +std::unique_ptr create_metadata(uint32_t version, size_t ovHeaderOffset, uint64_t blobDataSize); + +/** + * @brief Reads metadata from a blob. + * + * @return If the blob is versioned and its major version is supported, returns an unique pointer to the read + * MetadataBase object; otherwise, returns 'nullptr'. + */ +std::unique_ptr read_metadata_from(std::istream& stream); + +std::unique_ptr read_metadata_from(std::istream& stream, const std::shared_ptr& modelBuffer); +} // namespace intel_npu diff --git a/src/plugins/intel_npu/src/plugin/src/compiled_model.cpp b/src/plugins/intel_npu/src/plugin/src/compiled_model.cpp index 506502c819d72e..7d46e14db8a980 100644 --- a/src/plugins/intel_npu/src/plugin/src/compiled_model.cpp +++ b/src/plugins/intel_npu/src/plugin/src/compiled_model.cpp @@ -13,6 +13,7 @@ #include "intel_npu/config/compiler.hpp" #include "intel_npu/config/config.hpp" #include "intel_npu/config/runtime.hpp" +#include "metadata.hpp" #include "openvino/pass/constant_folding.hpp" #include "openvino/pass/manager.hpp" #include "openvino/runtime/properties.hpp" @@ -72,7 +73,12 @@ std::shared_ptr CompiledModel::create_sync_infer_request( void CompiledModel::export_model(std::ostream& stream) const { _logger.debug("CompiledModel::export_model"); - _graph->export_blob(stream); + size_t blobSizeBeforeVersioning = _graph->export_blob(stream); + + auto meta = Metadata(); + meta.write(stream); + stream.write(reinterpret_cast(&blobSizeBeforeVersioning), sizeof(blobSizeBeforeVersioning)); + stream.write(MAGIC_BYTES.data(), MAGIC_BYTES.size()); } std::shared_ptr CompiledModel::get_runtime_model() const { diff --git a/src/plugins/intel_npu/src/plugin/src/metadata.cpp b/src/plugins/intel_npu/src/plugin/src/metadata.cpp new file mode 100644 index 00000000000000..a4dc76fb84fc18 --- /dev/null +++ b/src/plugins/intel_npu/src/plugin/src/metadata.cpp @@ -0,0 +1,202 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "metadata.hpp" + +#include +#include + +#include "intel_npu/config/config.hpp" +#include "intel_npu/utils/logger/logger.hpp" +#include "openvino/core/version.hpp" +#include "openvino/runtime/shared_buffer.hpp" +namespace { + +size_t getFileSize(std::istream& stream) { + auto log = intel_npu::Logger::global().clone("getFileSize"); + if (!stream) { + OPENVINO_THROW("Stream is in bad status! Please check the passed stream status!"); + } + + const size_t streamStart = stream.tellg(); + stream.seekg(0, std::ios_base::end); + const size_t streamEnd = stream.tellg(); + stream.seekg(streamStart, std::ios_base::beg); + + log.debug("Read blob size: streamStart=%zu, streamEnd=%zu", streamStart, streamEnd); + + if (streamEnd < streamStart) { + OPENVINO_THROW("Invalid stream size: streamEnd (", + streamEnd, + ") is not larger than streamStart (", + streamStart, + ")!"); + } + + return streamEnd - streamStart; +} + +} // namespace + +namespace intel_npu { + +OpenvinoVersion::OpenvinoVersion(std::string_view version) + : _version(version), + _size(static_cast(version.size())) {} + +void OpenvinoVersion::read(std::istream& stream) { + stream.read(reinterpret_cast(&_size), sizeof(_size)); + _version.resize(_size); + stream.read(_version.data(), _size); +} + +Metadata::Metadata() + : _blobDataSize{0}, + _version{METADATA_VERSION_1_0}, + _ovHeaderOffset{0}, + _ovVersion{ov::get_openvino_version().buildNumber} {} + +Metadata::Metadata(size_t ovHeaderOffset, uint64_t blobDataSize) + : _blobDataSize{blobDataSize}, + _version{METADATA_VERSION_1_0}, + _ovHeaderOffset{ovHeaderOffset}, + _ovVersion{ov::get_openvino_version().buildNumber} {} + +void Metadata::read(std::istream& stream) { + _ovVersion.read(stream); +} + +uint32_t& OpenvinoVersion::get_size() { + return _size; +} + +void Metadata::write(std::ostream& stream) { + stream.write(reinterpret_cast(&_version), sizeof(_version)); + stream.write(reinterpret_cast(&_ovVersion.get_size()), sizeof(_ovVersion.get_size())); + stream.write(_ovVersion.get_version().data(), _ovVersion.get_version().size()); +} + +std::unique_ptr create_metadata(uint32_t version, size_t ovHeaderOffset, uint64_t blobDataSize) { + switch (version) { + case METADATA_VERSION_1_0: + return std::make_unique>(ovHeaderOffset, blobDataSize); + + default: + return nullptr; + } +} + +std::string OpenvinoVersion::get_version() { + return _version; +} + +bool Metadata::is_compatible() { + Logger logger("NPUPlugin", Logger::global().level()); + // checking if we can import the blob + if (_ovVersion.get_version() != ov::get_openvino_version().buildNumber) { + logger.warning("Imported blob OpenVINO version: %s, but the current OpenVINO version is: %s", + _ovVersion.get_version().c_str(), + ov::get_openvino_version().buildNumber); + +#ifdef NPU_PLUGIN_DEVELOPER_BUILD + if (auto envVar = std::getenv("NPU_DISABLE_VERSION_CHECK")) { + if (envVarStrToBool("NPU_DISABLE_VERSION_CHECK", envVar)) { + return true; + } + } +#endif + return false; + } + return true; +} + +std::unique_ptr read_metadata_from(std::istream& stream) { + Logger logger("NPUPlugin", Logger::global().level()); + size_t magicBytesSize = MAGIC_BYTES.size(); + std::string blobMagicBytes; + blobMagicBytes.resize(magicBytesSize); + + size_t currentStreamPos = stream.tellg(); + size_t streamSize = getFileSize(stream); + stream.seekg(streamSize - magicBytesSize, std::ios::beg); + stream.read(blobMagicBytes.data(), magicBytesSize); + if (MAGIC_BYTES != blobMagicBytes) { + logger.error("Blob is missing NPU metadata!"); + return nullptr; + } + + uint64_t blobDataSize; + stream.seekg(streamSize - magicBytesSize - sizeof(blobDataSize), std::ios::beg); + stream.read(reinterpret_cast(&blobDataSize), sizeof(blobDataSize)); + stream.seekg(currentStreamPos + blobDataSize, std::ios::beg); + + uint32_t metaVersion; + stream.read(reinterpret_cast(&metaVersion), sizeof(metaVersion)); + + auto storedMeta = create_metadata(metaVersion, currentStreamPos, blobDataSize); + if (storedMeta != nullptr) { + storedMeta->read(stream); + } else { + logger.warning("Imported blob metadata version: %d.%d, but the current version is: %d.%d", + get_major(metaVersion), + get_minor(metaVersion), + get_major(CURRENT_METADATA_VERSION), + get_minor(CURRENT_METADATA_VERSION)); + } + stream.seekg(currentStreamPos, std::ios::beg); + return storedMeta; +} + +std::unique_ptr read_metadata_from(std::istream& stream, const std::shared_ptr& modelBuffer) { + Logger logger("NPUPlugin", Logger::global().level()); + size_t magicBytesSize = MAGIC_BYTES.size(); + std::string blobMagicBytes; + blobMagicBytes.resize(magicBytesSize); + + size_t currentStreamPos = stream.tellg(); + size_t streamSize = modelBuffer->size(); + + blobMagicBytes.assign(reinterpret_cast(modelBuffer->get_ptr(streamSize - magicBytesSize)), magicBytesSize); + if (MAGIC_BYTES != blobMagicBytes) { + logger.error("Blob is missing NPU metadata!"); + return nullptr; + } + + uint64_t blobDataSize; + blobDataSize = *reinterpret_cast(modelBuffer->get_ptr(streamSize - magicBytesSize - sizeof(blobDataSize))); + + uint32_t metaVersion; + metaVersion = *reinterpret_cast(modelBuffer->get_ptr(currentStreamPos + blobDataSize)); + + auto storedMeta = create_metadata(metaVersion, currentStreamPos, blobDataSize); + stream.seekg(blobDataSize + sizeof(metaVersion), std::ios::cur); + if (storedMeta != nullptr) { + storedMeta->read(stream); + } else { + logger.warning("Imported blob metadata version: %d.%d, but the current version is: %d.%d", + get_major(metaVersion), + get_minor(metaVersion), + get_major(CURRENT_METADATA_VERSION), + get_minor(CURRENT_METADATA_VERSION)); + } + return storedMeta; +} + +void Metadata::set_version(uint32_t newVersion) { + _version = newVersion; +} + +void Metadata::set_ov_version(const OpenvinoVersion& newVersion) { + _ovVersion = newVersion; +} + +uint64_t Metadata::get_blob_size() const { + return _blobDataSize; +} + +size_t Metadata::get_ov_header_offset() const { + return _ovHeaderOffset; +} + +} // namespace intel_npu diff --git a/src/plugins/intel_npu/src/plugin/src/plugin.cpp b/src/plugins/intel_npu/src/plugin/src/plugin.cpp index f1e991aef1030f..68a1c4579629a1 100644 --- a/src/plugins/intel_npu/src/plugin/src/plugin.cpp +++ b/src/plugins/intel_npu/src/plugin/src/plugin.cpp @@ -17,6 +17,7 @@ #include "intel_npu/config/npuw.hpp" #include "intel_npu/config/runtime.hpp" #include "intel_npu/utils/zero/zero_init.hpp" +#include "metadata.hpp" #include "npuw/compiled_model.hpp" #include "openvino/op/constant.hpp" #include "openvino/op/parameter.hpp" @@ -131,30 +132,6 @@ std::map any_copy(const ov::AnyMap& params) { return result; } -size_t getFileSize(std::istream& stream) { - auto log = Logger::global().clone("getFileSize"); - if (!stream) { - OPENVINO_THROW("Stream is in bad status! Please check the passed stream status!"); - } - - const size_t streamStart = stream.tellg(); - stream.seekg(0, std::ios_base::end); - const size_t streamEnd = stream.tellg(); - stream.seekg(streamStart, std::ios_base::beg); - - log.debug("Read blob size: streamStart=%zu, streamEnd=%zu", streamStart, streamEnd); - - if (streamEnd < streamStart) { - OPENVINO_THROW("Invalid stream size: streamEnd (", - streamEnd, - ") is not larger than streamStart (", - streamStart, - ")!"); - } - - return streamEnd - streamStart; -} - void update_log_level(const std::map& propertiesMap) { auto it = propertiesMap.find(std::string(LOG_LEVEL::key())); if (it != propertiesMap.end()) { @@ -783,10 +760,22 @@ std::shared_ptr Plugin::import_model(std::istream& stream, c auto compiler = compilerAdapterFactory.getCompiler(_backends->getIEngineBackend(), localConfig); std::unique_ptr blobPtr; + std::unique_ptr storedMeta; - if (modelBuffer == nullptr) { - auto graphSize = getFileSize(stream); + if (dynamic_cast(stream.rdbuf())) { + storedMeta = read_metadata_from(stream, dynamic_cast(stream.rdbuf())->get_buffer()); + } else { + storedMeta = read_metadata_from(stream); + } + + if (storedMeta == nullptr) { + OPENVINO_THROW("Could not read metadata!"); + } else if (!storedMeta->is_compatible()) { + OPENVINO_THROW("Incompatible blob version!"); + } + auto graphSize = storedMeta->get_blob_size(); + if (modelBuffer == nullptr) { std::vector blob(graphSize); stream.read(reinterpret_cast(blob.data()), graphSize); if (!stream) { @@ -796,7 +785,7 @@ std::shared_ptr Plugin::import_model(std::istream& stream, c blobPtr = std::make_unique(std::move(blob)); } else { - blobPtr = std::make_unique(modelBuffer, stream.tellg(), 0); + blobPtr = std::make_unique(modelBuffer, storedMeta->get_ov_header_offset(), graphSize); } auto graph = compiler->parse(std::move(blobPtr), localConfig); diff --git a/src/plugins/intel_npu/tests/unit/CMakeLists.txt b/src/plugins/intel_npu/tests/unit/CMakeLists.txt index f4e8a64ecea92b..5c9044eb543906 100644 --- a/src/plugins/intel_npu/tests/unit/CMakeLists.txt +++ b/src/plugins/intel_npu/tests/unit/CMakeLists.txt @@ -29,6 +29,8 @@ ov_add_test_target( ${OpenVINO_SOURCE_DIR}/src/plugins/intel_npu/src/utils/include ${OpenVINO_SOURCE_DIR}/src/plugins/intel_npu/src/plugin/include ${OpenVINO_SOURCE_DIR}/src/plugins/intel_npu/src/al/include + OBJECT_FILES + ${OpenVINO_SOURCE_DIR}/src/plugins/intel_npu/src/plugin/src/metadata.cpp LINK_LIBRARIES ${MANDATORY_UNIT_TESTS_LIBS} LABELS diff --git a/src/plugins/intel_npu/tests/unit/npu/metadata_version.cpp b/src/plugins/intel_npu/tests/unit/npu/metadata_version.cpp new file mode 100644 index 00000000000000..f3b637e643101b --- /dev/null +++ b/src/plugins/intel_npu/tests/unit/npu/metadata_version.cpp @@ -0,0 +1,66 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "common_test_utils/test_assertions.hpp" +#include "metadata.hpp" + +using namespace intel_npu; + +using MetadataUnitTests = ::testing::Test; + +TEST_F(MetadataUnitTests, readUnversionedBlob) { + std::stringstream stream(" ELF"); + + auto storedMeta = read_metadata_from(stream); + ASSERT_EQ(storedMeta, nullptr); +} + +TEST_F(MetadataUnitTests, writeAndReadMetadataFromBlob) { + std::stringstream stream; + size_t blobSize = 0; + auto meta = Metadata(stream.tellg(), blobSize); + + OV_ASSERT_NO_THROW(meta.write(stream)); + OV_ASSERT_NO_THROW(stream.write(reinterpret_cast(&blobSize), sizeof(blobSize))); + OV_ASSERT_NO_THROW(stream.write(MAGIC_BYTES.data(), MAGIC_BYTES.size())); + + auto storedMeta = read_metadata_from(stream); + ASSERT_NE(storedMeta, nullptr); + ASSERT_TRUE(storedMeta->is_compatible()); +} + +TEST_F(MetadataUnitTests, writeAndReadInvalidOpenvinoVersion) { + size_t blobSize = 0; + std::stringstream stream; + auto meta = Metadata(stream.tellg(), blobSize); + + OpenvinoVersion badOvVersion("just_some_wrong_ov_version"); + meta.set_ov_version(badOvVersion); + + OV_ASSERT_NO_THROW(meta.write(stream)); + OV_ASSERT_NO_THROW(stream.write(reinterpret_cast(&blobSize), sizeof(blobSize))); + OV_ASSERT_NO_THROW(stream.write(MAGIC_BYTES.data(), MAGIC_BYTES.size())); + + auto storedMeta = read_metadata_from(stream); + ASSERT_NE(storedMeta, nullptr); + ASSERT_FALSE(storedMeta->is_compatible()); +} + +TEST_F(MetadataUnitTests, writeAndReadInvalidMetadataVersion) { + size_t blobSize = 0; + std::stringstream stream; + auto meta = Metadata(stream.tellg(), blobSize); + + constexpr uint32_t dummy_version = make_version(0x00007E57, 0x0000AC3D); + meta.set_version(dummy_version); + + OV_ASSERT_NO_THROW(meta.write(stream)); + OV_ASSERT_NO_THROW(stream.write(reinterpret_cast(&blobSize), sizeof(blobSize))); + OV_ASSERT_NO_THROW(stream.write(MAGIC_BYTES.data(), MAGIC_BYTES.size())); + + auto storedMeta = read_metadata_from(stream); + ASSERT_EQ(storedMeta, nullptr); +}