diff options
| author | pechatnov <[email protected]> | 2025-03-20 13:43:00 +0300 |
|---|---|---|
| committer | pechatnov <[email protected]> | 2025-03-20 14:01:03 +0300 |
| commit | 947f03db644bc892f3ba032c6b5ff6302eb696bc (patch) | |
| tree | 5d65359ea7f7ca1070aeec660249349eb312c310 | |
| parent | daa0fae61cb590ee620443fde726dae156c60d4e (diff) | |
YT: Parse strings '10G', '8Ki' as TSize (~i64)
commit_hash:3957ea35dac5850782a055f4e9f77151da63874b
| -rw-r--r-- | yt/yt/core/ya.make | 1 | ||||
| -rw-r--r-- | yt/yt/core/ytree/public.h | 4 | ||||
| -rw-r--r-- | yt/yt/core/ytree/serialize.cpp | 23 | ||||
| -rw-r--r-- | yt/yt/core/ytree/serialize.h | 4 | ||||
| -rw-r--r-- | yt/yt/core/ytree/size-inl.h | 121 | ||||
| -rw-r--r-- | yt/yt/core/ytree/size.cpp | 102 | ||||
| -rw-r--r-- | yt/yt/core/ytree/size.h | 48 | ||||
| -rw-r--r-- | yt/yt/core/ytree/unittests/size_ut.cpp | 213 | ||||
| -rw-r--r-- | yt/yt/core/ytree/unittests/ya.make | 1 | ||||
| -rw-r--r-- | yt/yt/core/ytree/unittests/yson_struct_ut.cpp | 33 |
10 files changed, 546 insertions, 4 deletions
diff --git a/yt/yt/core/ya.make b/yt/yt/core/ya.make index 81e4e3fe30e..a87958125de 100644 --- a/yt/yt/core/ya.make +++ b/yt/yt/core/ya.make @@ -288,6 +288,7 @@ SRCS( ytree/request_complexity_limiter.cpp ytree/request_complexity_limits.cpp ytree/serialize.cpp + ytree/size.cpp ytree/static_service_dispatcher.cpp ytree/system_attribute_provider.cpp ytree/tree_builder.cpp diff --git a/yt/yt/core/ytree/public.h b/yt/yt/core/ytree/public.h index c4bf9e3014f..368840528c1 100644 --- a/yt/yt/core/ytree/public.h +++ b/yt/yt/core/ytree/public.h @@ -132,4 +132,8 @@ DECLARE_REFCOUNTED_CLASS(TReadRequestComplexityLimiter) //////////////////////////////////////////////////////////////////////////////// +class TSize; + +//////////////////////////////////////////////////////////////////////////////// + } // namespace NYT::NYTree diff --git a/yt/yt/core/ytree/serialize.cpp b/yt/yt/core/ytree/serialize.cpp index b0ebda38717..b3949aef33f 100644 --- a/yt/yt/core/ytree/serialize.cpp +++ b/yt/yt/core/ytree/serialize.cpp @@ -1,5 +1,6 @@ #include "serialize.h" +#include "size.h" #include "tree_visitor.h" #include <yt/yt/core/misc/blob.h> @@ -173,6 +174,13 @@ void Serialize(IInputStream& input, IYsonConsumer* consumer) Serialize(TYsonInput(&input), consumer); } +// TSize +void Serialize(const TSize& value, NYson::IYsonConsumer* consumer) +{ + Serialize(value.Underlying(), consumer); +} + + // TStatisticPath. void Serialize(const NStatisticPath::TStatisticPath& path, IYsonConsumer* consumer) { @@ -360,6 +368,21 @@ void Deserialize(TGuid& value, INodePtr node) value = TGuid::FromString(node->AsString()->GetValue()); } +// TSize +void Deserialize(TSize& value, INodePtr node) +{ + if (node->GetType() == ENodeType::Int64) { + value = TSize(node->AsInt64()->GetValue()); + } else if (node->GetType() == ENodeType::Uint64) { + value = TSize(CheckedIntegralCast<i64>(node->AsUint64()->GetValue())); + } else if (node->GetType() == ENodeType::String) { + value = TSize::FromString(node->AsString()->GetValue()); + } else { + THROW_ERROR_EXCEPTION("Cannot parse TSize value from %Qlv", + node->GetType()); + } +} + // TStatisticPath. void Deserialize(NStatisticPath::TStatisticPath& value, INodePtr node) { diff --git a/yt/yt/core/ytree/serialize.h b/yt/yt/core/ytree/serialize.h index 98622249f08..138c77e1d62 100644 --- a/yt/yt/core/ytree/serialize.h +++ b/yt/yt/core/ytree/serialize.h @@ -178,6 +178,8 @@ void Serialize( template <class T, class TTag> void Serialize(const TStrongTypedef<T, TTag>& value, NYson::IYsonConsumer* consumer); +void Serialize(const TSize& value, NYson::IYsonConsumer* consumer); + void Serialize(const NStatisticPath::TStatisticPath& path, NYson::IYsonConsumer* consumer); //////////////////////////////////////////////////////////////////////////////// @@ -285,6 +287,8 @@ void Deserialize( template <class T, class TTag> void Deserialize(TStrongTypedef<T, TTag>& value, INodePtr node); +void Deserialize(TSize& value, INodePtr node); + void Deserialize(NStatisticPath::TStatisticPath& path, INodePtr node); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/ytree/size-inl.h b/yt/yt/core/ytree/size-inl.h new file mode 100644 index 00000000000..6cd360967f7 --- /dev/null +++ b/yt/yt/core/ytree/size-inl.h @@ -0,0 +1,121 @@ +#ifndef YTREE_SIZE_INL_H_ +#error "Direct inclusion of this file is not allowed, include size.h" +// For the sake of sane code completion. +#include "size.h" +#endif + +namespace NYT::NYTree { + +//////////////////////////////////////////////////////////////////////////////// + +constexpr TSize::TSize() + : Underlying_(0) +{ } + +constexpr TSize::TSize(TUnderlying underlying) + : Underlying_(underlying) +{ } + +constexpr TSize::operator const TUnderlying&() const +{ + return Underlying_; +} + +constexpr TSize::operator TUnderlying&() +{ + return Underlying_; +} + +constexpr TSize::TUnderlying& TSize::Underlying() & +{ + return Underlying_; +} + +constexpr const TSize::TUnderlying& TSize::Underlying() const & +{ + return Underlying_; +} + +constexpr TSize::TUnderlying TSize::Underlying() && +{ + return Underlying_; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NYTree + +//////////////////////////////////////////////////////////////////////////////// + +namespace std { + +//////////////////////////////////////////////////////////////////////////////// + +template <> +struct hash<NYT::NYTree::TSize> +{ + size_t operator()(const NYT::NYTree::TSize& value) const + { + return std::hash<NYT::NYTree::TSize::TUnderlying>()(value.Underlying()); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <> +class numeric_limits<NYT::NYTree::TSize> +{ +public: + #define XX(name) \ + static constexpr decltype(numeric_limits<NYT::NYTree::TSize::TUnderlying>::name) name = numeric_limits<NYT::NYTree::TSize::TUnderlying>::name; + + XX(is_specialized) + XX(is_signed) + XX(digits) + XX(digits10) + XX(max_digits10) + XX(is_integer) + XX(is_exact) + XX(radix) + XX(min_exponent) + XX(min_exponent10) + XX(max_exponent) + XX(max_exponent10) + XX(has_infinity) + XX(has_quiet_NaN) + XX(has_signaling_NaN) + XX(has_denorm) + XX(has_denorm_loss) + XX(is_iec559) + XX(is_bounded) + XX(is_modulo) + XX(traps) + XX(tinyness_before) + XX(round_style) + + #undef XX + + #define XX(name) \ + static constexpr NYT::NYTree::TSize name() noexcept \ + { \ + return NYT::NYTree::TSize(numeric_limits<NYT::NYTree::TSize::TUnderlying>::name()); \ + } + + XX(min) + XX(max) + XX(lowest) + XX(epsilon) + XX(round_error) + XX(infinity) + XX(quiet_NaN) + XX(signaling_NaN) + XX(denorm_min) + + #undef XX +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace std + +//////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/ytree/size.cpp b/yt/yt/core/ytree/size.cpp new file mode 100644 index 00000000000..ec29ebb9a4c --- /dev/null +++ b/yt/yt/core/ytree/size.cpp @@ -0,0 +1,102 @@ +#include "size.h" + +#include <yt/yt/core/misc/error.h> + +#include <util/string/cast.h> + +namespace NYT { + +namespace NYTree { + +//////////////////////////////////////////////////////////////////////////////// + +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +// Use suffixes for 1000, ..., 1000 ** 6 and 1024, ..., 1024 ** 6. +// 1000 ** 6 < 1024 ** 6 = 2 ** 60 < std::numeric_limits<TSize::TUnderlying>::max(). +constexpr int MaxMultiplierOrder = 6; +using TMultipliers = std::array<std::array<TSize::TUnderlying, MaxMultiplierOrder + 1>, 2>; + +constexpr TMultipliers Multipliers = std::invoke([] { + TMultipliers result; + result[0][0] = 1; + result[1][0] = 1; + for (int i = 1; i <= MaxMultiplierOrder; ++i) { + result[0][i] = result[0][i - 1] * 1000; + result[1][i] = result[1][i - 1] * 1024; + } + return result; +}); + +TSize::TUnderlying DeserializeSizeWithSuffixesImpl(TStringBuf originalValue) +{ + TStringBuf value = originalValue; + + bool basedOnPowerOf2 = value.ChopSuffix("i"); + int order = + value.ChopSuffix("K") ? 1 : + value.ChopSuffix("M") ? 2 : + value.ChopSuffix("G") ? 3 : + value.ChopSuffix("T") ? 4 : + value.ChopSuffix("P") ? 5 : + value.ChopSuffix("E") ? 6 : 0; + + TSize::TUnderlying multiplier = Multipliers[static_cast<int>(basedOnPowerOf2)][order]; + TSize::TUnderlying result = FromString<TSize::TUnderlying>(value); + + bool tooLargeValue = result < 0 + ? result < std::numeric_limits<TSize::TUnderlying>::lowest() / multiplier + : result > std::numeric_limits<TSize::TUnderlying>::max() / multiplier; + THROW_ERROR_EXCEPTION_IF(tooLargeValue, "Cannot parse too large value %Qlv as 64-bit integral type", originalValue); + + return result * multiplier; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// + +TSize TSize::FromString(TStringBuf serializedValue) +{ + return TSize(DeserializeSizeWithSuffixesImpl(serializedValue)); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NYTree + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT + +//////////////////////////////////////////////////////////////////////////////// + +template <> +NYT::NYTree::TSize FromStringImpl<NYT::NYTree::TSize, char>(const char* data, size_t size) +{ + return NYT::NYTree::TSize::FromString(TStringBuf(data, size)); +} + +template<> +bool TryFromStringImpl<NYT::NYTree::TSize, char>(const char* data, size_t size, NYT::NYTree::TSize& value) +{ + try { + value = NYT::NYTree::TSize::FromString(TStringBuf(data, size)); + return true; + } catch (const std::exception&) { + return false; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +template <> +void Out<NYT::NYTree::TSize>(IOutputStream& out, const NYT::NYTree::TSize& value) { + out << value.Underlying(); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/ytree/size.h b/yt/yt/core/ytree/size.h new file mode 100644 index 00000000000..1f15c9c68b9 --- /dev/null +++ b/yt/yt/core/ytree/size.h @@ -0,0 +1,48 @@ +#pragma once + +#include "public.h" + +namespace NYT::NYTree { + +//////////////////////////////////////////////////////////////////////////////// + +//! Semi-strong typedef for integral type with advanced parsing from string. +//! Constructor/FromString<TSize> parses "2M" as 2'000'000 and "1Ki" as 1024. +//! Supported suffixes: K, Ki, M, Mi, G, Gi, T, Ti, P, Pi, E, Ei. +class TSize +{ +public: + using TUnderlying = i64; + + constexpr TSize(); + + constexpr explicit TSize(TUnderlying value); + + TSize(const TSize&) = default; + TSize(TSize&&) = default; + + static TSize FromString(TStringBuf serializedValue); + + TSize& operator=(const TSize&) = default; + TSize& operator=(TSize&&) = default; + + constexpr operator const TUnderlying&() const; + constexpr operator TUnderlying&(); + + constexpr auto operator<=>(const TSize& rhs) const = default; + + constexpr TUnderlying& Underlying() &; + constexpr const TUnderlying& Underlying() const &; + constexpr TUnderlying Underlying() &&; + +private: + TUnderlying Underlying_; +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NYTree + +#define YTREE_SIZE_INL_H_ +#include "size-inl.h" +#undef YTREE_SIZE_INL_H_ diff --git a/yt/yt/core/ytree/unittests/size_ut.cpp b/yt/yt/core/ytree/unittests/size_ut.cpp new file mode 100644 index 00000000000..77a407b947c --- /dev/null +++ b/yt/yt/core/ytree/unittests/size_ut.cpp @@ -0,0 +1,213 @@ +#include <yt/yt/core/test_framework/framework.h> + +#include <yt/yt/core/ytree/size.h> + +namespace NYT::NYTree { +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +#define XX(name) \ + static_assert(std::numeric_limits<TSize>::name == std::numeric_limits<i64>::name); + +XX(is_signed) +XX(digits) +XX(digits10) +XX(max_digits10) +XX(is_integer) +XX(is_exact) +XX(radix) +XX(min_exponent) +XX(min_exponent10) +XX(max_exponent) +XX(max_exponent10) +XX(has_infinity) +XX(has_quiet_NaN) +XX(has_signaling_NaN) +XX(has_denorm) +XX(has_denorm_loss) +XX(is_iec559) +XX(is_bounded) +XX(is_modulo) +XX(traps) +XX(tinyness_before) +XX(round_style) + +#undef XX + +#define XX(name) \ + static_assert(std::numeric_limits<TSize>::name() == TSize(std::numeric_limits<i64>::name())); + +XX(min) +XX(max) +XX(lowest) +XX(epsilon) +XX(round_error) +XX(infinity) +XX(quiet_NaN) +XX(signaling_NaN) +XX(denorm_min) + +#undef XX + +//////////////////////////////////////////////////////////////////////////////// + +TEST(TYTreeSizeTest, Simple) +{ + EXPECT_EQ(TSize(1), TSize(1)); + EXPECT_NE(TSize(1), TSize(2)); + + EXPECT_EQ(TSize(1), 1); + EXPECT_LE(0, TSize(1)); + EXPECT_GE(TSize(1), 0); + EXPECT_EQ(1u, TSize(1)); + + EXPECT_TRUE(TSize(1)); + EXPECT_FALSE(TSize(0)); + + EXPECT_EQ(TSize(10) + 100, 110); + EXPECT_EQ(100 + TSize(10), 110); + EXPECT_EQ(10 * TSize(10), 100); + EXPECT_EQ(TSize(10) * 10, 100); + EXPECT_EQ(TSize(10) / 10, 1); + EXPECT_EQ(10 / TSize(10), 1); + EXPECT_EQ(10 - TSize(10), 0); + EXPECT_EQ(TSize(10) - 10, 0); + + { + EXPECT_EQ(TSize(10).Underlying(), 10); + + TSize a{56}; + a.Underlying() = 55; + EXPECT_EQ(a.Underlying(), 55); + + const TSize b{58}; + static_assert(std::is_const_v<std::remove_reference_t<decltype(b.Underlying())>>); + EXPECT_EQ(b.Underlying(), 58); + EXPECT_EQ(b, 58); + } +} + +TEST(TYTreeSizeTest, Serialize) +{ + EXPECT_EQ(ToString(TSize(1)), TString{"1"}); + EXPECT_EQ(Format("|%v|", TSize(42)), TString{"|42|"}); + + { + TSize v; + EXPECT_TRUE(::TryFromString("21", 2, v)); + EXPECT_EQ(TSize(21), v); + EXPECT_FALSE(::TryFromString("", 0, v)); + } + + EXPECT_EQ(TSize(46), FromString<TSize>("46")); + + { + TStringBuilder builder; + Format(&builder, "%v", TSize(42)); + EXPECT_EQ(builder.Flush(), "42"); + } + + { + TStringStream stream; + stream << TSize(42); + EXPECT_EQ(stream.Str(), "42"); + } +} + +TEST(TYTreeSizeTest, Constexpr) +{ + static_assert(TSize(1) == TSize(1)); + static_assert(TSize(1000).Underlying() == 1000); +} + +TEST(TYTreeSizeTest, Hash) +{ + for (i64 i = -1000; i < 1000; ++i) { + EXPECT_EQ(THash<TSize>()(TSize(i)), THash<i64>()(i)); + EXPECT_EQ(std::hash<TSize>()(TSize(i)), std::hash<i64>()(i)); + } +} + +i64 DeserializeString(TStringBuf value) +{ + return TSize::FromString(value).Underlying(); +} + +TEST(TYTreeSizeTest, FromString) +{ + EXPECT_EQ(0, DeserializeString("0")); + EXPECT_EQ(127, DeserializeString("127")); + EXPECT_EQ(-2, DeserializeString("-2")); + + EXPECT_EQ(1000, DeserializeString("1K")); + EXPECT_EQ(3000000, DeserializeString("3M")); + EXPECT_EQ(-1024, DeserializeString("-1Ki")); + EXPECT_EQ(2000000000, DeserializeString("2G")); + EXPECT_THROW(DeserializeString("2000000000000P"), std::exception); + + EXPECT_EQ(0, DeserializeString("0E")); + EXPECT_EQ(1000, DeserializeString("1K")); + EXPECT_EQ(1000'000, DeserializeString("1M")); + EXPECT_EQ(1000'000'000, DeserializeString("1G")); + EXPECT_EQ(1000'000'000'000, DeserializeString("1T")); + EXPECT_EQ(1000'000'000'000'000, DeserializeString("1P")); + EXPECT_EQ(1000'000'000'000'000'000, DeserializeString("1E")); + EXPECT_EQ(0LL, DeserializeString("0Ei")); + EXPECT_EQ(1024LL, DeserializeString("1Ki")); + EXPECT_EQ(1024LL * 1024, DeserializeString("1Mi")); + EXPECT_EQ(1024LL * 1024 * 1024, DeserializeString("1Gi")); + EXPECT_EQ(1024LL * 1024 * 1024 * 1024, DeserializeString("1Ti")); + EXPECT_EQ(1024LL * 1024 * 1024 * 1024 * 1024, DeserializeString("1Pi")); + EXPECT_EQ(1024LL * 1024 * 1024 * 1024 * 1024 * 1024, DeserializeString("1Ei")); + + EXPECT_THROW(DeserializeString("G"), std::exception); + EXPECT_THROW(DeserializeString("Gi"), std::exception); + EXPECT_THROW(DeserializeString("1GG"), std::exception); + EXPECT_THROW(DeserializeString("1KG"), std::exception); + EXPECT_THROW(DeserializeString("1KiG"), std::exception); + EXPECT_THROW(DeserializeString("1Ki2"), std::exception); +} + +TEST(TYTreeSizeTest, FromStringBoundaryCases) +{ + EXPECT_EQ(std::numeric_limits<i64>::max(), DeserializeString("9223372036854775807")); + EXPECT_EQ(std::numeric_limits<i64>::lowest(), DeserializeString("-9223372036854775808")); + EXPECT_THROW(DeserializeString("9223372036854775808"), std::exception); + EXPECT_THROW(DeserializeString("-9223372036854775809"), std::exception); + + EXPECT_EQ(9'223'372'036'854'775'000LL, DeserializeString("9223372036854775K")); + EXPECT_EQ(-9'223'372'036'854'775'000LL, DeserializeString("-9223372036854775K")); + EXPECT_THROW(DeserializeString("9223372036854776K"), std::exception); + EXPECT_THROW(DeserializeString("-9223372036854776K"), std::exception); + + EXPECT_EQ(9'223'372'036'854'000'000LL, DeserializeString("9223372036854M")); + EXPECT_EQ(-9'223'372'036'854'000'000LL, DeserializeString("-9223372036854M")); + EXPECT_THROW(DeserializeString("9223372036855M"), std::exception); + EXPECT_THROW(DeserializeString("-9223372036855M"), std::exception); + + EXPECT_EQ(9'223'372'036'000'000'000LL, DeserializeString("9223372036G")); + EXPECT_EQ(-9'223'372'036'000'000'000LL, DeserializeString("-9223372036G")); + EXPECT_THROW(DeserializeString("9223372037G"), std::exception); + EXPECT_THROW(DeserializeString("-9223372037G"), std::exception); + + EXPECT_EQ(9'223'372'000'000'000'000LL, DeserializeString("9223372T")); + EXPECT_EQ(-9'223'372'000'000'000'000LL, DeserializeString("-9223372T")); + EXPECT_THROW(DeserializeString("9223373T"), std::exception); + EXPECT_THROW(DeserializeString("-9223373T"), std::exception); + + EXPECT_EQ(9'223'000'000'000'000'000LL, DeserializeString("9223P")); + EXPECT_EQ(-9'223'000'000'000'000'000LL, DeserializeString("-9223P")); + EXPECT_THROW(DeserializeString("9224P"), std::exception); + EXPECT_THROW(DeserializeString("-9224P"), std::exception); + + EXPECT_EQ(9'000'000'000'000'000'000LL, DeserializeString("9E")); + EXPECT_EQ(-9'000'000'000'000'000'000LL, DeserializeString("-9E")); + EXPECT_THROW(DeserializeString("10E"), std::exception); + EXPECT_THROW(DeserializeString("-10E"), std::exception); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace +} // namespace NYT::NYTree diff --git a/yt/yt/core/ytree/unittests/ya.make b/yt/yt/core/ytree/unittests/ya.make index 172b7702944..256b8c014bc 100644 --- a/yt/yt/core/ytree/unittests/ya.make +++ b/yt/yt/core/ytree/unittests/ya.make @@ -10,6 +10,7 @@ SRCS( resolver_ut.cpp serialize_ut.cpp service_combiner_ut.cpp + size_ut.cpp text_yson_convert_ut.cpp tree_builder_ut.cpp lazy_ypath_service_ut.cpp diff --git a/yt/yt/core/ytree/unittests/yson_struct_ut.cpp b/yt/yt/core/ytree/unittests/yson_struct_ut.cpp index f38c233f446..1708ccbec83 100644 --- a/yt/yt/core/ytree/unittests/yson_struct_ut.cpp +++ b/yt/yt/core/ytree/unittests/yson_struct_ut.cpp @@ -5,6 +5,7 @@ #include <yt/yt/core/ytree/ephemeral_node_factory.h> #include <yt/yt/core/ytree/fluent.h> #include <yt/yt/core/ytree/polymorphic_yson_struct.h> +#include <yt/yt/core/ytree/size.h> #include <yt/yt/core/ytree/tree_builder.h> #include <yt/yt/core/ytree/tree_visitor.h> #include <yt/yt/core/ytree/ypath_client.h> @@ -42,6 +43,8 @@ struct TTestSubconfig bool MyBool; std::vector<TString> MyStringList; ETestEnum MyEnum; + TDuration MyDuration; + TSize MySize; REGISTER_YSON_STRUCT(TTestSubconfig); @@ -59,6 +62,10 @@ struct TTestSubconfig .Default(); registrar.Parameter("my_enum", &TThis::MyEnum) .Default(ETestEnum::Value1); + registrar.Parameter("my_duration", &TThis::MyDuration) + .Default(TDuration::Seconds(1)); + registrar.Parameter("my_size", &TThis::MySize) + .Default(TSize::FromString("8K")); } }; @@ -135,6 +142,8 @@ auto GetCompleteConfigNode(int offset = 0) .Item().Value("ListItem1") .Item().Value("ListItem2") .EndList() + .Item("my_duration").Value("2h") + .Item("my_size").Value("2M") .EndMap() .Item("sub_list").BeginList() .Item().BeginMap() @@ -147,6 +156,8 @@ auto GetCompleteConfigNode(int offset = 0) .Item().Value("ListItem1") .Item().Value("ListItem2") .EndList() + .Item("my_duration").Value("2h") + .Item("my_size").Value(2'000'000) .EndMap() .Item().BeginMap() .Item("my_int").Value(99 + offset) @@ -158,6 +169,8 @@ auto GetCompleteConfigNode(int offset = 0) .Item().Value("ListItem1") .Item().Value("ListItem2") .EndList() + .Item("my_duration").Value("2h") + .Item("my_size").Value("2000K") .EndMap() .EndList() .Item("sub_map").BeginMap() @@ -171,6 +184,8 @@ auto GetCompleteConfigNode(int offset = 0) .Item().Value("ListItem1") .Item().Value("ListItem2") .EndList() + .Item("my_duration").Value("2h") + .Item("my_size").Value(2'000'000) .EndMap() .Item("sub2").BeginMap() .Item("my_int").Value(99 + offset) @@ -182,6 +197,8 @@ auto GetCompleteConfigNode(int offset = 0) .Item().Value("ListItem1") .Item().Value("ListItem2") .EndList() + .Item("my_duration").Value(2 * 60 * 60 * 1000) + .Item("my_size").Value(2'000'000) .EndMap() .EndMap() .EndMap(); @@ -189,7 +206,7 @@ auto GetCompleteConfigNode(int offset = 0) void TestCompleteSubconfig(TTestSubconfig* subconfig, int offset = 0) { - for (auto field : {"my_int", "my_uint", "my_bool", "my_enum", "my_string_list"}) { + for (auto field : {"my_int", "my_uint", "my_bool", "my_enum", "my_string_list", "my_duration", "my_size"}) { EXPECT_TRUE(subconfig->IsSet(field)); } @@ -201,6 +218,8 @@ void TestCompleteSubconfig(TTestSubconfig* subconfig, int offset = 0) EXPECT_EQ("ListItem1", subconfig->MyStringList[1]); EXPECT_EQ("ListItem2", subconfig->MyStringList[2]); EXPECT_EQ(ETestEnum::Value2, subconfig->MyEnum); + EXPECT_EQ(TDuration::Hours(2), subconfig->MyDuration); + EXPECT_EQ(2'000'000, subconfig->MySize); } void TestCompleteConfig(TIntrusivePtr<TTestConfig> config, int offset = 0) @@ -695,14 +714,18 @@ TEST(TYsonStructTest, Save) "\"my_enum\"=\"value1\";" "\"my_int\"=200;" "\"my_uint\"=50u;" - "\"my_string_list\"=[]}"; + "\"my_string_list\"=[];" + "\"my_duration\"=1000;" + "\"my_size\"=8000}"; TString subconfigYsonOrigin = "{\"my_bool\"=%false;" "\"my_enum\"=\"value1\";" "\"my_int\"=100;" "\"my_uint\"=50u;" - "\"my_string_list\"=[]}"; + "\"my_string_list\"=[];" + "\"my_duration\"=1000;" + "\"my_size\"=8000}"; TString expectedYson; expectedYson += "{\"my_string\"=\"hello!\";"; @@ -713,7 +736,9 @@ TEST(TYsonStructTest, Save) EXPECT_TRUE(AreNodesEqual( ConvertToNode(TYsonString(expectedYson)), - ConvertToNode(TYsonString(output.AsStringBuf())))); + ConvertToNode(TYsonString(output.AsStringBuf())))) + << "Expected: " << expectedYson + << ", got: " << output.AsStringBuf(); } TEST(TYsonStructTest, TestConfigUpdate) |
