TEST_F(WebsocketTest, CleanShutdown) { AutoRequired<WebsocketExceptionFilter>(); // Try starting and stopping server multiple times { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); AutoRequired<AutoNetServer>(); ctxt->Initiate(); ctxt->Wait(std::chrono::milliseconds(200)); ctxt->SignalShutdown(true); } { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); AutoRequired<AutoNetServer>(); ctxt->Initiate(); ctxt->Wait(std::chrono::milliseconds(200)); ctxt->SignalShutdown(true); } { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); AutoRequired<AutoNetServer>(); ctxt->Initiate(); ctxt->Wait(std::chrono::milliseconds(200)); ctxt->SignalShutdown(true); } }
TEST_F(CoreContextTest, CorrectHitAllocatorNew) { HasOverriddenNewOperator::s_deleterHitCount = 0; { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); // Verify that the overload itsef gets called as expected: ASSERT_EQ( (void*) HasOverriddenNewOperator::s_space, new HasOverriddenNewOperator(false) ) << "Overloaded new operator on a test type did not get invoked as expected"; // Create an instance which won't throw: auto hono = AutoCurrentContext()->Construct<HasOverriddenNewOperator>(false); // Verify the correct new allocator was hit: ASSERT_EQ( (void*)HasOverriddenNewOperator::s_space, (void*)hono.get() ) << "Overridden new allocator was not invoked as anticipated"; } // Verify that the deleter got hit as anticipated: ASSERT_EQ(1UL, HasOverriddenNewOperator::s_deleterHitCount) << "The correct deleter was not hit under ordinary teardown"; }
TEST_F(ContextEnumeratorTest, VerifySimpleEnumeration) { // Create a pair of descendant contexts, verify we don't accidentally hit these when enumerating children AutoCreateContext outer; AutoCreateContext inner; CurrentContextPusher pshr(inner); // Add a few children to the current context: AutoCreateContext c1; AutoCreateContext c2; AutoCreateContext c3; AutoCreateContext c4; std::unordered_set<std::shared_ptr<CoreContext>> allCtxts; allCtxts.insert(AutoCurrentContext()); allCtxts.insert(c1); allCtxts.insert(c2); allCtxts.insert(c3); allCtxts.insert(c4); // Create the enumerator, verify we get the expected count: size_t nContexts = 0; for(const std::shared_ptr<CoreContext>& cur : CurrentContextEnumerator()) { ASSERT_TRUE(!!allCtxts.count(cur)) << "Context enumerator accidentally enumerated a context not rooted in the specified parent"; allCtxts.erase(cur); nContexts++; } ASSERT_EQ(5UL, nContexts) << "Context enumerator did not encounter the number of expected children"; ASSERT_TRUE(allCtxts.empty()) << "Context enumerator did not encounter all children as expected"; }
TEST_F(MultiInheritTest, VerifyBaseInitializer) { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); MultiInheritDerived derived; // Expect that something autowires when we're done at least: ASSERT_TRUE(derived.m_member.IsAutowired()); }
TEST_F(ContextCreatorTest, ValidateMultipleEviction) { AutoCurrentContext()->Initiate(); // Number of dependent contexts to be created const size_t count = 100; // Teardown lock, counter, and condition: std::mutex lock; std::condition_variable cond; int counter = count; // Obtain creator pointer: AutoRequired<Creator> creator; // Set up a signal manager at global context scope: AutoRequired<GlobalSignal> signal; { // Array of objects to test destruction on, and corresponding collection of contexts: std::shared_ptr<WaitMember> members[count]; // Create a few contexts: for(int i = count; i--;) { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); // Trivial validation that the newly created context is an empty context: ASSERT_EQ(static_cast<size_t>(0), ctxt->GetMemberCount()) << "A created context was not empty"; // Add in an object to test asynchronous destruction: AutoRequired<WaitMember> obj; members[i] = obj; ASSERT_EQ(signal.get(), obj->m_signal.get()) << "Dependent context wiring did not correctly match to the enclosing scope"; // Add a notifier to signal a continue condition when we have everything we need: ctxt->AddTeardownListener([&lock, &cond, &counter] { (std::lock_guard<std::mutex>)lock, counter--, cond.notify_all(); }); // Kick off the context: ctxt->Initiate(); } // Signal all members and then release everything: signal->Signal(); } // Wait for all contexts to be destroyed std::unique_lock<std::mutex> lk(lock); bool wait_status = cond.wait_for(lk, std::chrono::seconds(1), [&counter] {return counter == 0;}); ASSERT_TRUE(wait_status) << "All teardown listeners didn't trigger, counter still at " << counter; // Validate that everything expires: ASSERT_EQ(static_cast<size_t>(0), creator->GetSize()) << "Not all contexts were evicted as expected"; }
TEST_F(ContextEnumeratorTest, VerifyComplexEnumeration) { std::shared_ptr<CoreContext> firstNamed, secondNamed; AutoCreateContext firstContext; { CurrentContextPusher pshr(firstContext); AutoCreateContextT<NamedContext> named; firstNamed = named; } AutoCreateContext secondContext; { CurrentContextPusher pshr(secondContext); AutoCreateContextT<NamedContext> named; secondNamed = named; } // Verify there is only one context under the first context int firstCount = 0; for(auto ctxt : ContextEnumeratorT<NamedContext>(firstContext)) { firstCount++; ASSERT_EQ(ctxt, firstNamed); ASSERT_EQ(ctxt->GetParentContext(), firstContext); } ASSERT_EQ(firstCount, 1) << "Expected exactly one context in the parent context, found " << firstCount; // Verify there is only one context under the second context int secondCount = 0; for(auto ctxt : ContextEnumeratorT<NamedContext>(secondContext)) { secondCount++; ASSERT_EQ(ctxt, secondNamed); ASSERT_EQ(ctxt->GetParentContext(), secondContext); } ASSERT_EQ(secondCount, 1) << "Expected exactly one context in the parent context, found " << secondCount; // Verify global context structure int globalCount = 0; for(auto ctxt : ContextEnumeratorT<NamedContext>(AutoGlobalContext())) { globalCount++; } ASSERT_EQ(globalCount, 2) << "Expected exactly one context in the parent context, found " << globalCount; }
TEST_F(MultiInheritTest, VerifyCast) { // Create a dummy context and make it current: AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); // Insert a MultiInherit object: auto obj = ctxt->Inject<MultiInherit>(); // Autowire in the pObj: Autowired<MultiInherit> wiredPobj; ASSERT_TRUE(wiredPobj.IsAutowired()) << "Autowiring failed for a multi-inheritance object"; // Verify that we get a pObj back with correct casting: ASSERT_EQ(obj.get(), wiredPobj.get()) << "Autowiring failed on a multiple inheritance object"; }
TEST_F(AutowiringUtilitiesTest, ThreadSpecificPtr) { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); AutoRequired<Thread1> thread1; AutoRequired<Thread2> thread2; s_thread_specific_int.reset(new int(5)); ctxt->Initiate(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); EXPECT_EQ(5, *s_thread_specific_int); AutoCurrentContext()->SignalShutdown(true); }
TEST_F(AutoPacketFactoryTest, AutoPacketFactoryCycle) { AutoCurrentContext()->Initiate(); std::weak_ptr<CoreContext> ctxtWeak; std::weak_ptr<HoldsAutoPacketFactoryReference> hapfrWeak; std::shared_ptr<AutoPacket> packet; { // Create a context, fill it up, kick it off: AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); AutoRequired<HoldsAutoPacketFactoryReference> hapfr(ctxt); ctxt->Initiate(); // A weak pointer is used to detect object destruction ctxtWeak = ctxt; hapfrWeak = hapfr; // Trivial validation-of-reciept: AutoRequired<AutoPacketFactory> factory; { auto trivial = factory->NewPacket(); trivial->Decorate((int) 54); ASSERT_EQ(54, hapfr->m_value) << "A simple packet was not received as expected by an AutoFilter"; } // Create a packet which will force in a back-reference: packet = factory->NewPacket(); // Terminate the context: ctxt->SignalShutdown(); // Verify that we can still decorate the packet and also that the packet is delivered to the factory: packet->Decorate((int) 55); // Relock, verify the value was received by the hapfr: ASSERT_EQ(55, hapfr->m_value) << "AutoFilter did not receive a packet as expected"; } // The context cannot go out of socpe until all packets in the context are out of scope ASSERT_FALSE(ctxtWeak.expired()) << "Context went out of scope before all packets were finished being processed"; // Now we can release the packet and verify that everything gets cleaned up: packet.reset(); ASSERT_TRUE(ctxtWeak.expired()) << "AutoPacketFactory incorrectly held a cyclic reference even after the context was shut down"; ASSERT_TRUE(hapfrWeak.expired()) << "The last packet from a factory was released; this should have resulted in teardown, but it did not"; }
TEST_F(AutoConstructTest, CanConstructRvalueCtor) { auto originalPtr = std::make_shared<int>(555); // Make a unique pointer to a shared pointer, and pass it in: { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); std::unique_ptr<std::shared_ptr<int>> forwarded{new std::shared_ptr<int>(originalPtr)}; AutoConstruct<CanOnlyAcceptMovedInput> coami(std::move(forwarded)); // Should have the correct number of references, no more and no less ASSERT_EQ(2UL, originalPtr.use_count()) << "Forwarding unique pointer did not correctly"; } ASSERT_TRUE(originalPtr.unique()) << "Memory leak detected due to incorrect forwarding of a unique pointer"; }
TEST_F(AutowiringUtilitiesTest, ThreadSpecificPtr) { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); AutoRequired<Thread1> thread1; AutoRequired<Thread2> thread2; s_thread_specific_int.reset(new int(5)); ctxt->Initiate(); auto limit = std::chrono::high_resolution_clock::now() + std::chrono::seconds(10); while(std::chrono::high_resolution_clock::now() < limit) if(5 == *s_thread_specific_int) return; FAIL() << "Thread specific pointer did not increment to the destination value in a timely fashion"; }
TEST_F(ObjectPoolTest, VerifyAsynchronousUsage) { AutoCreateContext ctxt; CurrentContextPusher pshr(ctxt); AutoRequired<SimpleThreadedT<PooledObject>> obj; AutoFired<SharedPtrReceiver<PooledObject>> spr; ObjectPool<PooledObject> pool(3); { // Obtain the pool limit in objects: std::shared_ptr<PooledObject> obj1, obj2, obj3; pool(obj1); pool(obj2); pool(obj3); ASSERT_TRUE(nullptr != obj1.get()) << "Failed to obtain an entry from a new object pool"; // Block--verify that we _do not_ get any of those objects back while they are // still outstanding. { auto obj4 = pool.WaitFor(std::chrono::milliseconds(1)); EXPECT_TRUE(obj4 == nullptr) << "Pool issued another element even though it should have hit its outstanding limit"; } // Now we kick off threads: AutoCurrentContext()->Initiate(); // Fire off a few events: spr(&SharedPtrReceiver<PooledObject>::OnEvent)(obj1); spr(&SharedPtrReceiver<PooledObject>::OnEvent)(obj2); spr(&SharedPtrReceiver<PooledObject>::OnEvent)(obj3); } // This should return more or less right away as objects become available: { auto obj4 = pool.WaitFor(std::chrono::milliseconds(10)); EXPECT_TRUE(obj4 != nullptr) << "Object pool failed to be notified that it received a new element"; } // Cause the thread to quit: *obj += [&obj] { obj->Stop(); }; obj->Wait(); }
void CoreJob::DispatchAllAndClearCurrent(void) { CurrentContextPusher pshr(GetContext()); for(;;) { // Trivially run down the queue as long as we're in the pool: this->DispatchAllEvents(); // Check the size of the queue. Could be that someone added something // between when we finished looping, and when we obtained the lock, and // we don't want to exit our pool if that has happened. std::lock_guard<std::mutex> lk(m_dispatchLock); if(AreAnyDispatchersReady()) continue; // Indicate that we're tearing down and will be done very soon. This is // a signal to consumers that a call to m_curEvent.wait() will be nearly // non-blocking. m_curEventInTeardown = true; m_queueUpdated.notify_all(); break; } }