123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #ifndef TEST_DATASRC_CLIENTS_MGR_H
- #define TEST_DATASRC_CLIENTS_MGR_H 1
- #include <exceptions/exceptions.h>
- #include <auth/datasrc_clients_mgr.h>
- #include <datasrc/datasrc_config.h>
- #include <asiolink/io_service.h>
- #include <boost/function.hpp>
- #include <list>
- // In this file we provide specialization of thread, mutex, condition variable,
- // and DataSrcClientsBuilder for convenience of tests. They don't use
- // actual threads or mutex, and allow tests to inspect some internal states
- // of the corresponding objects.
- //
- // In many cases, tests can use TestDataSrcClientsMgr (defined below) where
- // DataSrcClientsMgr is needed.
- // Below we extend the isc::auth::datasrc_clientmgr_internal namespace to
- // specialize the doNoop() method.
- namespace isc {
- namespace auth {
- namespace datasrc_clientmgr_internal {
- class TestMutex {
- public:
- // for throw_from_noop.
- // None: no throw from specialized doNoop()
- // EXCLASS: throw some exception class object
- // INTEGER: throw an integer
- // INTERNAL: internal error (shouldn't terminate the thread)
- enum ExceptionFromNoop { NONE, EXCLASS, INTEGER, INTERNAL };
- TestMutex() : lock_count(0), unlock_count(0), noop_count(0),
- throw_from_noop(NONE)
- {}
- class Locker {
- public:
- Locker(TestMutex& mutex) : mutex_(mutex) {
- if (mutex.lock_count != mutex.unlock_count) {
- isc_throw(Unexpected,
- "attempt of duplicate lock acquisition");
- }
- ++mutex.lock_count;
- if (mutex.lock_count > 100) { // 100 is an arbitrary choice
- isc_throw(Unexpected,
- "too many test mutex count, likely a bug in test");
- }
- }
- ~Locker() {
- ++mutex_.unlock_count;
- }
- private:
- TestMutex& mutex_;
- };
- size_t lock_count; // number of lock acquisitions; tests can check this
- size_t unlock_count; // number of lock releases; tests can check this
- size_t noop_count; // allow doNoop() to modify this
- ExceptionFromNoop throw_from_noop; // tests can set this to control doNoop
- };
- class TestCondVar {
- public:
- TestCondVar() : wait_count(0), signal_count(0), command_queue_(NULL),
- delayed_command_queue_(NULL)
- {}
- TestCondVar(std::list<Command>& command_queue,
- std::list<Command>& delayed_command_queue) :
- wait_count(0),
- signal_count(0),
- command_queue_(&command_queue),
- delayed_command_queue_(&delayed_command_queue)
- {
- }
- void wait(TestMutex& mutex) {
- // bookkeeping
- ++mutex.unlock_count;
- ++wait_count;
- ++mutex.lock_count;
- if (wait_count > 100) { // 100 is an arbitrary choice
- isc_throw(Unexpected,
- "too many cond wait count, likely a bug in test");
- }
- // make the delayed commands available
- command_queue_->splice(command_queue_->end(), *delayed_command_queue_);
- }
- void signal() {
- ++signal_count;
- }
- size_t wait_count; // number of calls to wait(); tests can check this
- size_t signal_count; // number of calls to signal(); tests can check this
- private:
- std::list<Command>* command_queue_;
- std::list<Command>* delayed_command_queue_;
- };
- // Convenient shortcut
- typedef DataSrcClientsBuilderBase<TestMutex, TestCondVar>
- TestDataSrcClientsBuilder;
- // We specialize this command handler for the convenience of tests.
- // It abuses our specialized Mutex to count the number of calls of this method.
- template<>
- void
- TestDataSrcClientsBuilder::doNoop();
- // A specialization of DataSrcClientsBuilder that allows tests to inspect
- // its internal states via static class variables. Using static is suboptimal,
- // but DataSrcClientsMgr is highly encapsulated, this seems to be the best
- // possible compromise.
- class FakeDataSrcClientsBuilder {
- public:
- // true iff a builder has started.
- static bool started;
- // These five correspond to the resource shared with the manager.
- // xxx_copy will be set in the manager's destructor to record the
- // final state of the manager.
- static std::list<Command>* command_queue;
- static std::list<FinishedCallback>* callback_queue;
- static TestCondVar* cond;
- static TestMutex* queue_mutex;
- static int wakeup_fd;
- static isc::datasrc::ClientListMapPtr* clients_map;
- static TestMutex* map_mutex;
- static std::list<Command> command_queue_copy;
- static std::list<FinishedCallback> callback_queue_copy;
- static TestCondVar cond_copy;
- static TestMutex queue_mutex_copy;
- // true iff the manager waited on the thread running the builder.
- static bool thread_waited;
- // If set to true by a test, TestThread::wait() throws an exception
- // exception.
- enum ExceptionFromWait { NOTHROW, THROW_UNCAUGHT_EX, THROW_OTHER };
- static ExceptionFromWait thread_throw_on_wait;
- FakeDataSrcClientsBuilder(
- std::list<Command>* command_queue,
- std::list<FinishedCallback>* callback_queue,
- TestCondVar* cond,
- TestMutex* queue_mutex,
- isc::datasrc::ClientListMapPtr* clients_map,
- TestMutex* map_mutex, int wakeup_fd)
- {
- FakeDataSrcClientsBuilder::started = false;
- FakeDataSrcClientsBuilder::command_queue = command_queue;
- FakeDataSrcClientsBuilder::callback_queue = callback_queue;
- FakeDataSrcClientsBuilder::cond = cond;
- FakeDataSrcClientsBuilder::queue_mutex = queue_mutex;
- FakeDataSrcClientsBuilder::wakeup_fd = wakeup_fd;
- FakeDataSrcClientsBuilder::clients_map = clients_map;
- FakeDataSrcClientsBuilder::map_mutex = map_mutex;
- FakeDataSrcClientsBuilder::thread_waited = false;
- FakeDataSrcClientsBuilder::thread_throw_on_wait = NOTHROW;
- }
- void run() {
- FakeDataSrcClientsBuilder::started = true;
- }
- };
- // A fake thread class that doesn't really invoke thread but simply calls
- // the given main function (synchronously). Tests can tweak the wait()
- // behavior via some static variables so it will throw some exceptions.
- class TestThread {
- public:
- TestThread(const boost::function<void()>& main) {
- main();
- }
- void wait() {
- FakeDataSrcClientsBuilder::thread_waited = true;
- switch (FakeDataSrcClientsBuilder::thread_throw_on_wait) {
- case FakeDataSrcClientsBuilder::NOTHROW:
- break;
- case FakeDataSrcClientsBuilder::THROW_UNCAUGHT_EX:
- isc_throw(util::thread::Thread::UncaughtException,
- "TestThread wait() saw an exception");
- case FakeDataSrcClientsBuilder::THROW_OTHER:
- isc_throw(Unexpected,
- "General emulated failure in TestThread wait()");
- }
- }
- };
- } // namespace datasrc_clientmgr_internal
- // Convenient shortcut
- typedef DataSrcClientsMgrBase<
- datasrc_clientmgr_internal::TestThread,
- datasrc_clientmgr_internal::FakeDataSrcClientsBuilder,
- datasrc_clientmgr_internal::TestMutex,
- datasrc_clientmgr_internal::TestCondVar> TestDataSrcClientsMgrBase;
- // A specialization of manager's "cleanup" called at the end of the
- // destructor. We use this to record the final values of some of the class
- // member variables.
- template<>
- void
- TestDataSrcClientsMgrBase::cleanup();
- template<>
- void
- TestDataSrcClientsMgrBase::reconfigureHook();
- // A (hackish) trick how to not require the IOService to be passed from the
- // tests. We can't create the io service as a member, because it would
- // get initialized too late.
- class TestDataSrcClientsMgr :
- public asiolink::IOService,
- public TestDataSrcClientsMgrBase {
- public:
- TestDataSrcClientsMgr() :
- TestDataSrcClientsMgrBase(*static_cast<asiolink::IOService*>(this))
- {}
- };
- } // namespace auth
- } // namespace isc
- #endif // TEST_DATASRC_CLIENTS_MGR_H
- // Local Variables:
- // mode: c++
- // End:
|