diff --git a/src/common/util/include/openvino/util/ov_version.hpp b/src/common/util/include/openvino/util/ov_version.hpp new file mode 100644 index 00000000000000..1911322b1fa577 --- /dev/null +++ b/src/common/util/include/openvino/util/ov_version.hpp @@ -0,0 +1,117 @@ +// Copyright (C) 2018-2025 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "openvino/core/except.hpp" + +namespace ov::util { +struct Version { + size_t major = 0; + size_t minor = 0; + size_t patch = 0; + size_t tweak = 0; + size_t build = 0; + + explicit Version(const char* version_str) { + // Pattern: MAJOR.MINOR.PATCH[.TWEAK]-BUILD-... + std::regex full_pattern(R"(^([0-9]+)\.([0-9]+)\.([0-9]+)(?:\.([0-9]+))?\-([0-9]+)\-.*)"); + std::regex build_only_pattern(R"(^[0-9]+$)"); + std::cmatch match; + + if (std::regex_match(version_str, match, full_pattern)) { + major = std::stoi(match[1].str()); + minor = std::stoi(match[2].str()); + patch = std::stoi(match[3].str()); + + // match[4] is the entire optional group (\.TWEAK), match[5] is TWEAK + // the 4th version number is not used in OpenVINO, but used in OpenVINO extensions + if (match[4].matched) { + tweak = std::stoi(match[4].str()); + } + + build = std::stoi(match[5].str()); + } else if (std::regex_match(version_str, build_only_pattern)) { + // Parse as just a build number + build = std::stoi(version_str); + } else { + OPENVINO_THROW("Failed to parse version: ", version_str); + } + } + + explicit Version(std::string_view version_str) : Version(version_str.data()) {} + + // Comparison operators + bool operator==(const Version& other) const { + return std::tie(major, minor, patch, tweak, build) == + std::tie(other.major, other.minor, other.patch, other.tweak, other.build); + } + + bool operator!=(const Version& other) const { + return !(*this == other); + } + + bool operator<(const Version& other) const { + return std::tie(major, minor, patch, tweak, build) < + std::tie(other.major, other.minor, other.patch, other.tweak, other.build); + } + + bool operator>(const Version& other) const { + return other < *this; + } + + bool operator<=(const Version& other) const { + return !(other < *this); + } + + bool operator>=(const Version& other) const { + return !(*this < other); + } +}; + +struct VersionCompatibilityPolicy { + size_t max_major_diff = std::numeric_limits::max(); + size_t max_minor_diff = std::numeric_limits::max(); + size_t max_patch_diff = std::numeric_limits::max(); + size_t max_tweak_diff = std::numeric_limits::max(); + size_t max_build_diff = std::numeric_limits::max(); +}; + +inline bool is_version_compatible(const Version& older_version, + const Version& newer_version, + const VersionCompatibilityPolicy& policy = {}) { + if (older_version > newer_version) { + return false; + } + + // Check each version component against policy + if (newer_version.major - older_version.major > policy.max_major_diff) { + return false; + } + + if (newer_version.minor - older_version.minor > policy.max_minor_diff) { + return false; + } + + if (newer_version.patch - older_version.patch > policy.max_patch_diff) { + return false; + } + + if (newer_version.tweak - older_version.tweak > policy.max_tweak_diff) { + return false; + } + + if (newer_version.build - older_version.build > policy.max_build_diff) { + return false; + } + + return true; +} +} // namespace ov::util diff --git a/src/core/tests/ov_version.cpp b/src/core/tests/ov_version.cpp new file mode 100644 index 00000000000000..4aa4870e4b7d40 --- /dev/null +++ b/src/core/tests/ov_version.cpp @@ -0,0 +1,355 @@ +// Copyright (C) 2018-2025 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/util/ov_version.hpp" + +#include + +#include "openvino/core/except.hpp" + +namespace ov::test { + +// Test fixture for Version tests +class VersionTest : public ::testing::Test {}; + +// Test fixture for version compatibility tests +class VersionCompatibilityTest : public ::testing::Test {}; + +// ============================================================================ +// Version Parsing Tests +// ============================================================================ + +TEST_F(VersionTest, ParseFullVersionString) { + ov::util::Version version("2024.5.0-12345-abcdef"); + EXPECT_EQ(version.major, 2024); + EXPECT_EQ(version.minor, 5); + EXPECT_EQ(version.patch, 0); + EXPECT_EQ(version.tweak, 0); + EXPECT_EQ(version.build, 12345); +} + +TEST_F(VersionTest, ParseVersionWithTweak) { + ov::util::Version version("2024.5.0.1-12345-abcdef"); + EXPECT_EQ(version.major, 2024); + EXPECT_EQ(version.minor, 5); + EXPECT_EQ(version.patch, 0); + EXPECT_EQ(version.tweak, 1); + EXPECT_EQ(version.build, 12345); +} + +TEST_F(VersionTest, ParseVersionWithLargeNumbers) { + ov::util::Version version("2025.10.99.999-99999-something"); + EXPECT_EQ(version.major, 2025); + EXPECT_EQ(version.minor, 10); + EXPECT_EQ(version.patch, 99); + EXPECT_EQ(version.tweak, 999); + EXPECT_EQ(version.build, 99999); +} + +TEST_F(VersionTest, ParseBuildNumberOnly) { + ov::util::Version version("12345"); + EXPECT_EQ(version.major, 0); + EXPECT_EQ(version.minor, 0); + EXPECT_EQ(version.patch, 0); + EXPECT_EQ(version.tweak, 0); + EXPECT_EQ(version.build, 12345); +} + +TEST_F(VersionTest, ParseVersionFromString) { + std::string version_str = "2024.1.2-5000-test"; + ov::util::Version version(version_str); + EXPECT_EQ(version.major, 2024); + EXPECT_EQ(version.minor, 1); + EXPECT_EQ(version.patch, 2); + EXPECT_EQ(version.tweak, 0); + EXPECT_EQ(version.build, 5000); +} + +TEST_F(VersionTest, ParseVersionFromConstCharPtr) { + const char* version_str = "2024.1.2-5000-test"; + ov::util::Version version(version_str); + EXPECT_EQ(version.major, 2024); + EXPECT_EQ(version.minor, 1); + EXPECT_EQ(version.patch, 2); + EXPECT_EQ(version.tweak, 0); + EXPECT_EQ(version.build, 5000); +} + +TEST_F(VersionTest, ParseVersionFromCharArray) { + char version_str[] = "2024.1.2-5000-test"; + ov::util::Version version(version_str); + EXPECT_EQ(version.major, 2024); + EXPECT_EQ(version.minor, 1); + EXPECT_EQ(version.patch, 2); + EXPECT_EQ(version.tweak, 0); + EXPECT_EQ(version.build, 5000); +} + +TEST_F(VersionTest, ParseVersionFromStringView) { + std::string_view version_str = "2024.1.2-5000-test"; + ov::util::Version version(version_str); + EXPECT_EQ(version.major, 2024); + EXPECT_EQ(version.minor, 1); + EXPECT_EQ(version.patch, 2); + EXPECT_EQ(version.tweak, 0); + EXPECT_EQ(version.build, 5000); +} + +TEST_F(VersionTest, ParseVersionFromTemporaryString) { + ov::util::Version version(std::string("2024.1.2-5000-test")); + EXPECT_EQ(version.major, 2024); + EXPECT_EQ(version.minor, 1); + EXPECT_EQ(version.patch, 2); + EXPECT_EQ(version.tweak, 0); + EXPECT_EQ(version.build, 5000); +} + +TEST_F(VersionTest, ParseVersionWithZeros) { + ov::util::Version version("0.0.0-0-test"); + EXPECT_EQ(version.major, 0); + EXPECT_EQ(version.minor, 0); + EXPECT_EQ(version.patch, 0); + EXPECT_EQ(version.tweak, 0); + EXPECT_EQ(version.build, 0); +} + +TEST_F(VersionTest, ParseVersionWithComplexSuffix) { + ov::util::Version version("2024.5.0-12345-rc1-something-else"); + EXPECT_EQ(version.major, 2024); + EXPECT_EQ(version.minor, 5); + EXPECT_EQ(version.patch, 0); + EXPECT_EQ(version.build, 12345); +} + +TEST_F(VersionTest, ThrowsOnInvalidFormat) { + EXPECT_THROW(ov::util::Version("invalid"), ov::Exception); + EXPECT_THROW(ov::util::Version("2024.5"), ov::Exception); + EXPECT_THROW(ov::util::Version("2024.5.0"), ov::Exception); + EXPECT_THROW(ov::util::Version("2024.5.0-"), ov::Exception); + EXPECT_THROW(ov::util::Version("a.b.c-1-test"), ov::Exception); + EXPECT_THROW(ov::util::Version("2024.5.0-abc-test"), ov::Exception); +} + +TEST_F(VersionTest, ThrowsOnEmptyString) { + EXPECT_THROW(ov::util::Version(""), ov::Exception); +} + +// ============================================================================ +// Version Comparison Tests +// ============================================================================ + +TEST_F(VersionTest, EqualityOperator) { + ov::util::Version v1("2024.5.0-12345-test"); + ov::util::Version v2("2024.5.0-12345-test"); + ov::util::Version v3("2024.5.0-12346-test"); + + EXPECT_TRUE(v1 == v2); + EXPECT_FALSE(v1 == v3); +} + +TEST_F(VersionTest, InequalityOperator) { + ov::util::Version v1("2024.5.0-12345-test"); + ov::util::Version v2("2024.5.0-12345-test"); + ov::util::Version v3("2024.5.0-12346-test"); + + EXPECT_FALSE(v1 != v2); + EXPECT_TRUE(v1 != v3); +} + +TEST_F(VersionTest, LessThanOperator) { + ov::util::Version v1("2024.5.0-12345-test"); + ov::util::Version v2("2024.5.0-12346-test"); + ov::util::Version v3("2024.5.1-12345-test"); + ov::util::Version v4("2024.6.0-12345-test"); + ov::util::Version v5("2025.5.0-12345-test"); + + EXPECT_TRUE(v1 < v2); + EXPECT_TRUE(v1 < v3); + EXPECT_TRUE(v1 < v4); + EXPECT_TRUE(v1 < v5); + EXPECT_FALSE(v2 < v1); +} + +TEST_F(VersionTest, GreaterThanOperator) { + ov::util::Version v1("2024.5.0-12345-test"); + ov::util::Version v2("2024.5.0-12346-test"); + + EXPECT_TRUE(v2 > v1); + EXPECT_FALSE(v1 > v2); + EXPECT_FALSE(v1 > v1); +} + +TEST_F(VersionTest, LessThanOrEqualOperator) { + ov::util::Version v1("2024.5.0-12345-test"); + ov::util::Version v2("2024.5.0-12345-test"); + ov::util::Version v3("2024.5.0-12346-test"); + + EXPECT_TRUE(v1 <= v2); + EXPECT_TRUE(v1 <= v3); + EXPECT_FALSE(v3 <= v1); +} + +TEST_F(VersionTest, GreaterThanOrEqualOperator) { + ov::util::Version v1("2024.5.0-12345-test"); + ov::util::Version v2("2024.5.0-12345-test"); + ov::util::Version v3("2024.5.0-12346-test"); + + EXPECT_TRUE(v1 >= v2); + EXPECT_TRUE(v3 >= v1); + EXPECT_FALSE(v1 >= v3); +} + +TEST_F(VersionTest, ComparisonWithTweak) { + ov::util::Version v1("2024.5.0.0-12345-test"); + ov::util::Version v2("2024.5.0.1-12345-test"); + + EXPECT_TRUE(v1 < v2); + EXPECT_FALSE(v1 > v2); +} + +TEST_F(VersionTest, ComparisonPriorityOrder) { + // Major takes precedence + ov::util::Version v1("2023.99.99.99-99999-test"); + ov::util::Version v2("2024.0.0.0-0-test"); + EXPECT_TRUE(v1 < v2); + + // Minor takes precedence over patch + ov::util::Version v3("2024.5.99.99-99999-test"); + ov::util::Version v4("2024.6.0.0-0-test"); + EXPECT_TRUE(v3 < v4); + + // Patch takes precedence over tweak + ov::util::Version v5("2024.5.0.99-99999-test"); + ov::util::Version v6("2024.5.1.0-0-test"); + EXPECT_TRUE(v5 < v6); + + // Tweak takes precedence over build + ov::util::Version v7("2024.5.0.0-99999-test"); + ov::util::Version v8("2024.5.0.1-0-test"); + EXPECT_TRUE(v7 < v8); +} + +// ============================================================================ +// Version Compatibility Tests +// ============================================================================ + +TEST_F(VersionCompatibilityTest, IdenticalVersionsAreCompatible) { + ov::util::Version v1("2024.5.0-12345-test"); + ov::util::Version v2("2024.5.0-12345-test"); + + EXPECT_TRUE(ov::util::is_version_compatible(v1, v2)); +} + +TEST_F(VersionCompatibilityTest, NewerVersionIsNotCompatible) { + ov::util::Version older("2024.5.0-12345-test"); + ov::util::Version newer("2024.6.0-12345-test"); + + EXPECT_FALSE(ov::util::is_version_compatible(newer, older)); +} + +TEST_F(VersionCompatibilityTest, DefaultPolicy) { + ov::util::Version older("2024.5.0-12345-test"); + ov::util::Version newer("2024.6.0-12345-test"); + + // Default policy has all max_diff = 0, so only identical versions are compatible + EXPECT_TRUE(ov::util::is_version_compatible(older, newer)); + EXPECT_FALSE(ov::util::is_version_compatible(newer, older)); + EXPECT_TRUE(ov::util::is_version_compatible(older, older)); +} + +TEST_F(VersionCompatibilityTest, AnyDifference) { + ov::util::Version older("2020.0.0-0-test"); + ov::util::Version newer("2024.5.10-12345-test"); + + ov::util::VersionCompatibilityPolicy policy{SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX}; + + EXPECT_TRUE(ov::util::is_version_compatible(older, newer, policy)); +} + +TEST_F(VersionCompatibilityTest, MajorVersionDifferenceRespected) { + ov::util::Version older("2022.5.0-12345-test"); + ov::util::Version newer("2024.5.0-12345-test"); + + EXPECT_FALSE(ov::util::is_version_compatible(older, newer, {1})); + EXPECT_TRUE(ov::util::is_version_compatible(older, newer, {2})); +} + +TEST_F(VersionCompatibilityTest, MinorVersionDifferenceRespected) { + ov::util::Version older("2024.3.0-12345-test"); + ov::util::Version newer("2024.5.0-12345-test"); + + EXPECT_FALSE(ov::util::is_version_compatible(older, newer, {0, 1})); + EXPECT_TRUE(ov::util::is_version_compatible(older, newer, {0, 2})); +} + +TEST_F(VersionCompatibilityTest, PatchVersionDifferenceRespected) { + ov::util::Version older("2024.5.0-12345-test"); + ov::util::Version newer("2024.5.3-12345-test"); + + EXPECT_FALSE(ov::util::is_version_compatible(older, newer, {0, 0, 2})); + EXPECT_TRUE(ov::util::is_version_compatible(older, newer, {0, 0, 3})); +} + +TEST_F(VersionCompatibilityTest, TweakVersionDifferenceRespected) { + ov::util::Version older("2024.5.0.0-12345-test"); + ov::util::Version newer("2024.5.0.3-12345-test"); + + EXPECT_FALSE(ov::util::is_version_compatible(older, newer, {0, 0, 0, 2})); + EXPECT_TRUE(ov::util::is_version_compatible(older, newer, {0, 0, 0, 3})); +} + +TEST_F(VersionCompatibilityTest, BuildDifferenceRespected) { + ov::util::Version older("2024.5.0-12340-test"); + ov::util::Version newer("2024.5.0-12345-test"); + + EXPECT_FALSE(ov::util::is_version_compatible(older, newer, {0, 0, 0, 0, 4})); + EXPECT_TRUE(ov::util::is_version_compatible(older, newer, {0, 0, 0, 0, 5})); +} + +TEST_F(VersionCompatibilityTest, NotSpecifiedDifferencesAreUnlimited) { + ov::util::Version v1("2024.5.0-5-test"); + ov::util::Version v2("2024.6.5-0-test"); + + // Only major and minor differences are specified. Difference in patch, tweak, build don't matter. + EXPECT_TRUE(ov::util::is_version_compatible(v1, v2, {0, 1})); + EXPECT_FALSE(ov::util::is_version_compatible(v2, v1, {0, 1})); +} + +TEST_F(VersionCompatibilityTest, StrictPolicyNoToleranceExceptExact) { + ov::util::Version older("2024.5.0-12345-test"); + ov::util::Version newer("2024.5.0-12345-test"); + + ov::util::VersionCompatibilityPolicy strict_policy{0, 0, 0, 0, 0}; + + EXPECT_TRUE(ov::util::is_version_compatible(older, newer, strict_policy)); + + ov::util::Version older2("2024.5.0-12344-test"); + EXPECT_FALSE(ov::util::is_version_compatible(older2, newer, strict_policy)); +} + +TEST_F(VersionCompatibilityTest, ComplexPolicyMultipleConstraints) { + ov::util::Version older("2024.3.0-12340-test"); + ov::util::Version newer("2024.5.2-12345-test"); + + ov::util::VersionCompatibilityPolicy policy{0, 2, 2, SIZE_MAX, 5}; + + EXPECT_TRUE(ov::util::is_version_compatible(older, newer, policy)); + + // Exceeds minor difference + ov::util::Version older2("2024.2.0-12340-test"); + EXPECT_FALSE(ov::util::is_version_compatible(older2, newer, policy)); + + // Different major version + ov::util::Version older3("2023.5.0-12340-test"); + EXPECT_FALSE(ov::util::is_version_compatible(older3, newer, policy)); +} + +TEST_F(VersionCompatibilityTest, BuildOnlyVersions) { + ov::util::Version older("12340"); + ov::util::Version newer("12345"); + + EXPECT_TRUE(ov::util::is_version_compatible(older, newer, {0, 0, 0, 0, 5})); + EXPECT_FALSE(ov::util::is_version_compatible(older, newer, {0, 0, 0, 0, 4})); +} +} // namespace ov::test diff --git a/src/inference/src/dev/core_impl.cpp b/src/inference/src/dev/core_impl.cpp index 28e94cf73ac825..ef5fe823e9c7a4 100644 --- a/src/inference/src/dev/core_impl.cpp +++ b/src/inference/src/dev/core_impl.cpp @@ -31,6 +31,7 @@ #include "openvino/util/common_util.hpp" #include "openvino/util/file_util.hpp" #include "openvino/util/log.hpp" +#include "openvino/util/ov_version.hpp" #include "openvino/util/shared_object.hpp" #include "openvino/util/variant_visitor.hpp" #include "openvino/util/xml_parse_utils.hpp" @@ -1554,7 +1555,9 @@ ov::SoPtr ov::CoreImpl::load_model_from_cache( "Original model runtime properties have been changed, not supported anymore!"); } } else { - if (header.get_openvino_version() != ov::get_openvino_version().buildNumber) { + if (!ov::util::is_version_compatible(ov::util::Version(header.get_openvino_version()), + ov::util::Version(ov::get_openvino_version().buildNumber), + ov::util::VersionCompatibilityPolicy{0, 5})) { // Build number mismatch, don't use this cache OPENVINO_THROW("Version does not match"); }