| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <limits> |
| #include <string> |
| |
| #include <zxtest/zxtest.h> |
| |
| #include "fidl/versioning_types.h" |
| |
| namespace { |
| |
| using fidl::Availability; |
| using fidl::Platform; |
| using fidl::Version; |
| using fidl::VersionRange; |
| |
| VersionRange range(uint64_t x, uint64_t y) { |
| return VersionRange(Version::From(x).value(), Version::From(y).value()); |
| } |
| |
| TEST(VersioningTypesTests, GoodPlatformParse) { |
| EXPECT_EQ(fidl::Platform::Parse("foo123").value().name(), "foo123"); |
| } |
| |
| TEST(VersioningTypesTests, BadPlatformParseEmpty) { |
| EXPECT_FALSE(fidl::Platform::Parse("").has_value()); |
| } |
| |
| TEST(VersioningTypesTests, BadPlatformParseInvalidChar) { |
| EXPECT_FALSE(fidl::Platform::Parse("foo_bar").has_value()); |
| } |
| |
| TEST(VersioningTypesTests, GoodVersionFromMinNumeric) { |
| auto maybe_version = fidl::Version::From(1); |
| ASSERT_TRUE(maybe_version.has_value()); |
| EXPECT_EQ(maybe_version.value().ordinal(), 1); |
| EXPECT_EQ(maybe_version.value().ToString(), "1"); |
| } |
| |
| TEST(VersioningTypesTests, GoodVersionFromMaxNumeric) { |
| uint64_t ordinal = (1ull << 63) - 1; |
| auto maybe_version = Version::From(ordinal); |
| ASSERT_TRUE(maybe_version.has_value()); |
| EXPECT_EQ(maybe_version.value().ordinal(), ordinal); |
| EXPECT_EQ(maybe_version.value().ToString(), std::to_string(ordinal)); |
| // Confirm this is in fact the last valid ordinal. |
| EXPECT_EQ(fidl::Version::From(ordinal + 1), std::nullopt); |
| } |
| |
| TEST(VersioningTypesTests, GoodVersionFromHead) { |
| uint64_t ordinal = std::numeric_limits<uint64_t>::max(); |
| auto maybe_version = Version::From(ordinal); |
| ASSERT_TRUE(maybe_version.has_value()); |
| EXPECT_EQ(maybe_version.value().ordinal(), ordinal); |
| EXPECT_EQ(maybe_version.value().ToString(), "HEAD"); |
| } |
| |
| TEST(VersioningTypesTests, BadVersionFrom) { |
| ASSERT_EQ(Version::From(0), std::nullopt); |
| ASSERT_EQ(Version::From(1ull << 63), std::nullopt); |
| ASSERT_EQ(Version::From(std::numeric_limits<uint64_t>::max() - 1), std::nullopt); |
| } |
| |
| TEST(VersioningTypesTests, GoodVersionParse) { |
| uint64_t max_numeric_ordinal = (1ull << 63) - 1; |
| uint64_t head_ordinal = std::numeric_limits<uint64_t>::max(); |
| |
| EXPECT_EQ(Version::Parse("1"), Version::From(1)); |
| EXPECT_EQ(Version::Parse(std::to_string(max_numeric_ordinal)), |
| Version::From(max_numeric_ordinal)); |
| EXPECT_EQ(Version::Parse(std::to_string(head_ordinal)), Version::From(head_ordinal)); |
| EXPECT_EQ(Version::Parse("HEAD"), Version::From(head_ordinal)); |
| } |
| |
| TEST(VersioningTypesTests, BadVersionParse) { |
| EXPECT_EQ(Version::Parse(""), std::nullopt); |
| EXPECT_EQ(Version::Parse("0"), std::nullopt); |
| EXPECT_EQ(Version::Parse("18446744073709551616"), std::nullopt); |
| EXPECT_EQ(Version::Parse("-1"), std::nullopt); |
| } |
| |
| TEST(VersioningTypesTests, GoodVersionRangeComparisons) { |
| EXPECT_EQ(range(1, 2), range(1, 2)); |
| EXPECT_EQ(range(2, 3), range(2, 3)); |
| |
| EXPECT_NE(range(1, 2), range(1, 3)); |
| EXPECT_NE(range(1, 3), range(2, 3)); |
| EXPECT_NE(range(2, 3), range(1, 2)); |
| |
| EXPECT_LT(range(1, 2), range(1, 3)); |
| EXPECT_LT(range(1, 3), range(2, 3)); |
| EXPECT_LT(range(1, 2), range(2, 3)); |
| |
| EXPECT_GT(range(1, 3), range(1, 2)); |
| EXPECT_GT(range(2, 3), range(1, 3)); |
| EXPECT_GT(range(2, 3), range(1, 2)); |
| } |
| |
| TEST(VersioningTypesTests, GoodVersionRangeIntersect) { |
| // Case #1: (empty) (empty) |
| EXPECT_EQ(fidl::VersionRange::Intersect(std::nullopt, std::nullopt), std::nullopt); |
| |
| // Case #2: (empty) |---| |
| EXPECT_EQ(fidl::VersionRange::Intersect(std::nullopt, range(3, 6)), std::nullopt); |
| |
| // Case #3: |---| (empty) |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), std::nullopt), std::nullopt); |
| |
| // Case #4: |---| |
| // |--| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(7, 9))), std::nullopt); |
| |
| // Case #5: |---| |
| // |--| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(6, 8))), std::nullopt); |
| |
| // Case #6: |---| |
| // |--| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(5, 7))), range(5, 6)); |
| |
| // Case #7: |---| |
| // |--| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(4, 6))), range(4, 6)); |
| |
| // Case #8: |---| |
| // |--| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(3, 5))), range(3, 5)); |
| |
| // Case #9: |---| |
| // |-| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(4, 5))), range(4, 5)); |
| |
| // Case #10: |---| |
| // |---| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(3, 6))), range(3, 6)); |
| |
| // Case #11: |---| |
| // |--| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(2, 4))), range(3, 4)); |
| |
| // Case #12: |---| |
| // |--| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(1, 3))), std::nullopt); |
| |
| // Case #13: |---| |
| // |--| |
| EXPECT_EQ(fidl::VersionRange::Intersect(range(3, 6), (range(1, 2))), std::nullopt); |
| } |
| |
| TEST(VersioningTypesTests, GoodVersionRangeSubtract) { |
| EXPECT_EQ(fidl::VersionRange::Subtract(std::nullopt, std::nullopt), std::nullopt); |
| EXPECT_EQ(fidl::VersionRange::Subtract(std::nullopt, range(1, 2)), std::nullopt); |
| EXPECT_EQ(fidl::VersionRange::Subtract(range(1, 2), (range(1, 2))), std::nullopt); |
| EXPECT_EQ(fidl::VersionRange::Subtract(range(1, 2), (range(1, 3))), std::nullopt); |
| EXPECT_EQ(fidl::VersionRange::Subtract(range(2, 3), (range(1, 3))), std::nullopt); |
| EXPECT_EQ(fidl::VersionRange::Subtract(range(2, 3), (range(1, 4))), std::nullopt); |
| |
| EXPECT_EQ(fidl::VersionRange::Subtract(range(1, 2), std::nullopt), range(1, 2)); |
| EXPECT_EQ(fidl::VersionRange::Subtract(range(1, 3), (range(1, 2))), range(2, 3)); |
| EXPECT_EQ(fidl::VersionRange::Subtract(range(1, 3), (range(2, 3))), range(1, 2)); |
| EXPECT_EQ(fidl::VersionRange::Subtract(range(2, 4), (range(1, 3))), range(3, 4)); |
| EXPECT_EQ(fidl::VersionRange::Subtract(range(2, 4), (range(3, 5))), range(2, 3)); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityInitNone) { |
| Availability availability; |
| ASSERT_TRUE(availability.Init(std::nullopt, std::nullopt, std::nullopt)); |
| EXPECT_EQ(availability.Debug(), "_ _ _"); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityInitSome) { |
| Availability availability; |
| ASSERT_TRUE(availability.Init(Version::From(1), std::nullopt, std::nullopt)); |
| EXPECT_EQ(availability.Debug(), "1 _ _"); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityInitAll) { |
| Availability availability; |
| ASSERT_TRUE(availability.Init(Version::From(1), Version::From(2), Version::From(3))); |
| EXPECT_EQ(availability.Debug(), "1 2 3"); |
| } |
| |
| TEST(VersioningTypesTests, BadAvailabilityInitWrongOrder) { |
| Availability availability; |
| EXPECT_FALSE(availability.Init(Version::From(1), std::nullopt, Version::From(1))); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityInheritUnbounded) { |
| Availability availability; |
| ASSERT_TRUE(availability.Init(std::nullopt, std::nullopt, std::nullopt)); |
| ASSERT_TRUE(availability.Inherit(Availability::Unbounded()).Ok()); |
| EXPECT_EQ(availability.Debug(), "-inf _ +inf"); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityInheritUnset) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(Version::From(1), Version::From(2), Version::From(3))); |
| ASSERT_TRUE(child.Init(std::nullopt, std::nullopt, std::nullopt)); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| ASSERT_TRUE(child.Inherit(parent).Ok()); |
| EXPECT_EQ(parent.Debug(), "1 2 3"); |
| EXPECT_EQ(child.Debug(), "1 2 3"); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityInheritUnchanged) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(Version::From(1), Version::From(2), Version::From(3))); |
| ASSERT_TRUE(child.Init(Version::From(1), Version::From(2), Version::From(3))); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| ASSERT_TRUE(child.Inherit(parent).Ok()); |
| EXPECT_EQ(parent.Debug(), "1 2 3"); |
| EXPECT_EQ(child.Debug(), "1 2 3"); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityInheritPartial) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(Version::From(1), std::nullopt, std::nullopt)); |
| ASSERT_TRUE(child.Init(std::nullopt, std::nullopt, Version::From(2))); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| ASSERT_TRUE(child.Inherit(parent).Ok()); |
| EXPECT_EQ(parent.Debug(), "1 _ +inf"); |
| EXPECT_EQ(child.Debug(), "1 _ 2"); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityInheritChangeDeprecation) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(Version::From(1), Version::From(1), std::nullopt)); |
| ASSERT_TRUE(child.Init(Version::From(2), std::nullopt, std::nullopt)); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| ASSERT_TRUE(child.Inherit(parent).Ok()); |
| EXPECT_EQ(parent.Debug(), "1 1 +inf"); |
| EXPECT_EQ(child.Debug(), "2 2 +inf"); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityInheritEliminateDeprecation) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(Version::From(1), Version::From(2), std::nullopt)); |
| ASSERT_TRUE(child.Init(std::nullopt, std::nullopt, Version::From(2))); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| ASSERT_TRUE(child.Inherit(parent).Ok()); |
| EXPECT_EQ(parent.Debug(), "1 2 +inf"); |
| EXPECT_EQ(child.Debug(), "1 _ 2"); |
| } |
| |
| TEST(VersioningTypesTests, BadAvailabilityInheritBeforeParentCompletely) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(Version::From(3), std::nullopt, std::nullopt)); |
| ASSERT_TRUE(child.Init(Version::From(1), Version::From(2), Version::From(3))); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| |
| auto status = child.Inherit(parent); |
| EXPECT_EQ(status.added, Availability::InheritResult::Status::kBeforeParentAdded); |
| EXPECT_EQ(status.deprecated, Availability::InheritResult::Status::kBeforeParentAdded); |
| EXPECT_EQ(status.removed, Availability::InheritResult::Status::kBeforeParentAdded); |
| } |
| |
| TEST(VersioningTypesTests, BadAvailabilityInheritBeforeParentPartially) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(Version::From(3), std::nullopt, std::nullopt)); |
| ASSERT_TRUE(child.Init(Version::From(1), Version::From(2), Version::From(4))); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| |
| auto status = child.Inherit(parent); |
| EXPECT_EQ(status.added, Availability::InheritResult::Status::kBeforeParentAdded); |
| EXPECT_EQ(status.deprecated, Availability::InheritResult::Status::kBeforeParentAdded); |
| EXPECT_EQ(status.removed, Availability::InheritResult::Status::kOk); |
| } |
| |
| TEST(VersioningTypesTests, BadAvailabilityInheritAfterParentCompletely) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(std::nullopt, std::nullopt, Version::From(2))); |
| ASSERT_TRUE(child.Init(Version::From(2), Version::From(3), Version::From(4))); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| |
| auto status = child.Inherit(parent); |
| EXPECT_EQ(status.added, Availability::InheritResult::Status::kAfterParentRemoved); |
| EXPECT_EQ(status.deprecated, Availability::InheritResult::Status::kAfterParentRemoved); |
| EXPECT_EQ(status.removed, Availability::InheritResult::Status::kAfterParentRemoved); |
| } |
| |
| TEST(VersioningTypesTests, BadAvailabilityInheritAfterParentPartially) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(std::nullopt, std::nullopt, Version::From(2))); |
| ASSERT_TRUE(child.Init(Version::From(1), Version::From(2), Version::From(3))); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| |
| auto status = child.Inherit(parent); |
| EXPECT_EQ(status.added, Availability::InheritResult::Status::kOk); |
| EXPECT_EQ(status.deprecated, Availability::InheritResult::Status::kAfterParentRemoved); |
| EXPECT_EQ(status.removed, Availability::InheritResult::Status::kAfterParentRemoved); |
| } |
| |
| TEST(VersioningTypesTests, BadAvailabilityInheritAfterParentDeprecated) { |
| Availability parent, child; |
| ASSERT_TRUE(parent.Init(std::nullopt, Version::From(2), std::nullopt)); |
| ASSERT_TRUE(child.Init(Version::From(1), Version::From(3), Version::From(4))); |
| ASSERT_TRUE(parent.Inherit(Availability::Unbounded()).Ok()); |
| |
| auto status = child.Inherit(parent); |
| EXPECT_EQ(status.added, Availability::InheritResult::Status::kOk); |
| EXPECT_EQ(status.deprecated, Availability::InheritResult::Status::kAfterParentDeprecated); |
| EXPECT_EQ(status.removed, Availability::InheritResult::Status::kOk); |
| } |
| |
| TEST(VersioningTypesTests, GoodAvailabilityDecomposeWhole) { |
| Availability availability; |
| ASSERT_TRUE(availability.Init(Version::From(1), std::nullopt, Version::From(2))); |
| ASSERT_TRUE(availability.Inherit(Availability::Unbounded()).Ok()); |
| |
| availability.Narrow(range(1, 2)); |
| EXPECT_EQ(availability.Debug(), "1 _ 2"); |
| } |
| |
| } // namespace |