#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/Services/ServiceContainer.h" #include namespace { // ------------------------------------------------------------------------------------------- // /// Example service providing a few simple math methods class CalculatorService { /// Frees all resources owned by a calculator instance public: virtual ~CalculatorService() = default; /// Calculates the sum of two integers /// First integer that will be part of the sum /// Second integer that will be part of the sum /// The sum of the two integers public: virtual int Add(int first, int second) = 0; /// Multiplies two integers with each other /// First integer that will be multiplied with the other /// Second integer that will be multiplied with the other /// The sum of the two integers public: virtual int Multiply(int first, int second) = 0; }; // ------------------------------------------------------------------------------------------- // /// Example implementation of the calculator service class BrokenCalculator : public virtual CalculatorService { /// Calculates the sum of two integers /// First integer that will be part of the sum /// Second integer that will be part of the sum /// The sum of the two integers public: int Add(int first, int second) override { return first + second + 1; } /// Multiplies two integers with each other /// First integer that will be multiplied with the other /// Second integer that will be multiplied with the other /// The sum of the two integers public: int Multiply(int first, int second) override { return first + first * second; }; }; // ------------------------------------------------------------------------------------------- // /// Helper class used by the unit tests to track service destruction class DestructorTester { /// Initializes a new destructor tester using the specified flag /// Will be set when the instance is destroyed public: DestructorTester(bool *destructionFlag) : destructionFlag(destructionFlag) {} /// Sets the destruction flag (unless disarmeds) public: ~DestructorTester() { if(this->destructionFlag != nullptr) { *this->destructionFlag = true; } } /// Disarms the destructor tester, no longer letting it set the flag public: void Disarm() { this->destructionFlag = nullptr; } /// Address of a boolean that will be set when the destructor runs private: bool *destructionFlag; }; // ------------------------------------------------------------------------------------------- // } // anonymous namespace namespace Nuclex { namespace Support { namespace Services { // ------------------------------------------------------------------------------------------- // TEST(ServiceContainerTest, HasDefaultConstructor) { EXPECT_NO_THROW( ServiceContainer test; ); } // ------------------------------------------------------------------------------------------- // TEST(ServiceContainerTest, NewContainerHasNoServices) { ServiceContainer test; EXPECT_EQ(test.CountServices(), 0U); } // ------------------------------------------------------------------------------------------- // TEST(ServiceContainerTest, ServicesCanBeAddedUnderOwnType) { ServiceContainer test; EXPECT_EQ(test.CountServices(), 0U); test.Add(std::make_shared()); EXPECT_EQ(test.CountServices(), 1U); std::shared_ptr service; EXPECT_FALSE(test.TryGet(service)); } // ------------------------------------------------------------------------------------------- // TEST(ServiceContainerTest, ServicesCanBeAddedUnderServiceType) { ServiceContainer test; EXPECT_EQ(test.CountServices(), 0U); test.Add(std::make_shared()); EXPECT_EQ(test.CountServices(), 1U); std::shared_ptr service; EXPECT_TRUE(test.TryGet(service)); } // ------------------------------------------------------------------------------------------- // TEST(ServiceContainerTest, ServicesCanBeRemoved) { ServiceContainer test; EXPECT_EQ(test.CountServices(), 0U); test.Add(std::make_shared()); EXPECT_EQ(test.CountServices(), 1U); std::shared_ptr service; EXPECT_TRUE(test.TryGet(service)); EXPECT_TRUE(test.Remove()); EXPECT_FALSE(test.TryGet(service)); } // ------------------------------------------------------------------------------------------- // TEST(ServiceContainerTest, ContainerDestructorReleasesServices) { bool destructorCalled = false; std::weak_ptr weak; { std::shared_ptr tester = ( std::make_shared(&destructorCalled) ); weak = tester; ServiceContainer test; EXPECT_EQ(test.CountServices(), 0U); test.Add(tester); EXPECT_EQ(test.CountServices(), 1U); // Dropping our shared_ptr to the test object will not destroy it because // another shared_ptr to it is kept by the service container tester.reset(); EXPECT_FALSE(destructorCalled); } // When the service container is destroyed, it should release all shared_ptrs // it is holding on to (in whatever manner), thus now the destructor should run EXPECT_TRUE(destructorCalled); if(!weak.expired()) { std::shared_ptr crap = weak.lock(); if(!!crap) { crap->Disarm(); } } } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Support::Services