#include "ysaveload.h" #include #include #include #include #include #include #include #include #include #include #include #include #include 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(TestSaveLoadEmptyStruct) 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 inline void SaveLoad(S* s) { ::SaveLoad(s, Str); } TString Str; }; struct TNewNewStyleHelper { TString Str; ui32 Int; Y_SAVELOAD_DEFINE(Str, Int); }; struct TNewNewStyleEmptyHelper { Y_SAVELOAD_DEFINE(); }; 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 vec; vec.push_back((ui16)1); vec.push_back((ui16)2); vec.push_back((ui16)4); Save(&S_, vec); } { TMap map; map[(ui16)1] = 2; map[(ui16)2] = 3; map[(ui16)3] = 4; Save(&S_, map); } { TMultiMap 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 vec; vec.push_back("1"); vec.push_back("123"); vec.push_back("4567"); Save(&S_, vec); } { TDeque deq; deq.push_back(1); deq.push_back(2); deq.push_back(4); deq.push_back(5); Save(&S_, deq); } { TMaybe h(10); Save(&S_, h); } { TMaybe h(20); Save(&S_, h); } { TMaybe h; Save(&S_, h); } { TMaybe h; Save(&S_, h); } { THashMultiMap 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 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 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 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 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 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 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 h(5); Load(&S_, h); UNIT_ASSERT_EQUAL(*h, 10); } { TMaybe h; Load(&S_, h); UNIT_ASSERT_EQUAL(*h, 20); } { TMaybe h; UNIT_ASSERT(!h); Load(&S_, h); UNIT_ASSERT(!h); } { TMaybe h(7); UNIT_ASSERT(!!h); Load(&S_, h); UNIT_ASSERT(!h); } { THashMultiMap 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); } } inline void TestSaveLoadEmptyStruct() { TBufferStream S_; TNewNewStyleEmptyHelper h; Save(&S_, h); Load(&S_, h); } void TestList() { TBufferStream s; TList 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; 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 void TestVariantImpl(TVariant& v, const T& expected) { v = expected; TBufferStream s; ::Save(&s, v); ::Load(&s, v); UNIT_ASSERT_VALUES_EQUAL(std::get(v), expected); } void TestVariant() { std::variant> v(1); TestVariantImpl(v, 42); TestVariantImpl(v, true); TestVariantImpl(v, TString("foo")); TestVariantImpl(v, TVector{'b', 'a', 'r'}); v = TString("baz"); TBufferStream s; ::Save(&s, v); std::variant v2 = false; UNIT_ASSERT_EXCEPTION(::Load(&s, v2), TLoadEOF); } template void TestOptionalImpl(const std::optional& v) { std::optional 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(42ull)); TestOptionalImpl(std::optional(true)); TestOptionalImpl(std::optional("abacaba")); TestOptionalImpl(std::optional(std::nullopt)); } // tests serialization of class with three public string members template void TestInheritClassImpl() { TBufferStream s; { TDerived v1; v1.Str1 = "One"; v1.Str2 = "Two"; v1.Str3 = "Three"; ::Save(&s, static_cast(v1)); } { TDerived v2; ::Load(&s, static_cast(v2)); UNIT_ASSERT_VALUES_EQUAL_C(v2.Str1, "One", TypeName() << " via " << TypeName()); UNIT_ASSERT_VALUES_EQUAL_C(v2.Str2, "Two", TypeName() << " via " << TypeName()); UNIT_ASSERT_VALUES_EQUAL_C(v2.Str3, "Three", TypeName() << " via " << TypeName()); } } void TestInheritNonVirtualClass() { struct TBaseNonVirtual { TString Str1; Y_SAVELOAD_DEFINE(Str1); }; struct TDerivedNonVirtual: TBaseNonVirtual { TString Str2; TString Str3; Y_SAVELOAD_DEFINE(TNonVirtualSaver{this}, Str2, Str3); }; TestInheritClassImpl(); } 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{this}, Str2, Str3); }; TestInheritClassImpl(); TestInheritClassImpl(); TestInheritClassImpl(); } }; UNIT_TEST_SUITE_REGISTRATION(TSaveLoadTest);