#include "ysaveload.h" #include <library/cpp/testing/unittest/registar.h> #include <util/memory/pool.h> #include <util/stream/buffer.h> #include <util/memory/blob.h> #include <util/generic/list.h> #include <util/generic/map.h> #include <util/generic/hash_multi_map.h> #include <util/generic/deque.h> #include <util/generic/string.h> #include <util/generic/vector.h> #include <util/generic/buffer.h> #include <util/generic/hash_set.h> #include <util/generic/maybe.h> static inline char* AllocateFromPool(TMemoryPool& pool, size_t len) { return (char*)pool.Allocate(len); } class TSaveLoadTest: public TTestBase { UNIT_TEST_SUITE(TSaveLoadTest); UNIT_TEST(TestSaveLoad) UNIT_TEST(TestNewStyle) UNIT_TEST(TestNewNewStyle) UNIT_TEST(TestList) UNIT_TEST(TestTuple) UNIT_TEST(TestVariant) UNIT_TEST(TestOptional) UNIT_TEST(TestInheritNonVirtualClass) UNIT_TEST(TestInheritVirtualClass) UNIT_TEST_SUITE_END(); struct TSaveHelper { inline void Save(IOutputStream* o) const { o->Write("qwerty", 7); } inline void Load(IInputStream* i) { char buf[7]; UNIT_ASSERT_EQUAL(i->Load(buf, 7), 7); UNIT_ASSERT_EQUAL(strcmp(buf, "qwerty"), 0); } }; struct TNewStyleSaveHelper { template <class S> inline void SaveLoad(S* s) { ::SaveLoad(s, Str); } TString Str; }; struct TNewNewStyleHelper { TString Str; ui32 Int; Y_SAVELOAD_DEFINE(Str, Int); }; private: inline void TestNewNewStyle() { TString ss; { TNewNewStyleHelper h; h.Str = "qw"; h.Int = 42; TStringOutput so(ss); ::Save(&so, h); } { TNewNewStyleHelper h; TStringInput si(ss); ::Load(&si, h); UNIT_ASSERT_EQUAL(h.Str, "qw"); UNIT_ASSERT_EQUAL(h.Int, 42); } } inline void TestNewStyle() { TString ss; { TNewStyleSaveHelper sh; sh.Str = "qwerty"; TStringOutput so(ss); SaveLoad(&so, sh); } { TNewStyleSaveHelper sh; TStringInput si(ss); SaveLoad(&si, sh); UNIT_ASSERT_EQUAL(sh.Str, "qwerty"); } } inline void TestSaveLoad() { TBufferStream S_; //save part { Save(&S_, (ui8)1); Save(&S_, (ui16)2); Save(&S_, (ui32)3); Save(&S_, (ui64)4); } { TVector<ui16> vec; vec.push_back((ui16)1); vec.push_back((ui16)2); vec.push_back((ui16)4); Save(&S_, vec); } { TMap<ui16, ui32> map; map[(ui16)1] = 2; map[(ui16)2] = 3; map[(ui16)3] = 4; Save(&S_, map); } { TMultiMap<ui16, ui32> multimap; multimap.emplace((ui16)1, 2); multimap.emplace((ui16)2, 3); multimap.emplace((ui16)2, 4); multimap.emplace((ui16)2, 5); multimap.emplace((ui16)3, 6); Save(&S_, multimap); } { TSaveHelper helper; Save(&S_, helper); } { TString val("123456"); Save(&S_, val); } { TBuffer buf; buf.Append("asdf", 4); Save(&S_, buf); } { TVector<const char*> vec; vec.push_back("1"); vec.push_back("123"); vec.push_back("4567"); Save(&S_, vec); } { TDeque<ui16> deq; deq.push_back(1); deq.push_back(2); deq.push_back(4); deq.push_back(5); Save(&S_, deq); } { TMaybe<size_t> h(10); Save(&S_, h); } { TMaybe<size_t> h(20); Save(&S_, h); } { TMaybe<size_t> h; Save(&S_, h); } { TMaybe<size_t> h; Save(&S_, h); } { THashMultiMap<TString, int> mm; mm.insert({"one", 1}); mm.insert({"two", 2}); mm.insert({"two", 22}); Save(&S_, mm); } //load part { ui8 val; Load(&S_, val); UNIT_ASSERT_EQUAL(val, 1); } { ui16 val; Load(&S_, val); UNIT_ASSERT_EQUAL(val, 2); } { ui32 val; Load(&S_, val); UNIT_ASSERT_EQUAL(val, 3); } { ui64 val; Load(&S_, val); UNIT_ASSERT_EQUAL(val, 4); } { TVector<ui16> vec; Load(&S_, vec); UNIT_ASSERT_EQUAL(vec.size(), 3); UNIT_ASSERT_EQUAL(vec[0], 1); UNIT_ASSERT_EQUAL(vec[1], 2); UNIT_ASSERT_EQUAL(vec[2], 4); } { TMap<ui16, ui32> map; Load(&S_, map); UNIT_ASSERT_EQUAL(map.size(), 3); UNIT_ASSERT_EQUAL(map[(ui16)1], 2); UNIT_ASSERT_EQUAL(map[(ui16)2], 3); UNIT_ASSERT_EQUAL(map[(ui16)3], 4); } { TMultiMap<ui16, ui32> multimap; Load(&S_, multimap); UNIT_ASSERT_EQUAL(multimap.size(), 5); UNIT_ASSERT_EQUAL(multimap.find((ui16)1)->second, 2); UNIT_ASSERT_EQUAL(multimap.find((ui16)3)->second, 6); THashSet<ui32> values; auto range = multimap.equal_range((ui16)2); for (auto i = range.first; i != range.second; ++i) { values.insert(i->second); } UNIT_ASSERT_EQUAL(values.size(), 3); UNIT_ASSERT_EQUAL(values.contains(3), true); UNIT_ASSERT_EQUAL(values.contains(4), true); UNIT_ASSERT_EQUAL(values.contains(5), true); } { TSaveHelper helper; Load(&S_, helper); } { TString val; Load(&S_, val); UNIT_ASSERT_EQUAL(val, "123456"); } { TBuffer buf; Load(&S_, buf); UNIT_ASSERT_EQUAL(buf.size(), 4); UNIT_ASSERT_EQUAL(memcmp(buf.data(), "asdf", 4), 0); } { TVector<const char*> vec; TMemoryPool pool(1024); Load(&S_, vec, pool); UNIT_ASSERT_EQUAL(vec.size(), 3); UNIT_ASSERT_EQUAL(vec[0], TString("1")); UNIT_ASSERT_EQUAL(vec[1], TString("123")); UNIT_ASSERT_EQUAL(vec[2], TString("4567")); } { TDeque<ui16> deq; Load(&S_, deq); UNIT_ASSERT_EQUAL(deq.size(), 4); UNIT_ASSERT_EQUAL(deq[0], 1); UNIT_ASSERT_EQUAL(deq[1], 2); UNIT_ASSERT_EQUAL(deq[2], 4); UNIT_ASSERT_EQUAL(deq[3], 5); } { TMaybe<size_t> h(5); Load(&S_, h); UNIT_ASSERT_EQUAL(*h, 10); } { TMaybe<size_t> h; Load(&S_, h); UNIT_ASSERT_EQUAL(*h, 20); } { TMaybe<size_t> h; UNIT_ASSERT(!h); Load(&S_, h); UNIT_ASSERT(!h); } { TMaybe<size_t> h(7); UNIT_ASSERT(!!h); Load(&S_, h); UNIT_ASSERT(!h); } { THashMultiMap<TString, int> mm; Load(&S_, mm); UNIT_ASSERT_EQUAL(mm.size(), 3); UNIT_ASSERT_EQUAL(mm.count("one"), 1); auto oneIter = mm.equal_range("one").first; UNIT_ASSERT_EQUAL(oneIter->second, 1); UNIT_ASSERT_EQUAL(mm.count("two"), 2); auto twoIter = mm.equal_range("two").first; UNIT_ASSERT_EQUAL(twoIter->second, 2); UNIT_ASSERT_EQUAL((++twoIter)->second, 22); } } void TestList() { TBufferStream s; TList<int> list = {0, 1, 10}; Save(&s, list); list.clear(); Load(&s, list); UNIT_ASSERT_VALUES_EQUAL(list.size(), 3); UNIT_ASSERT_VALUES_EQUAL(*std::next(list.begin(), 0), 0); UNIT_ASSERT_VALUES_EQUAL(*std::next(list.begin(), 1), 1); UNIT_ASSERT_VALUES_EQUAL(*std::next(list.begin(), 2), 10); } void TestTuple() { TBufferStream s; using TTuple = std::tuple<int, TString, unsigned int>; const TTuple toSave{-10, "qwerty", 15}; Save(&s, toSave); TTuple toLoad; Load(&s, toLoad); UNIT_ASSERT_VALUES_EQUAL(std::get<0>(toLoad), std::get<0>(toSave)); UNIT_ASSERT_VALUES_EQUAL(std::get<1>(toLoad), std::get<1>(toSave)); UNIT_ASSERT_VALUES_EQUAL(std::get<2>(toLoad), std::get<2>(toSave)); } template <class TVariant, class T> void TestVariantImpl(TVariant& v, const T& expected) { v = expected; TBufferStream s; ::Save(&s, v); ::Load(&s, v); UNIT_ASSERT_VALUES_EQUAL(std::get<T>(v), expected); } void TestVariant() { std::variant<int, bool, TString, TVector<char>> v(1); TestVariantImpl(v, 42); TestVariantImpl(v, true); TestVariantImpl(v, TString("foo")); TestVariantImpl(v, TVector<char>{'b', 'a', 'r'}); v = TString("baz"); TBufferStream s; ::Save(&s, v); std::variant<char, bool> v2 = false; UNIT_ASSERT_EXCEPTION(::Load(&s, v2), TLoadEOF); } template <class T> void TestOptionalImpl(const std::optional<T>& v) { std::optional<T> loaded; TBufferStream s; ::Save(&s, v); ::Load(&s, loaded); UNIT_ASSERT_VALUES_EQUAL(v.has_value(), loaded.has_value()); if (v.has_value()) { UNIT_ASSERT_VALUES_EQUAL(*v, *loaded); } } void TestOptional() { TestOptionalImpl(std::optional<ui64>(42ull)); TestOptionalImpl(std::optional<bool>(true)); TestOptionalImpl(std::optional<TString>("abacaba")); TestOptionalImpl(std::optional<ui64>(std::nullopt)); } // tests serialization of class with three public string members template <class TDerived, class TInterface = TDerived> void TestInheritClassImpl() { TBufferStream s; { TDerived v1; v1.Str1 = "One"; v1.Str2 = "Two"; v1.Str3 = "Three"; ::Save(&s, static_cast<const TInterface&>(v1)); } { TDerived v2; ::Load(&s, static_cast<TInterface&>(v2)); UNIT_ASSERT_VALUES_EQUAL_C(v2.Str1, "One", TypeName<TDerived>() << " via " << TypeName<TInterface>()); UNIT_ASSERT_VALUES_EQUAL_C(v2.Str2, "Two", TypeName<TDerived>() << " via " << TypeName<TInterface>()); UNIT_ASSERT_VALUES_EQUAL_C(v2.Str3, "Three", TypeName<TDerived>() << " via " << TypeName<TInterface>()); } } void TestInheritNonVirtualClass() { struct TBaseNonVirtual { TString Str1; Y_SAVELOAD_DEFINE(Str1); }; struct TDerivedNonVirtual: TBaseNonVirtual { TString Str2; TString Str3; Y_SAVELOAD_DEFINE(TNonVirtualSaver<TBaseNonVirtual>{this}, Str2, Str3); }; TestInheritClassImpl<TDerivedNonVirtual>(); } void TestInheritVirtualClass() { struct IInterface { virtual void Save(IOutputStream* out) const = 0; virtual void Load(IInputStream* in) = 0; }; struct TBaseVirtual: IInterface { TString Str1; Y_SAVELOAD_DEFINE_OVERRIDE(Str1); }; struct TDerivedVirtual: TBaseVirtual { TString Str2; TString Str3; Y_SAVELOAD_DEFINE_OVERRIDE(TNonVirtualSaver<TBaseVirtual>{this}, Str2, Str3); }; TestInheritClassImpl<TDerivedVirtual>(); TestInheritClassImpl<TDerivedVirtual, TBaseVirtual>(); TestInheritClassImpl<TDerivedVirtual, IInterface>(); } }; UNIT_TEST_SUITE_REGISTRATION(TSaveLoadTest);