aboutsummaryrefslogblamecommitdiffstats
path: root/util/generic/maybe_ut.cpp
blob: 2c1a425c5ef8e0b4b5e195cc9fbb00aacd444bc8 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                
                            
                                                  



                           
              
       
                                          
                   

     
                            
                

     
                               
















                                                                            
                              
                         
                     



                                   
                      


                        
                                             










































                                                                     
                                      


                                         
                                     









                                                 
                                     








                                                 







                                          



                                          
 
                              






                              
                                        
                            
                            
     
 
                           









































                                                          




                                                      
     
 
                           









                                                               
 
                            
                                                                                          
         
                                                                                                    


                                                  
                                                                                                         


                                                 
      






    
                                        









                                 
                                           
                          







                                 
                                              
                          







                                 
                                              
                         














                               
                                                        
                         














                               
                                                           
                         














                               
                                             
                      
                 














                               
                                                   
















                               
                                                      
                         















                               
                                                         
                            














                               
                                                                   
                            














                               
                                                                      
                            














                               
                                                        
                         
                          














                               
                                









































                                                                


                                                            


















































                                                                    
                                                               






                                              
                                              






































                                         
                                                    






































                                         
                                              






































                                         
                                                






































                                         
                                           





































                                         
 
                                             
                  



                                              
                                          




                                                     
                                               
                  



                                   
                                              

                                 
             


















                                                                           
                                              

                                          
             










                                                                   
                                            

                                          
             




















                                                               
                                                






                                  
             

                                                              
             



















                                                                                          
                                                      






                                  
             

                                                              
             



































                                                                                 

                                                    
                                       

                                                   

















                                                    



                                                                            
 
#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");
    }
}