Beispiel #1
0
TEST_F(ContextMapTest, VerifyWithThreadsPathological) {
  ContextMap<size_t> mp;

  // Context collection and exit race threads:
  vector<std::shared_ptr<CoreContext>> contexts;

  // Exit race controller:
  AutoRequired<ExitRaceSignal> signal;

  // Create a number of dependent contexts:
  for(size_t i = 0; i < 100; i++) {
    AutoCreateContext context;
    contexts.push_back(context);

    // Store a shared pointer
    mp.Add(i, context);

    // Start the context
    context->Initiate();
  }

  // Set the signal:
  signal->Signal();

  // Verify that the map empties once our zero-count is hit:
  for(size_t i = 0; i < contexts.size(); i++) {
    contexts[i]->SignalShutdown(true);
  }

  // Clear the context collection:
  contexts.clear();
  EXPECT_EQ(0UL, mp.size()) << "Context map did not empty as expected";
}
Beispiel #2
0
TEST_F(ContextMapTest, VerifyWithThreads) {
  ContextMap<string> mp;
  std::shared_ptr<SimpleThreaded> threaded;
  std::weak_ptr<CoreContext> weakContext;

  {
    AutoCreateContext context;

    // Obtain a weak pointer of our own, and add to the context:
    weakContext = context;
    mp.Add("context_withthreads", context);

    // Add a thread to hold the context open for awhile:
    threaded = context->Inject<SimpleThreaded>();

    // Start the context
    context->Initiate();
  }

  // Assert that the context still actually exists:
  ASSERT_TRUE(!weakContext.expired()) << "Simple thread exited before it was signalled to exit";

  {
    // Verify that we can still find the context while the thread is alive:
    std::shared_ptr<CoreContext> context = mp.Find("context_withthreads");
    ASSERT_TRUE(!!context.get()) << "Map evicted a context before expected";

    // Relock the weak context, verify that we get back the same pointer:
    auto relocked = weakContext.lock();
    EXPECT_EQ(relocked, context) << "Mapped context pointer was not identical to a previously stored context pointer";

    // Terminate whole context
    context->SignalTerminate();
  }

  // Release our threaded entity:
  threaded.reset();

  {
    // Verify that the context is gone now that everything in it has stopped running
    auto ctxt = mp.Find("context_withthreads");
    EXPECT_FALSE(ctxt) << "Context was not properly evicted from the map";

    // Just return early if the context was empty as we expected, the next part of this test is for diagnostics
    if(!ctxt)
      return;

    // Release the pointer so we aren't guilty of holding a reference to the very thing whose
    // destruction we are trying to assure.
    ctxt.reset();

    // Sleep for a little bit and run the verification again.  If the prior expectation fails,
    // but this one succeeds, it could be due to race conditions in CoreThread
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    ctxt = mp.Find("context_withthreads");
    EXPECT_FALSE(ctxt) << "Context was not properly evicted even after waiting for a time to ensure eviction";
  }
}
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(IPCMessagingTest, SequentialMessageTransmission)
{
  AutoCurrentContext ctxt;
  std::string ns = GenerateNamespaceName();

  // Create client and server
  AutoConstruct<IPCClient> client(IPCTestScope(), ns.c_str());
  AutoConstruct<IPCListener> listener(IPCTestScope(), ns.c_str());

  auto val = std::make_shared<std::promise<int>>();
  listener->onClientConnected += [&val](const std::shared_ptr<IPCEndpoint>& ep) {
    AutoCreateContext ctxt;
    ctxt->Add(ep);

    auto channel = ep->AcquireChannel(0, IPCEndpoint::Channel::READ_ONLY);
    int nMessages = 0;
    while (!ep->IsClosed()) {
      auto buffers = channel->ReadMessageBuffers();
      if (buffers.size() != 4)
        break;
      nMessages++;
    }
    val->set_value(nMessages);
  };

  auto ep = client->Connect(std::chrono::minutes(1));
  ASSERT_NE(nullptr, ep) << " Failed to connect in time";

  {
    auto channel = ep->AcquireChannel(0, IPCEndpoint::Channel::WRITE_ONLY);
    for (uint32_t i = 0; i < 300; i++) {
      Message message{ i, i + 1, i + 2, i + 3 };
      channel->Write(&message, sizeof(message));
      channel->Write(&message, sizeof(message));
      channel->Write(&message, sizeof(message));
      channel->Write(&message, sizeof(message));

      // Indicate that the entire message has been written
      channel->WriteMessageComplete();
    }
  }

  // Close connection, shut down context:
  ep.reset();
  ctxt->SignalShutdown();

  // Block until gatherer stops:
  auto f = val->get_future();
  ASSERT_EQ(std::future_status::ready, f.wait_for(std::chrono::minutes(1))) << "Server did not receive messages in a timely fashion";
  ASSERT_EQ(300, f.get()) << "Not all messages were received as expected";
}
void AutowiringEnclosure::OnTestStart(const testing::TestInfo& info) {
  AutoRequired<AutowiringEnclosureExceptionFilter> filter;

  // The context proper.  This is automatically assigned as the current
  // context when SetUp is invoked.
  AutoCreateContext create;
  create->Construct<TestInfoProxy>(info);

  // Add exception filter in this context:
  create->Inject<AutowiringEnclosureExceptionFilter>();

  // Now make it current and let the test run:
  create->SetCurrent();
}
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);
}
Beispiel #7
0
TEST_F(CoreContextTest, ChildContextSignalOrder) {
  AutoCurrentContext ctxt;

  AutoCreateContext childCtxt;
  AutoRequired<ChildListener> cl(childCtxt);
  childCtxt->Initiate();

  // Now verify correct ordering:
  bool gotStartObservation = false;
  childCtxt->onRunning += [&] {
    gotStartObservation = cl->gotStart;
  };

  ctxt->Initiate();
  ASSERT_TRUE(gotStartObservation) << "Start observation obtained";
}
Beispiel #8
0
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";
}
Beispiel #9
0
TEST_F(CoreContextTest, InitiateCausesDelayedHold) {
  std::weak_ptr<CoreContext> ctxtWeak;

  // Create and initiate a subcontext, but do not initiate the parent context
  {
    AutoCreateContext ctxt;
    ctxtWeak = ctxt;
    ctxt->Initiate();
  }

  // Weak pointer should not be expired yet
  ASSERT_FALSE(ctxtWeak.expired()) << "Subcontext expired after initiation even though its parent context was not yet initiated";

  // Starting up the outer context should cause the inner one to self destruct
  AutoCurrentContext()->Initiate();
  ASSERT_TRUE(ctxtWeak.expired()) << "Subcontext containing no threads incorrectly persisted after termination";
}
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";
}
Beispiel #11
0
TEST_F(CoreContextTest, InitiateAssertsSignals) {
  AutoCurrentContext outer;

  auto teardown = std::make_shared<bool>(false);
  {
    AutoCreateContext ctxt;
    auto initiated = std::make_shared<bool>(false);
    auto running = std::make_shared<bool>(false);
    auto shutdown = std::make_shared<bool>(false);

    ctxt->onInitiated += [initiated] { *initiated = true; };
    ctxt->onRunning += [running] { *running = true; };
    ctxt->onShutdown += [shutdown] { *shutdown = true; };
    ctxt->onTeardown += [teardown] (const CoreContext&) { *teardown = true; };

    ctxt->Initiate();
    ASSERT_TRUE(*initiated) << "Initiation signal not asserted on context startup";
    ASSERT_FALSE(*running) << "Running signal asserted before the outer context was started";
    ASSERT_FALSE(*shutdown) << "Termination signal asserted prematurely";
    *initiated = false;

    outer->Initiate();
    ASSERT_FALSE(*initiated) << "Initiation signal was redundantly asserted";
    ASSERT_TRUE(*running) << "Running signal not asserted when the outer context was started";
    ASSERT_FALSE(*shutdown) << "Termination signal asserted prematurely";

    *running = false;

    ctxt->Initiate();
    ASSERT_FALSE(*initiated) << "Initiation signal redundantly asserted";
    ASSERT_FALSE(*running) << "Running signal redundantly asserted";
    ASSERT_FALSE(*shutdown) << "Termination signal asserted unexpectedly";

    ctxt->SignalShutdown();
    ASSERT_FALSE(*initiated) << "Initiation signal not asserted during teardown";
    ASSERT_FALSE(*running) << "Running signal asserted improperly on teardown";
    ASSERT_TRUE(*shutdown) << "Termination signal not asserted as expected";

    ASSERT_FALSE(*teardown) << "Teardown handler notified prematurely";
  }
  ASSERT_TRUE(*teardown) << "Teardown handler not correctly notified on context teardown";
}
Beispiel #12
0
TEST_F(CoreContextTest, AppropriateShutdownInterleave) {
  // Need both an outer and an inner context
  AutoCurrentContext ctxtOuter;
  AutoCreateContext ctxtInner;

  // Need to inject types at both scopes
  AutoRequired<ExplicitlyHoldsOutstandingCount> outer(ctxtOuter);
  AutoRequired<ExplicitlyHoldsOutstandingCount> inner(ctxtInner);

  // Start both contexts up
  ctxtOuter->Initiate();
  ctxtInner->Initiate();

  // Now shut down the outer context.  Hand off to an async, we want this to block.
  std::thread holder{
    [ctxtOuter] {
      ctxtOuter->SignalShutdown(true);
    }
  };
  auto holderClean = MakeAtExit([&holder] { holder.join(); });

  // Need to ensure that both outstanding counters are reset at some point:
  {
    auto cleanup = MakeAtExit([&] {
      outer->Proceed();
      inner->Proceed();
    });

    // Outer entry should have called "stop":
    auto future = outer->calledStop.get_future();
    ASSERT_EQ(
      std::future_status::ready,
      future.wait_for(std::chrono::seconds(5))
    ) << "Outer scope's OnStop method was incorrectly blocked by a child context member taking a long time to shut down";
  }

  // Both contexts should be stopped now:
  ASSERT_TRUE(ctxtOuter->Wait(std::chrono::seconds(5))) << "Outer context did not tear down in a timely fashion";
  ASSERT_TRUE(ctxtOuter->IsQuiescent()) << "Quiescence not achieved by outer context after shutdown";
  ASSERT_TRUE(ctxtInner->Wait(std::chrono::seconds(5))) << "Inner context did not tear down in a timely fashion";
}
Beispiel #13
0
TEST_F(CoreContextTest, UnlinkOnTeardown) {
  std::weak_ptr<ClassThatPoints1> weakA;
  std::shared_ptr<ClassThatPoints2> strongB;
  AutoRequired<SimpleObject> so;

  // Set up a subcontext with some cycles and external links:
  {
    // Main context that gets reset second
    AutoCreateContext ctxt;

    // Sibling context that we're also going to reset
    AutoCreateContext otherContext;
    otherContext->Inject<std::vector<int>>();

    AutoRequired<ClassThatPoints1> a(ctxt);
    AutoRequired<ClassThatPoints2> b(ctxt, otherContext);
    weakA = a;
    strongB = b;

    ASSERT_TRUE(a->so.IsAutowired()) << "Root object pointer not correctly obtained";
    ASSERT_TRUE(b->so.IsAutowired()) << "Root object pointer not correctly obtained";

    ctxt->onTeardown +=
      [weakA, strongB] (const CoreContext&) {
        // Verify that nothing got screwed up at this point:
        auto a = weakA.lock();
        ASSERT_FALSE(weakA.expired()) << "Weak pointer expired prematurely";
        ASSERT_EQ(strongB, a->b) << "Unlink occurred prematurely";
        ASSERT_EQ(a, strongB->a) << "Unlink occured prematurely";
      };

    // Set the flag at the last possible and to ensure things still get torn down
    ctxt->SetUnlinkOnTeardown(true);
  }

  ASSERT_TRUE(weakA.expired()) << "A reference was leaked even though unlinking was turned on";
  ASSERT_TRUE(strongB->v.IsAutowired()) << "An Autowired field pointing to a foreign context was incorrectly unlinked";
  ASSERT_EQ(so.get(), strongB->so.get()) << "An Autowired field was unlinked on teardown even though it pointed outside of a context";
}
Beispiel #14
0
TEST_F(ContextMapTest, VerifySimple) {
  ContextMap<string> mp;

  // Create a new context and add it to the map:
  {
    AutoCreateContext context;

    // Verify the reference count or the rest of the test will fail
    ASSERT_EQ(context.use_count(), 1) << "A newly created context's use count isn't what was expected";

    // Add and ensure the reference count is unmodified
    mp.Add("context_simple", context);
    ASSERT_EQ(context.use_count(), 1) << "The map altered the context use count";

    // We should be able to find this context now:
    std::shared_ptr<CoreContext> found = mp.Find("context_simple");
    EXPECT_TRUE(!!found.get()) << "Failed to find a context that was just inserted into a context map";
  }

  // We shouldn't be able to find it now that it's gone out of scope:
  std::shared_ptr<CoreContext> notFound = mp.Find("context_simple");
  EXPECT_FALSE(!!notFound.get()) << "Context was not evicted as expected when it went out of scope";
}
Beispiel #15
0
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);
  }
}
 /// <returns>
 /// True if there are no outstanding pointers to the created context, except the one held by base
 /// </returns>
 bool IsContextClean(void) const {return m_create.unique();}