TEST_F(AutoRestarterTest, RestartsOnException) {
    AutoCurrentContext()->Initiate();

    // Use a bolt facility to attach one of our text fixtures:
    AutoCurrentContext()->BoltTo<ThrowsAnExceptionFirstTime, RestartingSigil>();

    // Create our bolt and the restarter
    AutoRequired<CreationDetectionBolt> bolt;

    AutoRestarterConfig cfg;
    cfg.restartOnException = true;
    cfg.restartOnShutdown = false;
    cfg.startWhenCreated = true;
    AutoConstruct<AutoRestarter<RestartingSigil>> restarter(cfg);

    // Verify the bolt got called:
    ASSERT_TRUE(bolt->called) << "Bolt was not called even though a context restarter was present in the current context";

    // Verify subcontext properties:
    auto subCtxt = restarter->GetContext();
    ASSERT_TRUE(subCtxt != nullptr) << "Restarter did not correctly create a subcontext";
    ASSERT_TRUE(subCtxt->Is<RestartingSigil>()) << "Generated subcontext was not marked with the right sigil";
    ASSERT_TRUE(subCtxt->IsInitiated()) << "Generated subcontext should have been prospectively started, but was not";

    // Terminate the subcontext directly:
    subCtxt->SignalShutdown();

    // Verify that this causes the restarter to release its hold on the subcontext:
    auto newSubCtxt = restarter->GetContext();
    ASSERT_NE(subCtxt, newSubCtxt) << "Restarter did not release a terminated subcontext";
    ASSERT_FALSE(newSubCtxt) << "Restarter should not have attempted to create any new contexts on teardown";
}
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(AutoInjectableTest, VerifySimpleThreadWait) {
  // Immediate kickoff:
  AutoCurrentContext()->Initiate();

  // Make an injectable, run it, and stuff it right into a future:
  AutoFuture future;
  MakeInjectable<CoreThread>()(&future);

  // Make a thread and then start it going:
  Autowired<CoreThread> thread;
  ASSERT_TRUE(thread.IsAutowired()) << "Thread was not injected by an injector as expected";

  AutoRequired<std::mutex> barr;
  {
    std::lock_guard<std::mutex> lk(*barr);

    // Add a lambda that we intentionally block:
    *thread += [] {
      Autowired<CoreThread> thread;
      Autowired<std::mutex> barr;
      ASSERT_TRUE(thread && barr) << "Failed to find a required type in the current context";
      std::lock_guard<std::mutex> lk(*barr);
      thread->Stop();
    };

    // Instant wait:
    ASSERT_FALSE(future.WaitFor(std::chrono::nanoseconds(1))) << "Premature wait return on an injector-provided future";
  }

  // Now that the thread is unblocked, verify that it quits:
  ASSERT_TRUE(future.WaitFor(std::chrono::seconds(5))) << "Wait failed to return on an injector-provided future";
}
TEST_F(ContextEnumeratorTest, TrivialEnumeration) {
  size_t ct = 0;
  for(const auto& cur : ContextEnumerator(AutoCurrentContext())) {
    (void) cur;
    ct++;
  }
  ASSERT_EQ(1UL, ct) << "Context enumerator failed to enumerate a context with no children";
}
TEST_F(CoreContextTest, CorrectHitExceptionalTeardown) {
  HasOverriddenNewOperator::s_deleterHitCount = 0;

  // Create our type--we expect this to throw:
  ASSERT_ANY_THROW(AutoCurrentContext()->Construct<HasOverriddenNewOperator>(true)) << "Construct operation did not propagate an exception to the caller";

  // Now verify that the correct deleter was hit to release partially constructed memory:
  ASSERT_EQ(1UL, HasOverriddenNewOperator::s_deleterHitCount) << "Deleter was not correctly hit in an exceptional teardown";
}
Beispiel #7
0
TEST_F(AutoPacketFactoryTest, CurrentPacket) {
  AutoCurrentContext()->Initiate();
  AutoRequired<AutoPacketFactory> factory;
  ASSERT_EQ(nullptr, factory->CurrentPacket()) << "Current packet returned before any packets were issued";
  auto packet = factory->NewPacket();
  ASSERT_EQ(packet, factory->CurrentPacket()) << "Current packet was not reported correctly as being issued to the known current packet";
  packet.reset();
  ASSERT_EQ(nullptr, factory->CurrentPacket()) << "A current packet was reported after the current packet has expired";
}
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";
}
Beispiel #9
0
TEST_F(AutoPacketFactoryTest, WaitRunsDownAllPackets) {
  AutoCurrentContext()->Initiate();

  // Create a factory in our context, factory had better be started:
  AutoRequired<AutoPacketFactory> factory;
  ASSERT_TRUE(factory->IsRunning()) << "Factory was not started even though it was a member of an initiated context";

  // Make the thread create and hold a packet, and then return
  AutoRequired<IssuesPacketWaitsThenQuits> ipwtq;

  // Shutdown context
  AutoCurrentContext()->SignalShutdown();

  // Now we're going to try to run down the factory:
  factory->Wait();

  // Verify that the thread has quit:
  ASSERT_TRUE(ipwtq->m_hasQuit) << "AutoPacketFactory::Wait returned prematurely";
}
Beispiel #10
0
TEST_F(AutoPacketFactoryTest, CanRemoveAddedLambda) {
  AutoCurrentContext()->Initiate();
  AutoRequired<AutoPacketFactory> factory;

  auto desc = *factory += [](int&){};
  auto packet1 = factory->NewPacket();
  *factory -= desc;
  auto packet2 = factory->NewPacket();

  ASSERT_TRUE(packet1->Has<int>()) << "First packet did not posess expected decoration";
  ASSERT_FALSE(packet2->Has<int>()) << "Decoration present even after all filters were removed from a factory";
}
Beispiel #11
0
TEST_F(ObjectPoolTest, MovableObjectPoolAysnc) {
  static const size_t sc_count = 10000;
  ObjectPool<int> from;

  {
    // Issue a zillion objects from the from pool:
    std::vector<std::shared_ptr<int>> objs;
    for(size_t i = sc_count; i--;)
      objs.push_back(from.Wait());

    // Make a thread, let it hold these objects while we move its pool:
    *AutoRequired<CoreThread>() += [objs] {};
  }

  // Kick off threads, then immediately and asynchronously move the pool:
  AutoCurrentContext()->Initiate();
  ObjectPool<int> to = std::move(from);

  // Shutdown time:
  AutoCurrentContext()->SignalShutdown(true);

  // Verify that new pool got all of the objects:
  ASSERT_EQ(sc_count, to.GetCached()) << "Object pool move operation did not correctly relay checked out types";
}
Beispiel #12
0
TEST_F(ObjectPoolTest, OutstandingLimitIsOne) {
  ObjectPool<int> pool(1);
  AutoRequired<CoreThread> ct;
  AutoCurrentContext()->Initiate();

  // Bounce 1000 shared pointers into the CoreThread
  for(size_t i = 0; i < 1000; i++) {
    // Try to wait on the pool:
    auto entity = pool.WaitFor(std::chrono::seconds(10));
    ASSERT_NE(nullptr, entity) << "Failed to obtain an entity from a single-element pool";

    // Pend a lambda that just holds a closure on the entity:
    *ct += [entity] {};
  }
}
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 #14
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 #15
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(AutoRestarterTest, RestartsOnShutdown) {
    AutoCurrentContext()->Initiate();

    // Create the restarter
    AutoRestarterConfig cfg;
    cfg.restartOnShutdown = true;
    cfg.startWhenCreated = true;
    AutoConstruct<AutoRestarter<RestartingSigil>> restarter(cfg);

    // Terminate the restarter's subcontext:
    auto subCtxt = restarter->GetContext();
    subCtxt->SignalShutdown();

    // New context should be created immediately
    ASSERT_NE(subCtxt, restarter->GetContext()) << "Restarter incorrectly held original context beyond shutdown";
    ASSERT_TRUE(restarter->GetContext() != nullptr) << "Restarter did not correctly generate a new context after termination";
}
Beispiel #17
0
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();
}
TEST_F(ContextEnumeratorTest, SimpleRemovalInterference) {
  static const size_t nChildren = 5;

  // Create a few contexts which we intend to destroy as we go along:
  std::unordered_set<std::shared_ptr<CoreContext>> contexts;
  for(size_t i = nChildren; i--;)
    contexts.insert(AutoCreateContext());

  // Also add ourselves:
  contexts.insert(AutoCurrentContext());

  // Enumerate contexts, and remove them from the set:
  size_t nRemoved = 0;
  for(const auto& cur : CurrentContextEnumerator()) {
    ASSERT_TRUE(!!contexts.count(cur)) << "Failed to find a context enumerated by the context enumerator";
    contexts.erase(cur);
    nRemoved++;
  }

  // Verify we got the number removed we expected:
  ASSERT_EQ(nChildren + 1UL, nRemoved) << "Context enumerator did not remove the expected number of children";
}
int main() {
  // Initiate the current context
  AutoCurrentContext()->Initiate();
  
  // Inject our AutoFilter enabled context members into the context
  AutoRequired<StringFilter>();
  AutoRequired<StringIntFilter>();
  
  // Each context automatically includes an AutoPacketFactory type. This
  // can be used to create new packets in the AutoFilter network
  Autowired<AutoPacketFactory> factory;
  
  // When decorating a packet, all AutoFilters that have that type as an argument
  // will be called. It is only called when all argument types have been decorated
  auto packet = factory->NewPacket();
  
  packet->Decorate(std::string("Hello World"));
  
  std::cout << "StringIntFilter not called yet" << std::endl;
  
  // StringIntFilter will now be called since all AutoFilter arguments have ben decorated
  packet->Decorate(42);
}
Beispiel #20
0
int main () {
  // Initialization of context, injection of context members, obtaining access to the factory.
  AutoCurrentContext()->Initiate();
  AutoRequired<Hippo1> hippo1;
  AutoRequired<Hippo2> hippo2;
  AutoRequired<Hippo3> hippo3;
  AutoRequired<Hippo4> hippo4;
  AutoRequired<Hippo5> hippo5;
  AutoRequired<Hippo6> hippo6;
  Autowired<AutoPacketFactory> factory;
  // Declare a packet to use in the following code blocks.
  std::shared_ptr<AutoPacket> packet;

/// At this point we will execute the filter network a number of times, each with a different type related to `std::string`,
/// in order to observe which filters are executed.  The first six executions demonstrate the types that are equivalent to
/// `std::string` as input parameters (in which we expect only `Hippo#::AutoFilter` to be called). 
  {
    packet = factory->NewPacket();
    std::cout << "Decorating packet with instance of `std::string`:\n";
    std::string s("std::string");
    packet->Decorate(s);
    std::cout << '\n';
  }
  
  {
    packet = factory->NewPacket();
    std::cout << "Decorating packet with instance of `const std::string`:\n";
    const std::string s("const std::string");
    packet->Decorate(s);
    std::cout << '\n';
  }
  
  {
    packet = factory->NewPacket();
    std::cout << "Decorating packet with instance of `std::shared_ptr<std::string>`:\n";
    std::shared_ptr<std::string> s = std::make_shared<std::string>("std::shared_ptr<std::string>");
    packet->Decorate(s);
    std::cout << '\n';
  }
  
  {
    packet = factory->NewPacket();
    std::cout << "Decorating packet with instance of `const std::shared_ptr<std::string>`:\n";
    const std::shared_ptr<std::string> s = std::make_shared<std::string>("const std::shared_ptr<std::string>");
    packet->Decorate(s);
    std::cout << '\n';
  }
  
  {
    packet = factory->NewPacket();
    std::cout << "Decorating packet with instance of `std::shared_ptr<const std::string>`:\n";
    std::shared_ptr<const std::string> s = std::make_shared<const std::string>("std::shared_ptr<const std::string>");
    packet->Decorate(s);
    std::cout << '\n';
  }
  
  {
    packet = factory->NewPacket();
    std::cout << "Decorating packet with instance of `const std::shared_ptr<const std::string>`:\n";
    const std::shared_ptr<const std::string> s = std::make_shared<const std::string>("const std::shared_ptr<const std::string>");
    packet->Decorate(s);
    std::cout << '\n';
  }

/// Verify that the accumulated values in the `m_received_input_decoration_types` for each context member are what we expect.
  {
    std::unordered_set<std::string> expected_hippo_inputs({
      "std::string",
      "const std::string",
      "std::shared_ptr<std::string>",
      "const std::shared_ptr<std::string>",
      "std::shared_ptr<const std::string>",
      "const std::shared_ptr<const std::string>"
    });
    if (hippo1->m_received_input_decoration_types != expected_hippo_inputs)
      throw std::runtime_error("Hippo1 did not receive the expected inputs.");
    if (hippo1->m_received_input_decoration_types != expected_hippo_inputs)
      throw std::runtime_error("Hippo2 did not receive the expected inputs.");
    if (hippo1->m_received_input_decoration_types != expected_hippo_inputs)
      throw std::runtime_error("Hippo3 did not receive the expected inputs.");
    if (hippo1->m_received_input_decoration_types != expected_hippo_inputs)
      throw std::runtime_error("Hippo4 did not receive the expected inputs.");
    if (hippo1->m_received_input_decoration_types != expected_hippo_inputs)
      throw std::runtime_error("Hippo5 did not receive the expected inputs.");
    if (hippo1->m_received_input_decoration_types != expected_hippo_inputs)
      throw std::runtime_error("Hippo6 did not receive the expected inputs.");
    std::cout << "All Hippo# context members received the expected inputs.\n";
  }
  std::cout << "All verifications passed.\n";

  return 0; // Return with no error.
}
Beispiel #21
0
TEST_F(AutoPacketFactoryTest, StopReallyStops) {
  AutoCurrentContext()->SignalShutdown();

  AutoRequired<AutoPacketFactory> factory;
  ASSERT_TRUE(factory->ShouldStop()) << "Expected that an attempt to insert a packet factory to an already-stopped context would stop the packet factory";
}
Beispiel #22
0
TEST_F(AutoPacketFactoryTest, VerifyNoIssueWhileStopped) {
  AutoCurrentContext()->SignalShutdown();

  AutoRequired<AutoPacketFactory> factory;
  ASSERT_THROW(factory->NewPacket(), autowiring_error) << "Issuing a packet in a context that has already been stopped should throw an exception";
}