#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2023 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_SUPPORT_SOURCE 1 #include "Nuclex/Support/Events/Delegate.h" #include using namespace std; namespace { // ------------------------------------------------------------------------------------------- // /// Free function used to test delegate calls void freeFunction(int) { } // ------------------------------------------------------------------------------------------- // /// Writes a value into the integer at the provided address /// Address of an integer that will be set void setIntegerFunction(int *integerToSet) { *integerToSet = 123; } // ------------------------------------------------------------------------------------------- // /// Writes a different value into the integer at the provided address /// Address of an integer that will be set void setOtherIntegerFunction(int *integerToSet) { *integerToSet = 987; } // ------------------------------------------------------------------------------------------- // /// Dummy class used to test delegate calls to class methods class StaticMock { /// Method that can be called via delegate for testing /// Dummy integer value that will be remembered public: static void Notify(int something) { LastSomethingParameterValue = something; ++ReceivedNotificationCount; } /// Method that does nothing used for delegate comparison testing public: static void Dummy(int) {} /// Number of calls to Notify() the instance has observed public: static std::size_t ReceivedNotificationCount; /// Value that was last passed to the Notify() method public: static int LastSomethingParameterValue; }; /// Number of calls to Notify() the instance has observed std::size_t StaticMock::ReceivedNotificationCount = 0; /// Value that was last passed to the Notify() method int StaticMock::LastSomethingParameterValue = 0; // ------------------------------------------------------------------------------------------- // /// Dummy class used to test delegate calls to object methods class Mock { /// Initializes a new mocked subscriber public: Mock() : ReceivedNotificationCount(0), LastSomethingParameterValue(0), ConstNotifyCalled(false) {} /// Method that can be called via delegate for testing /// Dummy integer value that will be remembered public: void Notify(int something) { this->LastSomethingParameterValue = something; ++this->ReceivedNotificationCount; } /// Method that can be called via delegate for testing /// Dummy integer value that will be remembered public: void ConstNotify(int something) const{ this->LastSomethingParameterValue = something; ++this->ReceivedNotificationCount; this->ConstNotifyCalled = true; } /// Number of calls to Notify() the instance has observed public: mutable std::size_t ReceivedNotificationCount; /// Value that was last passed to the Notify() method public: mutable int LastSomethingParameterValue; /// Whether the ConstNotify() method was called public: mutable bool ConstNotifyCalled; }; // ------------------------------------------------------------------------------------------- // } // anonymous namespace namespace Nuclex { namespace Support { namespace Events { // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, HasDefaultConstructor) { EXPECT_NO_THROW( Delegate test; ); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CallingEmptyEventThrowsException) { Delegate test; EXPECT_THROW( test(123), Errors::EmptyDelegateCallError ); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanCallFreeFunctions) { Delegate test = ( Delegate::Create() ); int value = 456; EXPECT_EQ(value, 456); test(&value); EXPECT_EQ(value, 123); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanCallStaticClassMethods) { Delegate test = ( Delegate::Create<&StaticMock::Notify>() ); EXPECT_EQ(StaticMock::LastSomethingParameterValue, 0); EXPECT_EQ(StaticMock::ReceivedNotificationCount, 0U); test(666); EXPECT_EQ(StaticMock::LastSomethingParameterValue, 666); EXPECT_EQ(StaticMock::ReceivedNotificationCount, 1U); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanCallObjectMethods) { Mock callTarget; Delegate test = ( Delegate::Create(&callTarget) ); EXPECT_EQ(callTarget.LastSomethingParameterValue, 0); EXPECT_EQ(callTarget.ReceivedNotificationCount, 0U); test(42); EXPECT_EQ(callTarget.LastSomethingParameterValue, 42); EXPECT_EQ(callTarget.ReceivedNotificationCount, 1U); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanCallConstObjectMethodsOnNonConstObject) { Mock callTarget; Delegate test = ( Delegate::Create(&callTarget) ); EXPECT_EQ(callTarget.LastSomethingParameterValue, 0); EXPECT_EQ(callTarget.ReceivedNotificationCount, 0U); test(0x4472676E); EXPECT_EQ(callTarget.LastSomethingParameterValue, 0x4472676E); EXPECT_EQ(callTarget.ReceivedNotificationCount, 1U); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanCallConstObjectMethodsOnConstObject) { const Mock callTarget; Delegate test = ( Delegate::Create(&callTarget) ); EXPECT_EQ(callTarget.LastSomethingParameterValue, 0); EXPECT_EQ(callTarget.ReceivedNotificationCount, 0U); test(0x4472676E); EXPECT_EQ(callTarget.LastSomethingParameterValue, 0x4472676E); EXPECT_EQ(callTarget.ReceivedNotificationCount, 1U); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanResetDelegateToEmpty) { Delegate test = ( Delegate::Create() ); test.Reset(); EXPECT_THROW( test(654), Errors::EmptyDelegateCallError ); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanResetDelegateToDifferentFunction) { Delegate test = ( Delegate::Create() ); test.Reset(); int value = 111; EXPECT_EQ(value, 111); test(&value); EXPECT_EQ(value, 987); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanResetDelegateToDifferentStaticMethod) { Delegate test = ( Delegate::Create() ); test.Reset(); Delegate::Create<&StaticMock::Notify>(); int value = 111; EXPECT_EQ(value, 111); test(&value); EXPECT_EQ(value, 987); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanResetDelegateToDifferentObjectMethod) { Delegate test = ( Delegate::Create() ); Mock callTarget; test.Reset(&callTarget); EXPECT_EQ(callTarget.LastSomethingParameterValue, 0); EXPECT_EQ(callTarget.ReceivedNotificationCount, 0U); test(555); EXPECT_EQ(callTarget.LastSomethingParameterValue, 555); EXPECT_EQ(callTarget.ReceivedNotificationCount, 1U); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanResetDelegateToDifferentConstObjectMethodOnNonConstObject) { Delegate test = ( Delegate::Create() ); Mock callTarget; test.Reset(&callTarget); EXPECT_EQ(callTarget.LastSomethingParameterValue, 0); EXPECT_EQ(callTarget.ReceivedNotificationCount, 0U); test(555); EXPECT_EQ(callTarget.LastSomethingParameterValue, 555); EXPECT_EQ(callTarget.ReceivedNotificationCount, 1U); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, CanResetDelegateToDifferentConstObjectMethodOnConstObject) { Delegate test = ( Delegate::Create() ); const Mock callTarget; test.Reset(&callTarget); EXPECT_EQ(callTarget.LastSomethingParameterValue, 0); EXPECT_EQ(callTarget.ReceivedNotificationCount, 0U); test(555); EXPECT_EQ(callTarget.LastSomethingParameterValue, 555); EXPECT_EQ(callTarget.ReceivedNotificationCount, 1U); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, EmptyDelegateEqualsEmptyDelegate) { Delegate a, b; EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, FreeFunctionDelegatesCanBeCompared) { Delegate a = ( Delegate::Create() ); Delegate b = ( Delegate::Create() ); Delegate x = ( Delegate::Create() ); Delegate empty; // Both delegates invoking the same free function are equal EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); EXPECT_TRUE(b == a); EXPECT_FALSE(b != a); // Delegates invoking different free functions are not equal EXPECT_FALSE(b == x); EXPECT_TRUE(b != x); // Delegates invoking a free function aren't equal to empty delegates EXPECT_FALSE(b == empty); EXPECT_TRUE(b != empty); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, StaticClassMethodDelegatesCanBeCompared) { Delegate a = ( Delegate::Create<&StaticMock::Notify>() ); Delegate b = ( Delegate::Create<&StaticMock::Notify>() ); Delegate x = ( Delegate::Create<&StaticMock::Dummy>() ); Delegate empty; // Both delegates invoking the same free function are equal EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); EXPECT_TRUE(b == a); EXPECT_FALSE(b != a); // Delegates invoking different free functions are not equal EXPECT_FALSE(b == x); EXPECT_TRUE(b != x); // Delegates invoking a free function aren't equal to empty delegates EXPECT_FALSE(b == empty); EXPECT_TRUE(b != empty); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, ObjectMethodDelegatesCanBeCompared) { Mock firstTarget, secondTarget; Delegate a = ( Delegate::Create(&firstTarget) ); Delegate b = ( Delegate::Create(&firstTarget) ); Delegate x = ( Delegate::Create(&firstTarget) ); Delegate y = ( Delegate::Create(&secondTarget) ); Delegate empty; // Both delegates invoking the same object method are equal EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); EXPECT_TRUE(b == a); EXPECT_FALSE(b != a); // Delegates calling a different method on the same object are not equal EXPECT_FALSE(a == x); EXPECT_TRUE(a != x); // Delegates calling the same method on a different object are not equal EXPECT_FALSE(a == y); EXPECT_TRUE(a != y); // Delegates invoking an object method aren't the same as empty delegates EXPECT_FALSE(a == empty); EXPECT_TRUE(a != empty); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, ConstObjectMethodDelegatesCanBeCompared) { Mock firstTarget, secondTarget; Delegate a = ( Delegate::Create(&firstTarget) ); Delegate b = ( Delegate::Create(&firstTarget) ); Delegate x = ( Delegate::Create(&firstTarget) ); Delegate y = ( Delegate::Create(&secondTarget) ); Delegate empty; // Both delegates invoking the same object method are equal EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); EXPECT_TRUE(b == a); EXPECT_FALSE(b != a); // Delegates calling a different method on the same object are not equal EXPECT_FALSE(a == x); EXPECT_TRUE(a != x); // Delegates calling the same method on a different object are not equal EXPECT_FALSE(a == y); EXPECT_TRUE(a != y); // Delegates invoking an object method aren't the same as empty delegates EXPECT_FALSE(a == empty); EXPECT_TRUE(a != empty); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, ConstMethodDelegateMatchesBetweenConstAndNonConstInstances) { Mock callTarget; Mock &nonConstTarget = callTarget; const Mock &constTarget = callTarget; Delegate a = ( Delegate::Create(&nonConstTarget) ); Delegate b = ( Delegate::Create(&constTarget) ); // Both delegates invoking the same object method are equal, even if once is // subscribes to a const object and the other to the same object, but non-const EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); EXPECT_TRUE(b == a); EXPECT_FALSE(b != a); } // ------------------------------------------------------------------------------------------- // TEST(DelegateTest, OnlyEmptyDelegateHasNoValue) { Mock callTarget; Delegate test; EXPECT_TRUE(!test); EXPECT_FALSE(test.HasTarget()); test.Reset(&callTarget); EXPECT_FALSE(!test); EXPECT_TRUE(test.HasTarget()); test.Reset<&StaticMock::Dummy>(); EXPECT_FALSE(!test); EXPECT_TRUE(test.HasTarget()); } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Support::Events