// Originally taken from // https://raw.githubusercontent.com/cryfs/cryfs/14ad22570ddacef22d5ff139cdff68a54fc8234d/test/cpp-utils/either_test.cpp #include #include #include #include #include #include // NOLINTBEGIN(*) using c10::either; using c10::make_left; using c10::make_right; using std::ostringstream; using std::pair; using std::string; using std::tuple; using std::vector; namespace { class MovableOnly final { public: explicit MovableOnly(int value) : _value(value) {} MovableOnly(const MovableOnly&) = delete; MovableOnly& operator=(const MovableOnly&) = delete; MovableOnly(MovableOnly&& rhs) : _value(rhs._value) { rhs._value = 0; } MovableOnly& operator=(MovableOnly&& rhs) { _value = rhs._value; rhs._value = 0; return *this; } int value() const { return _value; } private: int _value; }; bool operator==(const MovableOnly& lhs, const MovableOnly& rhs) { return lhs.value() == rhs.value(); } template void test_with_matrix( std::vector)>> setups, std::vector> expectations) { for (const auto& setup : setups) { for (const auto& expectation : expectations) { setup(expectation); } } } template std::vector&)>> EXPECT_IS_LEFT( const Left& expected) { return { [&](either& obj) { EXPECT_TRUE(obj.is_left()); }, [&](either& obj) { EXPECT_FALSE(obj.is_right()); }, [&](either& obj) { EXPECT_EQ(expected, obj.left()); }, [&](either& obj) { EXPECT_EQ(expected, std::move(obj).left()); }, [&](either& obj) { // NOLINTNEXTLINE(hicpp-avoid-goto,cppcoreguidelines-avoid-goto) EXPECT_ANY_THROW(obj.right()); }, [&](either& obj) { // NOLINTNEXTLINE(hicpp-avoid-goto,cppcoreguidelines-avoid-goto) EXPECT_ANY_THROW(std::move(obj).right()); }}; } template std::vector&)>> EXPECT_IS_RIGHT( const Right& expected) { return { [&](either& obj) { EXPECT_FALSE(obj.is_left()); }, [&](either& obj) { EXPECT_TRUE(obj.is_right()); }, [&](either& obj) { EXPECT_EQ(expected, obj.right()); }, [&](either& obj) { EXPECT_EQ(expected, std::move(obj).right()); }, [&](either& obj) { // NOLINTNEXTLINE(hicpp-avoid-goto,cppcoreguidelines-avoid-goto) EXPECT_ANY_THROW(obj.left()); }, [&](either& obj) { // NOLINTNEXTLINE(hicpp-avoid-goto,cppcoreguidelines-avoid-goto) EXPECT_ANY_THROW(std::move(obj).left()); }}; } template std::vector> EXPECT_IS(const Value& v) { return {[&](Value& obj) { return obj == v; }}; } template struct StoreWith1ByteFlag { T val; char flag; }; template void TestSpaceUsage() { EXPECT_EQ( std::max( sizeof(StoreWith1ByteFlag), sizeof(StoreWith1ByteFlag)), sizeof(either)); } } // namespace TEST(EitherTest, SpaceUsage) { TestSpaceUsage(); TestSpaceUsage(); TestSpaceUsage(); TestSpaceUsage(); TestSpaceUsage>(); } TEST(EitherTest, givenLeft) { test_with_matrix( { [](std::function&)> test) { either a(4); test(a); }, [](std::function&)> test) { either a = 4; test(a); }, }, EXPECT_IS_LEFT(4)); } TEST(EitherTest, givenRight) { test_with_matrix( {[](std::function&)> test) { either a("4"); test(a); }, [](std::function&)> test) { either a = string("4"); test(a); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenMakeLeft) { test_with_matrix( { [](std::function&)> test) { either a = make_left(4); test(a); }, [](std::function&)> test) { auto a = make_left(4); test(a); }, }, EXPECT_IS_LEFT(4)); } TEST(EitherTest, givenMakeLeftWithSameType) { test_with_matrix( { [](std::function&)> test) { either a = make_left(4); test(a); }, [](std::function&)> test) { auto a = make_left(4); test(a); }, }, EXPECT_IS_LEFT(4)); } TEST(EitherTest, givenMakeRight) { test_with_matrix( {[](std::function&)> test) { either a = make_right("4"); test(a); }, [](std::function&)> test) { auto a = make_right("4"); test(a); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenMakeRightWithSameType) { test_with_matrix( {[](std::function&)> test) { either a = make_right("4"); test(a); }, [](std::function&)> test) { auto a = make_right("4"); test(a); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenMovableOnlyMakeLeft) { test_with_matrix( { [](std::function&)> test) { either a = make_left(3); test(a); }, [](std::function&)> test) { auto a = make_left(3); test(a); }, }, EXPECT_IS_LEFT(MovableOnly(3))); } TEST(EitherTest, givenMovableOnlyMakeRight) { test_with_matrix( {[](std::function&)> test) { either a = make_right(3); test(a); }, [](std::function&)> test) { auto a = make_right(3); test(a); }}, EXPECT_IS_RIGHT(MovableOnly(3))); } TEST(EitherTest, givenMultiParamMakeLeft) { test_with_matrix( { [](std::function, string>&)> test) { either, string> a = make_left, string>(5, 6); test(a); }, [](std::function, string>&)> test) { auto a = make_left, string>(5, 6); test(a); }, }, EXPECT_IS_LEFT, string>(pair(5, 6))); } TEST(EitherTest, givenMultiParamMakeRight) { test_with_matrix( {[](std::function>&)> test) { either> a = make_right>(5, 6); test(a); }, [](std::function>&)> test) { auto a = make_right>(5, 6); test(a); }}, EXPECT_IS_RIGHT>(pair(5, 6))); } TEST(EitherTest, givenLeftCopyConstructedFromValue_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { string a = "4"; either b(a); test(b); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenLeftCopyConstructedFromValue_thenOldIsCorrect) { test_with_matrix( {[](std::function test) { string a = "4"; either b(a); test(a); }}, EXPECT_IS("4")); } TEST(EitherTest, givenRightCopyConstructedFromValue_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { string a = "4"; either b(a); test(b); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenRightCopyConstructedFromValue_thenOldIsCorrect) { test_with_matrix( {[](std::function test) { string a = "4"; either b(a); test(a); }}, EXPECT_IS("4")); } TEST(EitherTest, givenLeftMoveConstructedFromValue_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { MovableOnly a(3); either b(std::move(a)); test(b); }}, EXPECT_IS_LEFT(MovableOnly(3))); } TEST(EitherTest, givenLeftMoveConstructedFromValue_thenOldIsCorrect) { test_with_matrix( {[](std::function test) { MovableOnly a(3); either b(std::move(a)); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS(MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenRightMoveConstructedFromValue_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { MovableOnly a(3); either b(std::move(a)); test(b); }}, EXPECT_IS_RIGHT(MovableOnly(3))); } TEST(EitherTest, givenRightMoveConstructedFromValue_thenOldIsCorrect) { test_with_matrix( {[](std::function test) { MovableOnly a(3); either b(std::move(a)); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS(MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenLeftCopyAssignedFromValue_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { string a = "4"; either b(2); b = a; test(b); }, [](std::function&)> test) { string a = "4"; either b("2"); b = a; test(b); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenLeftCopyAssignedFromValue_thenOldIsCorrect) { test_with_matrix( {[](std::function test) { string a = "4"; either b(2); b = a; test(a); }, [](std::function test) { string a = "4"; either b("2"); b = a; test(a); }}, EXPECT_IS("4")); } TEST(EitherTest, givenRightCopyAssignedFromValue_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { string a = "4"; either b(2); b = a; test(b); }, [](std::function&)> test) { string a = "4"; either b("2"); b = a; test(b); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenRightCopyAssignedFromValue_thenOldIsCorrect) { test_with_matrix( {[](std::function test) { string a = "4"; either b(2); b = a; test(a); }, [](std::function test) { string a = "4"; either b("2"); b = a; test(a); }}, EXPECT_IS("4")); } TEST(EitherTest, givenLeftMoveAssignedFromValue_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { MovableOnly a(3); either b(2); b = std::move(a); test(b); }, [](std::function&)> test) { MovableOnly a(3); either b(MovableOnly(2)); b = std::move(a); test(b); }}, EXPECT_IS_LEFT(MovableOnly(3))); } TEST(EitherTest, givenLeftMoveAssignedFromValue_thenOldIsCorrect) { test_with_matrix( {[](std::function test) { MovableOnly a(3); either b("2"); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }, [](std::function test) { MovableOnly a(3); either b(MovableOnly(0)); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS(MovableOnly(0))); } TEST(EitherTest, givenRightMoveAssignedFromValue_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { MovableOnly a(3); either b("2"); b = std::move(a); test(b); }, [](std::function&)> test) { MovableOnly a(3); either b(MovableOnly(2)); b = std::move(a); test(b); }}, EXPECT_IS_RIGHT(MovableOnly(3))); } TEST(EitherTest, givenRightMoveAssignedFromValue_thenOldIsCorrect) { test_with_matrix( {[](std::function test) { MovableOnly a(3); either b("2"); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }, [](std::function test) { MovableOnly a(3); either b(MovableOnly(2)); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS(MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenLeftCopyConstructed_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a("4"); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either b(a); test(b); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenLeftCopyConstructed_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a("4"); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either b(a); test(a); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenLeftCopyConstructed_withSameType_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_left("4"); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either b(a); test(b); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenLeftCopyConstructed_withSameType_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_left("4"); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either b(a); test(a); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenRightCopyConstructed_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a("4"); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either b(a); test(b); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenRightCopyConstructed_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a("4"); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either b(a); test(a); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenRightCopyConstructed_withSameType_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_right("4"); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either b(a); test(b); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenRightCopyConstructed_withSameType_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_right("4"); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either b(a); test(a); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenLeftMoveConstructed_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a(MovableOnly(3)); either b(std::move(a)); test(b); }}, EXPECT_IS_LEFT(MovableOnly(3))); } TEST(EitherTest, givenLeftMoveConstructed_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a(MovableOnly(3)); either b(std::move(a)); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS_LEFT(MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenLeftMoveConstructed_withSameType_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_left(MovableOnly(3)); either b(std::move(a)); test(b); }}, EXPECT_IS_LEFT(MovableOnly(3))); } TEST(EitherTest, givenLeftMoveConstructed_withSameType_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_left(MovableOnly(3)); either b(std::move(a)); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS_LEFT( MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenRightMoveConstructed_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a(MovableOnly(3)); either b(std::move(a)); test(b); }}, EXPECT_IS_RIGHT(MovableOnly(3))); } TEST(EitherTest, givenRightMoveConstructed_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a(MovableOnly(3)); either b(std::move(a)); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS_RIGHT(MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenRightMoveConstructed_withSameType_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_right(MovableOnly(3)); either b(std::move(a)); test(b); }}, EXPECT_IS_RIGHT(MovableOnly(3))); } TEST(EitherTest, givenRightMoveConstructed_withSameType_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_right(MovableOnly(3)); either b(std::move(a)); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS_RIGHT( MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenLeftCopyAssigned_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a("4"); either b(2); b = a; test(b); }, [](std::function&)> test) { either a("4"); either b("2"); b = a; test(b); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenLeftCopyAssigned_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a("4"); either b(2); b = a; test(a); }, [](std::function&)> test) { either a("4"); either b("2"); b = a; test(a); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenLeftCopyAssigned_withSameType_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_left("4"); either b = make_right("2"); b = a; test(b); }, [](std::function&)> test) { either a = make_left("4"); either b = make_left("2"); b = a; test(b); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenLeftCopyAssigned_withSameType_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_left("4"); either b = make_right("2"); b = a; test(a); }, [](std::function&)> test) { either a = make_left("4"); either b = make_left("2"); b = a; test(a); }}, EXPECT_IS_LEFT("4")); } TEST(EitherTest, givenRightCopyAssigned_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a("4"); either b(2); b = a; test(b); }, [](std::function&)> test) { either a("4"); either b("2"); b = a; test(b); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenRightCopyAssigned_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a("4"); either b(2); b = a; test(a); }, [](std::function&)> test) { either a("4"); either b("2"); b = a; test(a); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenRightCopyAssigned_withSameType_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_right("4"); either b = make_left("2"); b = a; test(b); }, [](std::function&)> test) { either a = make_right("4"); either b = make_right("2"); b = a; test(b); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenRightCopyAssigned_withSameType_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_right("4"); either b = make_left("2"); b = a; test(a); }, [](std::function&)> test) { either a = make_right("4"); either b = make_right("2"); b = a; test(a); }}, EXPECT_IS_RIGHT("4")); } TEST(EitherTest, givenLeftMoveAssigned_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a(MovableOnly(3)); either b(2); b = std::move(a); test(b); }, [](std::function&)> test) { either a(MovableOnly(3)); either b(MovableOnly(2)); b = std::move(a); test(b); }}, EXPECT_IS_LEFT(MovableOnly(3))); } TEST(EitherTest, givenLeftMoveAssigned_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a(MovableOnly(3)); either b(2); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }, [](std::function&)> test) { either a(MovableOnly(3)); either b(MovableOnly(2)); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS_LEFT( MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenLeftMoveAssigned_withSameType_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_left(3); either b = make_right(2); b = std::move(a); test(b); }, [](std::function&)> test) { either a = make_left(3); either b = make_left(2); b = std::move(a); test(b); }}, EXPECT_IS_LEFT(MovableOnly(3))); } TEST(EitherTest, givenLeftMoveAssigned_withSameType_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_left(3); either b = make_right(2); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }, [](std::function&)> test) { either a = make_left(3); either b = make_left(2); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS_LEFT( MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenRightMoveAssigned_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a(MovableOnly(3)); either b("2"); b = std::move(a); test(b); }, [](std::function&)> test) { either a(MovableOnly(3)); either b(MovableOnly(2)); b = std::move(a); test(b); }}, EXPECT_IS_RIGHT(MovableOnly(3))); } TEST(EitherTest, givenRightMoveAssigned_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a(MovableOnly(3)); either b("2"); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }, [](std::function&)> test) { either a(MovableOnly(3)); either b(MovableOnly(2)); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS_RIGHT( MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenRightMoveAssigned_withSameType_thenNewIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_right(3); either b = make_left(2); b = std::move(a); test(b); }, [](std::function&)> test) { either a = make_right(3); either b = make_right(2); b = std::move(a); test(b); }}, EXPECT_IS_RIGHT(MovableOnly(3))); } TEST(EitherTest, givenRightMoveAssigned_withSameType_thenOldIsCorrect) { test_with_matrix( {[](std::function&)> test) { either a = make_right(3); either b = make_left(2); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }, [](std::function&)> test) { either a = make_right(3); either b = make_right(2); b = std::move(a); test(a); // NOLINT(bugprone-use-after-move) }}, EXPECT_IS_RIGHT( MovableOnly(0)) // 0 is moved-from value ); } TEST(EitherTest, givenLeft_whenModified_thenValueIsChanged) { test_with_matrix( {[](std::function&)> test) { either a(4); a.left() = 5; test(a); }, [](std::function&)> test) { either a(4); a.left() = 5; test(a); }}, EXPECT_IS_LEFT(5)); } TEST(EitherTest, givenRight_whenModified_thenValueIsChanged) { test_with_matrix( {[](std::function&)> test) { either a("4"); a.right() = "5"; test(a); }, [](std::function&)> test) { either a("4"); a.right() = "5"; test(a); }}, EXPECT_IS_RIGHT("5")); } TEST(EitherTest, canEmplaceConstructLeft) { test_with_matrix( {[](std::function, tuple>&)> test) { either, tuple> a(2, 3); test(a); }}, EXPECT_IS_LEFT, tuple>( tuple(2, 3))); } TEST(EitherTest, canEmplaceConstructRight) { test_with_matrix( {[](std::function, tuple>&)> test) { either, tuple> a(2, "3", 4); test(a); }}, EXPECT_IS_RIGHT, tuple>( tuple(2, "3", 4))); } TEST(EitherTest, givenEqualLefts_thenAreEqual) { either a("3"); either b("3"); EXPECT_TRUE(a == b); } TEST(EitherTest, givenEqualLefts_thenAreNotUnequal) { either a("3"); either b("3"); EXPECT_FALSE(a != b); } TEST(EitherTest, givenEqualRights_thenAreEqual) { either a(3); either b(3); EXPECT_TRUE(a == b); } TEST(EitherTest, givenEqualRights_thenAreNotUnequal) { either a(3); either b(3); EXPECT_FALSE(a != b); } TEST(EitherTest, givenLeftAndRight_thenAreNotEqual) { either a("3"); either b(3); EXPECT_FALSE(a == b); EXPECT_FALSE(b == a); } TEST(EitherTest, givenLeftAndRight_thenAreUnequal) { either a("3"); either b(3); EXPECT_TRUE(a != b); EXPECT_TRUE(b != a); } TEST(EitherTest, OutputLeft) { ostringstream str; str << either("mystring"); EXPECT_EQ("Left(mystring)", str.str()); } TEST(EitherTest, OutputRight) { ostringstream str; str << either("mystring"); EXPECT_EQ("Right(mystring)", str.str()); } TEST(EitherTest, givenLeftAndRightWithSameType_thenAreNotEqual) { either a = make_left("3"); either b = make_right("3"); EXPECT_FALSE(a == b); EXPECT_FALSE(b == a); } TEST(EitherTest, givenLeftAndRightWithSameType_thenAreUnequal) { either a = make_left("3"); either b = make_right("3"); EXPECT_TRUE(a != b); EXPECT_TRUE(b != a); } namespace { class DestructorCallback { public: MOCK_CONST_METHOD0(call, void()); void EXPECT_CALLED(int times = 1) { EXPECT_CALL(*this, call()).Times(times); } }; class ClassWithDestructorCallback { public: ClassWithDestructorCallback(const DestructorCallback* destructorCallback) : _destructorCallback(destructorCallback) {} ~ClassWithDestructorCallback() { _destructorCallback->call(); } ClassWithDestructorCallback& operator=( const ClassWithDestructorCallback& rhs) = delete; private: const DestructorCallback* _destructorCallback; }; class OnlyMoveableClassWithDestructorCallback { public: OnlyMoveableClassWithDestructorCallback( const DestructorCallback* destructorCallback) : _destructorCallback(destructorCallback) {} OnlyMoveableClassWithDestructorCallback( OnlyMoveableClassWithDestructorCallback&& source) : _destructorCallback(source._destructorCallback) {} ~OnlyMoveableClassWithDestructorCallback() { _destructorCallback->call(); } private: C10_DISABLE_COPY_AND_ASSIGN(OnlyMoveableClassWithDestructorCallback); const DestructorCallback* _destructorCallback; }; } // namespace TEST(EitherTestDestructor, LeftDestructorIsCalled) { DestructorCallback destructorCallback; destructorCallback.EXPECT_CALLED( 2); // Once for the temp object, once when the either class destructs ClassWithDestructorCallback temp(&destructorCallback); either var = temp; } TEST(EitherTestDestructor, RightDestructorIsCalled) { DestructorCallback destructorCallback; destructorCallback.EXPECT_CALLED( 2); // Once for the temp object, once when the either class destructs ClassWithDestructorCallback temp(&destructorCallback); either var = temp; } TEST(EitherTestDestructor, LeftDestructorIsCalledAfterCopying) { DestructorCallback destructorCallback; destructorCallback.EXPECT_CALLED( 3); // Once for the temp object, once for var1 and once for var2 ClassWithDestructorCallback temp(&destructorCallback); either var1 = temp; // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either var2 = var1; } TEST(EitherTestDestructor, RightDestructorIsCalledAfterCopying) { DestructorCallback destructorCallback; destructorCallback.EXPECT_CALLED( 3); // Once for the temp object, once for var1 and once for var2 ClassWithDestructorCallback temp(&destructorCallback); either var1 = temp; // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) either var2 = var1; } TEST(EitherTestDestructor, LeftDestructorIsCalledAfterMoving) { DestructorCallback destructorCallback; destructorCallback.EXPECT_CALLED( 3); // Once for the temp object, once for var1 and once for var2 OnlyMoveableClassWithDestructorCallback temp(&destructorCallback); either var1 = std::move(temp); either var2 = std::move(var1); } TEST(EitherTestDestructor, RightDestructorIsCalledAfterMoving) { DestructorCallback destructorCallback; destructorCallback.EXPECT_CALLED( 3); // Once for the temp object, once for var1 and once for var2 OnlyMoveableClassWithDestructorCallback temp(&destructorCallback); either var1 = std::move(temp); either var2 = std::move(var1); } TEST(EitherTestDestructor, LeftDestructorIsCalledAfterAssignment) { DestructorCallback destructorCallback1; DestructorCallback destructorCallback2; destructorCallback1.EXPECT_CALLED( 2); // Once for the temp1 object, once at the assignment destructorCallback2.EXPECT_CALLED( 3); // Once for the temp2 object, once in destructor of var2, once in // destructor of var1 ClassWithDestructorCallback temp1(&destructorCallback1); either var1 = temp1; ClassWithDestructorCallback temp2(&destructorCallback2); either var2 = temp2; var1 = var2; } TEST(EitherTestDestructor, RightDestructorIsCalledAfterAssignment) { DestructorCallback destructorCallback1; DestructorCallback destructorCallback2; destructorCallback1.EXPECT_CALLED( 2); // Once for the temp1 object, once at the assignment destructorCallback2.EXPECT_CALLED( 3); // Once for the temp2 object, once in destructor of var2, once in // destructor of var1 ClassWithDestructorCallback temp1(&destructorCallback1); either var1 = temp1; ClassWithDestructorCallback temp2(&destructorCallback2); either var2 = temp2; var1 = var2; } TEST(EitherTestDestructor, LeftDestructorIsCalledAfterMoveAssignment) { DestructorCallback destructorCallback1; DestructorCallback destructorCallback2; destructorCallback1.EXPECT_CALLED( 2); // Once for the temp1 object, once at the assignment destructorCallback2.EXPECT_CALLED( 3); // Once for the temp2 object, once in destructor of var2, once in // destructor of var1 OnlyMoveableClassWithDestructorCallback temp1(&destructorCallback1); either var1 = std::move(temp1); OnlyMoveableClassWithDestructorCallback temp2(&destructorCallback2); either var2 = std::move(temp2); var1 = std::move(var2); } TEST(EitherTestDestructor, RightDestructorIsCalledAfterMoveAssignment) { DestructorCallback destructorCallback1; DestructorCallback destructorCallback2; destructorCallback1.EXPECT_CALLED( 2); // Once for the temp1 object, once at the assignment destructorCallback2.EXPECT_CALLED( 3); // Once for the temp2 object, once in destructor of var2, once in // destructor of var1 OnlyMoveableClassWithDestructorCallback temp1(&destructorCallback1); either var1 = std::move(temp1); OnlyMoveableClassWithDestructorCallback temp2(&destructorCallback2); either var2 = std::move(temp2); var1 = std::move(var2); } // NOLINTEND(*)