#include <util/generic/string.h> #include <util/generic/vector.h> #include <util/stream/str.h> #include <library/cpp/testing/unittest/registar.h> #include "maybe.h" class TIncrementOnDestroy { private: int* Ptr_; public: TIncrementOnDestroy(int* ptr) noexcept : Ptr_(ptr) { } ~TIncrementOnDestroy() { ++*Ptr_; } }; Y_UNIT_TEST_SUITE(TMaybeTest) { Y_UNIT_TEST(TestStatic) { using T1 = TMaybe<int>; static_assert(std::is_trivially_copy_constructible<T1>::value, ""); static_assert(std::is_trivially_destructible<T1>::value, ""); using T2 = TMaybe<TString*>; static_assert(std::is_trivially_copy_constructible<T2>::value, ""); static_assert(std::is_trivially_destructible<T2>::value, ""); using T3 = TMaybe<TMaybe<double>>; static_assert(std::is_trivially_copy_constructible<T3>::value, ""); static_assert(std::is_trivially_destructible<T3>::value, ""); using T4 = TMaybe<TString>; static_assert(!std::is_trivially_copy_constructible<T4>::value, ""); static_assert(!std::is_trivially_destructible<T4>::value, ""); } Y_UNIT_TEST(TestWarning) { TMaybe<size_t> x; TStringStream ss; TString line; while (ss.ReadLine(line)) { x = line.size(); } if (x == 5u) { ss << "5\n"; } } Y_UNIT_TEST(TTestConstructorDestructor) { int a = 0; int b = 0; TMaybe<TIncrementOnDestroy>(); UNIT_ASSERT_VALUES_EQUAL(a, b); TMaybe<TIncrementOnDestroy>(TIncrementOnDestroy(&a)); b += 2; UNIT_ASSERT_VALUES_EQUAL(a, b); { TMaybe<TIncrementOnDestroy> m1 = TIncrementOnDestroy(&a); b += 1; UNIT_ASSERT_VALUES_EQUAL(a, b); TMaybe<TIncrementOnDestroy> m2 = m1; UNIT_ASSERT_VALUES_EQUAL(a, b); TMaybe<TIncrementOnDestroy> m3; m3 = m1; UNIT_ASSERT_VALUES_EQUAL(a, b); } b += 3; UNIT_ASSERT_VALUES_EQUAL(a, b); { TMaybe<TIncrementOnDestroy> m4 = TIncrementOnDestroy(&a); b += 1; UNIT_ASSERT_VALUES_EQUAL(a, b); m4 = TIncrementOnDestroy(&a); b += 1; UNIT_ASSERT_VALUES_EQUAL(a, b); m4.Clear(); b += 1; UNIT_ASSERT_VALUES_EQUAL(a, b); m4.Clear(); UNIT_ASSERT_VALUES_EQUAL(a, b); } } Y_UNIT_TEST(TestAssignmentClear) { TMaybe<int> m5; UNIT_ASSERT(!m5.Defined()); UNIT_ASSERT(m5.Empty()); UNIT_ASSERT(m5 == TMaybe<int>()); UNIT_ASSERT(m5 == Nothing()); UNIT_ASSERT(m5 != TMaybe<int>(4)); m5 = 4; UNIT_ASSERT(m5.Defined()); UNIT_ASSERT(!m5.Empty()); UNIT_ASSERT_VALUES_EQUAL(4, m5.GetRef()); UNIT_ASSERT(m5 == TMaybe<int>(4)); UNIT_ASSERT(m5 != TMaybe<int>(3)); UNIT_ASSERT(m5 != TMaybe<int>()); UNIT_ASSERT(m5 != Nothing()); m5 = TMaybe<int>(5); UNIT_ASSERT(m5.Defined()); UNIT_ASSERT_VALUES_EQUAL(5, m5.GetRef()); UNIT_ASSERT(m5 == TMaybe<int>(5)); UNIT_ASSERT(m5 != TMaybe<int>(4)); m5 = TMaybe<int>(); UNIT_ASSERT(m5.Empty()); UNIT_ASSERT(m5 == TMaybe<int>()); UNIT_ASSERT(m5 == Nothing()); UNIT_ASSERT(m5 != TMaybe<int>(5)); m5 = 4; m5 = Nothing(); UNIT_ASSERT(m5.Empty()); UNIT_ASSERT(m5 == TMaybe<int>()); UNIT_ASSERT(m5 == Nothing()); UNIT_ASSERT(m5 != TMaybe<int>(5)); m5 = {}; UNIT_ASSERT(m5.Empty()); } Y_UNIT_TEST(TestInPlace) { TMaybe<int> m; UNIT_ASSERT(!m); m.ConstructInPlace(1); UNIT_ASSERT(m == 1); auto& x = m.ConstructInPlace(2); UNIT_ASSERT(m == 2); x = 7; UNIT_ASSERT(m == 7); } Y_UNIT_TEST(TestMove) { struct TMovable { int Flag = 0; TMovable(int flag) : Flag(flag) { } TMovable(const TMovable&) = delete; TMovable& operator=(const TMovable&) = delete; TMovable(TMovable&& other) { std::swap(Flag, other.Flag); } TMovable& operator=(TMovable&& other) { std::swap(Flag, other.Flag); return *this; } }; // Move ctor from value TMovable value1(1); TMaybe<TMovable> m1(std::move(value1)); UNIT_ASSERT(m1.Defined()); UNIT_ASSERT_VALUES_EQUAL(m1->Flag, 1); // Move assignment from value TMovable value2(2); TMaybe<TMovable> m2; m2 = std::move(value2); UNIT_ASSERT(m2.Defined()); UNIT_ASSERT_VALUES_EQUAL(m2->Flag, 2); // Move ctor from maybe TMaybe<TMovable> m3(std::move(m1)); UNIT_ASSERT(m3.Defined()); UNIT_ASSERT_VALUES_EQUAL(m3->Flag, 1); // Move assignment from maybe TMaybe<TMovable> m4; m4 = std::move(m2); UNIT_ASSERT(m4.Defined()); UNIT_ASSERT_VALUES_EQUAL(m4->Flag, 2); // Move value from temporary maybe instance TMovable o5 = *MakeMaybe<TMovable>(5); UNIT_ASSERT_VALUES_EQUAL(o5.Flag, 5); TMovable o6 = MakeMaybe<TMovable>(6).GetRef(); UNIT_ASSERT_VALUES_EQUAL(o6.Flag, 6); } Y_UNIT_TEST(TestCast) { // Undefined maybe casts to undefined maybe TMaybe<short> shortMaybe; const auto undefinedMaybe = shortMaybe.Cast<long>(); UNIT_ASSERT(!undefinedMaybe.Defined()); // Defined maybe casts to defined maybe of another type shortMaybe = 34; const auto longMaybe = shortMaybe.Cast<long>(); UNIT_ASSERT(longMaybe.Defined()); UNIT_ASSERT_VALUES_EQUAL(34, longMaybe.GetRef()); } Y_UNIT_TEST(TestGetOr) { UNIT_ASSERT_VALUES_EQUAL(TMaybe<TString>().GetOrElse("xxx"), TString("xxx")); UNIT_ASSERT_VALUES_EQUAL(TMaybe<TString>("yyy").GetOrElse("xxx"), TString("yyy")); { TString xxx = "xxx"; UNIT_ASSERT_VALUES_EQUAL(TMaybe<TString>().GetOrElse(xxx).append('x'), TString("xxxx")); UNIT_ASSERT_VALUES_EQUAL(xxx, "xxxx"); } { TString xxx = "xxx"; UNIT_ASSERT_VALUES_EQUAL(TMaybe<TString>("yyy").GetOrElse(xxx).append('x'), TString("yyyx")); UNIT_ASSERT_VALUES_EQUAL(xxx, "xxx"); } } /* == != < <= > >= */ Y_UNIT_TEST(TestCompareEqualEmpty) { TMaybe<int> m1; TMaybe<int> m2; UNIT_ASSERT(m1 == m2); UNIT_ASSERT(!(m1 != m2)); UNIT_ASSERT(!(m1 < m2)); UNIT_ASSERT(m1 <= m2); UNIT_ASSERT(!(m1 > m2)); UNIT_ASSERT(m1 >= m2); } Y_UNIT_TEST(TestCompareEqualNonEmpty) { TMaybe<int> m1{1}; TMaybe<int> m2{1}; UNIT_ASSERT(m1 == m2); UNIT_ASSERT(!(m1 != m2)); UNIT_ASSERT(!(m1 < m2)); UNIT_ASSERT(m1 <= m2); UNIT_ASSERT(!(m1 > m2)); UNIT_ASSERT(m1 >= m2); } Y_UNIT_TEST(TestCompareOneLessThanOther) { TMaybe<int> m1{1}; TMaybe<int> m2{2}; UNIT_ASSERT(!(m1 == m2)); UNIT_ASSERT(m1 != m2); UNIT_ASSERT(m1 < m2); UNIT_ASSERT(m1 <= m2); UNIT_ASSERT(!(m1 > m2)); UNIT_ASSERT(!(m1 >= m2)); } Y_UNIT_TEST(TestCompareTMaybeAndT_Equal) { TMaybe<int> m{1}; int v{1}; UNIT_ASSERT(m == v); UNIT_ASSERT(!(m != v)); UNIT_ASSERT(!(m < v)); UNIT_ASSERT(m <= v); UNIT_ASSERT(!(m > v)); UNIT_ASSERT(m >= v); UNIT_ASSERT(v == m); UNIT_ASSERT(!(v != m)); UNIT_ASSERT(!(v < m)); UNIT_ASSERT(v <= m); UNIT_ASSERT(!(v > m)); UNIT_ASSERT(v >= m); } Y_UNIT_TEST(TestCompareTMaybeAndT_TMaybeLessThanT) { TMaybe<int> m{1}; int v{2}; UNIT_ASSERT(!(m == v)); UNIT_ASSERT(m != v); UNIT_ASSERT(m < v); UNIT_ASSERT(m <= v); UNIT_ASSERT(!(m > v)); UNIT_ASSERT(!(m >= v)); UNIT_ASSERT(!(v == m)); UNIT_ASSERT(v != m); UNIT_ASSERT(!(v < m)); UNIT_ASSERT(!(v <= m)); UNIT_ASSERT(v > m); UNIT_ASSERT(v >= m); } Y_UNIT_TEST(TestCompareTMaybeAndT_TMaybeGreaterThanT) { TMaybe<int> m{2}; int v{1}; UNIT_ASSERT(!(m == v)); UNIT_ASSERT(m != v); UNIT_ASSERT(!(m < v)); UNIT_ASSERT(!(m <= v)); UNIT_ASSERT(m > v); UNIT_ASSERT(m >= v); UNIT_ASSERT(!(v == m)); UNIT_ASSERT(v != m); UNIT_ASSERT(v < m); UNIT_ASSERT(v <= m); UNIT_ASSERT(!(v > m)); UNIT_ASSERT(!(v >= m)); } Y_UNIT_TEST(TestCompareEmptyTMaybeAndT) { TMaybe<int> m; int v{1}; UNIT_ASSERT(!(m == v)); UNIT_ASSERT(m != v); UNIT_ASSERT(m < v); UNIT_ASSERT(m <= v); UNIT_ASSERT(!(m > v)); UNIT_ASSERT(!(m >= v)); UNIT_ASSERT(!(v == m)); UNIT_ASSERT(v != m); UNIT_ASSERT(!(v < m)); UNIT_ASSERT(!(v <= m)); UNIT_ASSERT(v > m); UNIT_ASSERT(v >= m); } Y_UNIT_TEST(TestCompareEmptyTMaybeAndNothing) { TMaybe<int> m; auto n = Nothing(); UNIT_ASSERT(m == n); UNIT_ASSERT(!(m != n)); UNIT_ASSERT(!(m < n)); UNIT_ASSERT(m <= n); UNIT_ASSERT(!(m > n)); UNIT_ASSERT(m >= n); UNIT_ASSERT(n == m); UNIT_ASSERT(!(n != m)); UNIT_ASSERT(!(n < m)); UNIT_ASSERT(n <= m); UNIT_ASSERT(!(n > m)); UNIT_ASSERT(n >= m); } Y_UNIT_TEST(TestCompareNonEmptyTMaybeAndNothing) { TMaybe<int> m{1}; auto n = Nothing(); UNIT_ASSERT(!(m == n)); UNIT_ASSERT(m != n); UNIT_ASSERT(!(m < n)); UNIT_ASSERT(!(m <= n)); UNIT_ASSERT(m > n); UNIT_ASSERT(m >= n); UNIT_ASSERT(!(n == m)); UNIT_ASSERT(n != m); UNIT_ASSERT(n < m); UNIT_ASSERT(n <= m); UNIT_ASSERT(!(n > m)); UNIT_ASSERT(!(n >= m)); } Y_UNIT_TEST(TestCompareTMaybeAndConvertibleT_Equal) { TMaybe<size_t> m{1}; unsigned int v{1}; UNIT_ASSERT(m == v); UNIT_ASSERT(!(m != v)); UNIT_ASSERT(!(m < v)); UNIT_ASSERT(m <= v); UNIT_ASSERT(!(m > v)); UNIT_ASSERT(m >= v); UNIT_ASSERT(v == m); UNIT_ASSERT(!(v != m)); UNIT_ASSERT(!(v < m)); UNIT_ASSERT(v <= m); UNIT_ASSERT(!(v > m)); UNIT_ASSERT(v >= m); } Y_UNIT_TEST(TestCompareTMaybeAndConvertibleT_TMaybeLessThanT) { TMaybe<size_t> m{1}; unsigned int v{2}; UNIT_ASSERT(!(m == v)); UNIT_ASSERT(m != v); UNIT_ASSERT(m < v); UNIT_ASSERT(m <= v); UNIT_ASSERT(!(m > v)); UNIT_ASSERT(!(m >= v)); UNIT_ASSERT(!(v == m)); UNIT_ASSERT(v != m); UNIT_ASSERT(!(v < m)); UNIT_ASSERT(!(v <= m)); UNIT_ASSERT(v > m); UNIT_ASSERT(v >= m); } Y_UNIT_TEST(TestCompareTMaybeAndConvertibleT_TMaybeGreaterThanT) { TMaybe<size_t> m{2}; unsigned int v{1}; UNIT_ASSERT(!(m == v)); UNIT_ASSERT(m != v); UNIT_ASSERT(!(m < v)); UNIT_ASSERT(!(m <= v)); UNIT_ASSERT(m > v); UNIT_ASSERT(m >= v); UNIT_ASSERT(!(v == m)); UNIT_ASSERT(v != m); UNIT_ASSERT(v < m); UNIT_ASSERT(v <= m); UNIT_ASSERT(!(v > m)); UNIT_ASSERT(!(v >= m)); } Y_UNIT_TEST(TestCompareEmptyTMaybeAndConvertibleT) { TMaybe<size_t> m; unsigned int v{1}; UNIT_ASSERT(!(m == v)); UNIT_ASSERT(m != v); UNIT_ASSERT(m < v); UNIT_ASSERT(m <= v); UNIT_ASSERT(!(m > v)); UNIT_ASSERT(!(m >= v)); UNIT_ASSERT(!(v == m)); UNIT_ASSERT(v != m); UNIT_ASSERT(!(v < m)); UNIT_ASSERT(!(v <= m)); UNIT_ASSERT(v > m); UNIT_ASSERT(v >= m); } Y_UNIT_TEST(TestMakeMaybe) { { auto m1 = MakeMaybe<int>(1); UNIT_ASSERT(*m1 == 1); } { struct TMockClass { TMockClass(int i) : I_(i) { } TMockClass(const TMockClass& other) : I_(other.I_) { IsCopyConstructorCalled_ = true; } TMockClass& operator=(const TMockClass& other) { if (this != &other) { I_ = other.I_; IsCopyAssignmentOperatorCalled_ = true; } return *this; } TMockClass(TMockClass&& other) : I_(other.I_) { IsMoveConstructorCalled_ = true; } TMockClass& operator=(TMockClass&& other) { if (this != &other) { I_ = other.I_; IsMoveAssignmentOperatorCalled_ = true; } return *this; } int I_; bool IsCopyConstructorCalled_{false}; bool IsMoveConstructorCalled_{false}; bool IsCopyAssignmentOperatorCalled_{false}; bool IsMoveAssignmentOperatorCalled_{false}; }; auto m2 = MakeMaybe<TMockClass>(1); UNIT_ASSERT(m2->I_ == 1); UNIT_ASSERT(!m2->IsCopyConstructorCalled_); UNIT_ASSERT(!m2->IsMoveConstructorCalled_); UNIT_ASSERT(!m2->IsCopyAssignmentOperatorCalled_); UNIT_ASSERT(!m2->IsMoveAssignmentOperatorCalled_); } { auto m3 = MakeMaybe<TVector<int>>({1, 2, 3, 4, 5}); UNIT_ASSERT(m3->size() == 5); UNIT_ASSERT(m3->at(0) == 1); UNIT_ASSERT(m3->at(1) == 2); UNIT_ASSERT(m3->at(2) == 3); UNIT_ASSERT(m3->at(3) == 4); UNIT_ASSERT(m3->at(4) == 5); } { struct TMockStruct4 { TMockStruct4(int a, int b, int c) : A_(a) , B_(b) , C_(c) { } int A_; int B_; int C_; }; auto m4 = MakeMaybe<TMockStruct4>(1, 2, 3); UNIT_ASSERT(m4->A_ == 1); UNIT_ASSERT(m4->B_ == 2); UNIT_ASSERT(m4->C_ == 3); } { struct TMockStruct5 { TMockStruct5(const TVector<int>& vec, bool someFlag) : Vec_(vec) , SomeFlag_(someFlag) { } TVector<int> Vec_; bool SomeFlag_; }; auto m5 = MakeMaybe<TMockStruct5>({1, 2, 3}, true); UNIT_ASSERT(m5->Vec_.size() == 3); UNIT_ASSERT(m5->Vec_[0] == 1); UNIT_ASSERT(m5->Vec_[1] == 2); UNIT_ASSERT(m5->Vec_[2] == 3); UNIT_ASSERT(m5->SomeFlag_); } } Y_UNIT_TEST(TestSwappingUsingMemberSwap) { { TMaybe<int> m1 = 1; TMaybe<int> m2 = 2; UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(*m2 == 2); m1.Swap(m2); UNIT_ASSERT(*m1 == 2); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = 1; TMaybe<int> m2 = Nothing(); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); m1.Swap(m2); UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = Nothing(); TMaybe<int> m2 = 1; UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); m1.Swap(m2); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); } } Y_UNIT_TEST(TestSwappingUsingMemberLittleSwap) { { TMaybe<int> m1 = 1; TMaybe<int> m2 = 2; UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(*m2 == 2); m1.swap(m2); UNIT_ASSERT(*m1 == 2); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = 1; TMaybe<int> m2 = Nothing(); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); m1.swap(m2); UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = Nothing(); TMaybe<int> m2 = 1; UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); m1.swap(m2); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); } } Y_UNIT_TEST(TestSwappingUsingGlobalSwap) { { TMaybe<int> m1 = 1; TMaybe<int> m2 = 2; UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(*m2 == 2); ::Swap(m1, m2); UNIT_ASSERT(*m1 == 2); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = 1; TMaybe<int> m2 = Nothing(); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); ::Swap(m1, m2); UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = Nothing(); TMaybe<int> m2 = 1; UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); ::Swap(m1, m2); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); } } Y_UNIT_TEST(TestSwappingUsingGlobalDoSwap) { { TMaybe<int> m1 = 1; TMaybe<int> m2 = 2; UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(*m2 == 2); ::DoSwap(m1, m2); UNIT_ASSERT(*m1 == 2); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = 1; TMaybe<int> m2 = Nothing(); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); ::DoSwap(m1, m2); UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = Nothing(); TMaybe<int> m2 = 1; UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); ::DoSwap(m1, m2); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); } } Y_UNIT_TEST(TestSwappingUsingStdSwap) { { TMaybe<int> m1 = 1; TMaybe<int> m2 = 2; UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(*m2 == 2); ::std::swap(m1, m2); UNIT_ASSERT(*m1 == 2); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = 1; TMaybe<int> m2 = Nothing(); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); ::std::swap(m1, m2); UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); } { TMaybe<int> m1 = Nothing(); TMaybe<int> m2 = 1; UNIT_ASSERT(m1 == Nothing()); UNIT_ASSERT(*m2 == 1); ::std::swap(m1, m2); UNIT_ASSERT(*m1 == 1); UNIT_ASSERT(m2 == Nothing()); } } Y_UNIT_TEST(TestOutputStreamEmptyMaybe) { TString s; TStringOutput output(s); output << TMaybe<int>(); UNIT_ASSERT_EQUAL("(empty maybe)", s); } Y_UNIT_TEST(TestOutputStreamNothing) { TString s; TStringOutput output(s); output << Nothing(); UNIT_ASSERT_VALUES_EQUAL("(empty maybe)", s); } Y_UNIT_TEST(TestOutputStreamDefinedMaybe) { TString s; TStringOutput output(s); output << TMaybe<int>(42); UNIT_ASSERT_EQUAL("42", s); } Y_UNIT_TEST(TestMaybeCovarianceImplicit) { struct TestStruct { TestStruct(int value) : Value_(value) { } operator int() const { return Value_; } static TMaybe<int> Unwrap(TMaybe<TestStruct> testStructMaybe) { return testStructMaybe; } int Value_; }; TMaybe<int> testMaybeFull = TestStruct::Unwrap(TMaybe<int>(42)); UNIT_ASSERT(testMaybeFull.Defined()); UNIT_ASSERT_EQUAL(testMaybeFull.GetRef(), 42); TMaybe<int> testMaybeEmpty = TestStruct::Unwrap(TMaybe<int>()); UNIT_ASSERT(!testMaybeEmpty.Defined()); } Y_UNIT_TEST(TestMaybeCovarianceExplicit) { struct TestStruct { explicit TestStruct(int value) : Value_(value) { } int Value_; }; TMaybe<TestStruct> testStructMaybeFull(TMaybe<int>(42)); UNIT_ASSERT(testStructMaybeFull.Defined()); UNIT_ASSERT_EQUAL(testStructMaybeFull.GetRef().Value_, 42); TMaybe<int> empty; TMaybe<TestStruct> testStructMaybeEmpty(empty); UNIT_ASSERT(!testStructMaybeEmpty.Defined()); } Y_UNIT_TEST(TestMaybeCovarianceAssign) { struct TestStruct { explicit TestStruct(int value) : Value_(value) { } TestStruct& operator=(int value) { Value_ = value; return *this; } int Value_; }; TMaybe<TestStruct> testStructMaybe(Nothing()); UNIT_ASSERT(!testStructMaybe.Defined()); testStructMaybe = TMaybe<int>(42); UNIT_ASSERT(testStructMaybe.Defined()); UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 42); testStructMaybe = TMaybe<int>(23); UNIT_ASSERT(testStructMaybe.Defined()); UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 23); testStructMaybe = TMaybe<int>(); UNIT_ASSERT(!testStructMaybe.Defined()); } Y_UNIT_TEST(TestMaybeCovarianceNonTrivial) { struct TestStruct { enum { FromValue, FromMaybe, }; TestStruct(int value) : Value_(value) , From_(FromValue) { } TestStruct(TMaybe<int> value) : Value_(value.Defined() ? value.GetRef() : 0) , From_(FromMaybe) { } int Value_; int From_; }; TMaybe<TestStruct> testStructFromMaybe(TMaybe<int>(42)); UNIT_ASSERT(testStructFromMaybe.Defined()); UNIT_ASSERT_EQUAL(testStructFromMaybe.GetRef().From_, TestStruct::FromMaybe); UNIT_ASSERT_EQUAL(testStructFromMaybe.GetRef().Value_, 42); TMaybe<int> empty; TMaybe<TestStruct> testStructFromEmptyMaybe(empty); UNIT_ASSERT(testStructFromEmptyMaybe.Defined()); UNIT_ASSERT_EQUAL(testStructFromEmptyMaybe.GetRef().From_, TestStruct::FromMaybe); UNIT_ASSERT_EQUAL(testStructFromEmptyMaybe.GetRef().Value_, 0); TMaybe<TestStruct> testStructFromValue(23); UNIT_ASSERT(testStructFromValue.Defined()); UNIT_ASSERT_EQUAL(testStructFromValue.GetRef().From_, TestStruct::FromValue); UNIT_ASSERT_EQUAL(testStructFromValue.GetRef().Value_, 23); } Y_UNIT_TEST(TestMaybeCovarianceNonTrivialAssign) { struct TestStruct { enum { FromValue, FromMaybe, }; TestStruct(int value) : Value_(value) , From_(FromValue) { } TestStruct(TMaybe<int> value) : Value_(value.Defined() ? value.GetRef() : 0) , From_(FromMaybe) { } TestStruct& operator=(int value) { Value_ = value; From_ = FromValue; return *this; } TestStruct& operator=(TMaybe<int> value) { Value_ = value.Defined() ? value.GetRef() : 0; From_ = FromMaybe; return *this; } int Value_; int From_; }; TMaybe<TestStruct> testStructMaybe(Nothing()); testStructMaybe = TMaybe<int>(42); UNIT_ASSERT(testStructMaybe.Defined()); UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().From_, TestStruct::FromMaybe); UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 42); testStructMaybe = TMaybe<int>(); UNIT_ASSERT(testStructMaybe.Defined()); UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().From_, TestStruct::FromMaybe); UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 0); testStructMaybe = 23; UNIT_ASSERT(testStructMaybe.Defined()); UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().From_, TestStruct::FromValue); UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 23); } Y_UNIT_TEST(TestMaybeConvertion) { struct TSrc {}; struct TDst { bool FromMaybeConstructorApplied; explicit TDst(TSrc) : FromMaybeConstructorApplied(false) { } explicit TDst(TMaybe<TSrc>) : FromMaybeConstructorApplied(true) { } TDst& operator=(TSrc) { FromMaybeConstructorApplied = false; return *this; } TDst& operator=(TMaybe<TSrc>) { FromMaybeConstructorApplied = true; return *this; } }; auto m = TMaybe<TDst>(TMaybe<TSrc>()); UNIT_ASSERT(m.Defined()); UNIT_ASSERT(m->FromMaybeConstructorApplied); m = TMaybe<TSrc>(); UNIT_ASSERT(m.Defined()); UNIT_ASSERT(m->FromMaybeConstructorApplied); } Y_UNIT_TEST(TestOnEmptyException) { TMaybe<TStringBuf> v; UNIT_ASSERT_EXCEPTION_CONTAINS(v.GetRef(), yexception, "StringBuf"); } }