// Copyright (C) 2013 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. #include #include #include using namespace isc::util; using namespace std; // Dummy class for testing namespace isc { namespace util { class CalloutHandle {}; } } namespace { class LibraryHandleTest : public ::testing::Test { public: /// @brief Constructor /// /// Sets up an appropriate number of server hooks to pass to the /// constructed callout handle objects. LibraryHandleTest() : hooks_(new ServerHooks()) { hooks_->registerHook("alpha"); hooks_->registerHook("beta"); hooks_->registerHook("gamma"); // Also initialize the callout variables. one_count = 0; two_count = 0; callout_value = 0; } /// Obtain constructed server hooks boost::shared_ptr getServerHooks() { return (hooks_); } /// Variables for callouts test. These are public and static to allow non- /// member functions to access them, but declared as class variables to /// allow initialization every time the test starts. static int one_count; static int two_count; static int callout_value; private: boost::shared_ptr hooks_; }; // Definition of the static variables. int LibraryHandleTest::one_count = 0; int LibraryHandleTest::two_count = 0; int LibraryHandleTest::callout_value = 0; // *** Context Tests *** // // The first set of tests check that the LibraryHandle can store and retrieve // context. // Test that we can store multiple values of the same type and that they // are distinct. TEST_F(LibraryHandleTest, ContextDistinctSimpleType) { LibraryHandle handle(getServerHooks(), 1); // Store and retrieve an int (random value). int a = 42; handle.setContext("integer1", a); EXPECT_EQ(42, a); int b = 0; handle.getContext("integer1", b); EXPECT_EQ(42, b); // Add another integer (another random value). int c = 142; handle.setContext("integer2", c); EXPECT_EQ(142, c); int d = -1; handle.getContext("integer2", d); EXPECT_EQ(142, d); // Add a short (random value). short e = 81; handle.setContext("short", e); EXPECT_EQ(81, e); short f = -1; handle.getContext("short", f); EXPECT_EQ(81, f); } // Test that trying to get something with an incorrect name throws an // exception. TEST_F(LibraryHandleTest, ContextUnknownName) { LibraryHandle handle(getServerHooks(), 1); // Set an integer int a = 42; handle.setContext("integer1", a); EXPECT_EQ(42, a); // Check we can retrieve it int b = 0; handle.getContext("integer1", b); EXPECT_EQ(42, b); // Check that getting an unknown name throws an exception. int c = -1; EXPECT_THROW(handle.getContext("unknown", c), NoSuchContext); } // Test that trying to get something with an incorrect type throws an exception. TEST_F(LibraryHandleTest, ContextIncorrectType) { LibraryHandle handle(getServerHooks(), 1); // Set an integer int a = 42; handle.setContext("integer1", a); EXPECT_EQ(42, a); // Check we can retrieve it long b = 0; EXPECT_THROW(handle.getContext("integer1", b), boost::bad_any_cast); } // Now try with some very complex types. The types cannot be defined within // the function and they should contain a copy constructor. For this reason, // a simple "struct" is used. struct Alpha { int a; int b; Alpha(int first = 0, int second = 0) : a(first), b(second) {} }; struct Beta { int c; int d; Beta(int first = 0, int second = 0) : c(first), d(second) {} }; TEST_F(LibraryHandleTest, ComplexTypes) { LibraryHandle handle(getServerHooks(), 1); // Declare two variables of different (complex) types. (Note as to the // variable names: aleph and beth are the first two letters of the Hebrew // alphabet.) Alpha aleph(1, 2); EXPECT_EQ(1, aleph.a); EXPECT_EQ(2, aleph.b); handle.setContext("aleph", aleph); Beta beth(11, 22); EXPECT_EQ(11, beth.c); EXPECT_EQ(22, beth.d); handle.setContext("beth", beth); // Ensure we can extract the data correctly Alpha aleph2; EXPECT_EQ(0, aleph2.a); EXPECT_EQ(0, aleph2.b); handle.getContext("aleph", aleph2); EXPECT_EQ(1, aleph2.a); EXPECT_EQ(2, aleph2.b); Beta beth2; EXPECT_EQ(0, beth2.c); EXPECT_EQ(0, beth2.d); handle.getContext("beth", beth2); EXPECT_EQ(11, beth2.c); EXPECT_EQ(22, beth2.d); // Ensure that complex types also thrown an exception if we attempt to // get a context element of the wrong type. EXPECT_THROW(handle.getContext("aleph", beth), boost::bad_any_cast); } // Check that the context can store pointers. And also check that it respects // that a "pointer to X" is not the same as a "pointer to const X". TEST_F(LibraryHandleTest, PointerTypes) { LibraryHandle handle(getServerHooks(), 1); // Declare a couple of variables, const and non-const. Alpha aleph(5, 10); const Beta beth(15, 20); Alpha* pa = ℵ const Beta* pcb = ℶ // Check pointers can be set and retrieved OK handle.setContext("non_const_pointer", pa); handle.setContext("const_pointer", pcb); Alpha* pa2 = 0; handle.getContext("non_const_pointer", pa2); EXPECT_TRUE(pa == pa2); const Beta* pcb2 = 0; handle.getContext("const_pointer", pcb2); EXPECT_TRUE(pcb == pcb2); // Check that the "const" is protected in the context. const Alpha* pca3; EXPECT_THROW(handle.getContext("non_const_pointer", pca3), boost::bad_any_cast); Beta* pb3; EXPECT_THROW(handle.getContext("const_pointer", pb3), boost::bad_any_cast); } // *** Callout Tests *** // // The next set of tests check that callouts can be registered. // Supply callouts structured in such a way that we can determine the order // that they are called and whether they are called at all. In particular // if the callout order is: // // * one followed by two, the resulting value is 20 // * two followed by one, the resuling value is -10 // * one and two is not called, the resulting value is 10 // * two and one is not called, the resulting value is -20 // * neither called, the resulting value is 0 // // The variable xxx_count is the number of times the function has been called // in the current test. extern "C" { int one(CalloutHandle&) { ++LibraryHandleTest::one_count; if (LibraryHandleTest::callout_value == 0) { LibraryHandleTest::callout_value = 10; } else { LibraryHandleTest::callout_value = -10; } return (0); } int two(CalloutHandle&) { ++LibraryHandleTest::two_count; if (LibraryHandleTest::callout_value == 10) { LibraryHandleTest::callout_value = 20; } else { LibraryHandleTest::callout_value = -20; } return (0); } // The next function is a duplicate of "one", but returns an error status. int one_error(CalloutHandle& handle) { (void) one(handle); return (1); } }; // extern "C" // Check that we can register callouts on a particular hook. TEST_F(LibraryHandleTest, RegisterSingleCallout) { LibraryHandle handle(getServerHooks(), 1); // Register callouts for hooks alpha and see that it is registered. EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("alpha"))); handle.registerCallout("alpha", one); EXPECT_TRUE(handle.calloutsPresent(getServerHooks()->getIndex("alpha"))); // Do the same for beta (which checks that the hooks are independent). EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("beta"))); handle.registerCallout("beta", one); EXPECT_TRUE(handle.calloutsPresent(getServerHooks()->getIndex("beta"))); } // Check that we can call a single callout on a particular hook. Refer // to the above definition of the callouts "one" and "two" to understand // the expected return values. TEST_F(LibraryHandleTest, CallSingleCallout) { LibraryHandle handle(getServerHooks(), 1); // Register callout for hook alpha... EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("alpha"))); handle.registerCallout("alpha", one); EXPECT_TRUE(handle.calloutsPresent(getServerHooks()->getIndex("alpha"))); // Call it. EXPECT_EQ(0, LibraryHandleTest::one_count); EXPECT_EQ(0, LibraryHandleTest::two_count); EXPECT_EQ(0, LibraryHandleTest::callout_value); int index = getServerHooks()->getIndex("alpha"); CalloutHandle dummy; int status = handle.callCallouts(index, dummy); EXPECT_EQ(0, status); EXPECT_EQ(1, LibraryHandleTest::one_count); EXPECT_EQ(0, LibraryHandleTest::two_count); EXPECT_EQ(10, LibraryHandleTest::callout_value); } // Check that we can register two callouts for a hook and that they are called // in order. TEST_F(LibraryHandleTest, TwoCallouts) { LibraryHandle handle(getServerHooks(), 1); // Register two callouts for hook alpha... handle.registerCallout("alpha", one); handle.registerCallout("alpha", two); // ... and call them. EXPECT_EQ(0, LibraryHandleTest::one_count); EXPECT_EQ(0, LibraryHandleTest::two_count); EXPECT_EQ(0, LibraryHandleTest::callout_value); int index = getServerHooks()->getIndex("alpha"); CalloutHandle dummy; int status = handle.callCallouts(index, dummy); EXPECT_EQ(0, status); EXPECT_EQ(1, LibraryHandleTest::one_count); EXPECT_EQ(1, LibraryHandleTest::two_count); EXPECT_EQ(20, LibraryHandleTest::callout_value); } // Check that we can register two callouts for a hook and that the second is not // called if the first returns a non-zero status. TEST_F(LibraryHandleTest, TwoCalloutsWithError) { LibraryHandle handle(getServerHooks(), 1); // Register callout for hook alpha... handle.registerCallout("alpha", one_error); handle.registerCallout("alpha", two); // Call them. EXPECT_EQ(0, LibraryHandleTest::one_count); EXPECT_EQ(0, LibraryHandleTest::two_count); EXPECT_EQ(0, LibraryHandleTest::callout_value); int index = getServerHooks()->getIndex("alpha"); CalloutHandle dummy; int status = handle.callCallouts(index, dummy); EXPECT_EQ(1, status); EXPECT_EQ(1, LibraryHandleTest::one_count); EXPECT_EQ(0, LibraryHandleTest::two_count); EXPECT_EQ(10, LibraryHandleTest::callout_value); } // Check that a callout can be registered more than once. TEST_F(LibraryHandleTest, MultipleRegistration) { LibraryHandle handle(getServerHooks(), 1); // Register callouts for hook alpha... handle.registerCallout("alpha", one); handle.registerCallout("alpha", two); handle.registerCallout("alpha", one); // Call them. EXPECT_EQ(0, LibraryHandleTest::one_count); EXPECT_EQ(0, LibraryHandleTest::two_count); EXPECT_EQ(0, LibraryHandleTest::callout_value); int index = getServerHooks()->getIndex("alpha"); CalloutHandle dummy; int status = handle.callCallouts(index, dummy); EXPECT_EQ(0, status); EXPECT_EQ(2, LibraryHandleTest::one_count); EXPECT_EQ(1, LibraryHandleTest::two_count); EXPECT_EQ(-10, LibraryHandleTest::callout_value); } // Check that a callout can be deregistered. TEST_F(LibraryHandleTest, Degreister) { LibraryHandle handle(getServerHooks(), 1); // Register callouts for hook alpha... handle.registerCallout("alpha", one); handle.registerCallout("alpha", two); handle.registerCallout("alpha", one); // Get rid of all the "one" callbacks. handle.deregisterCallout("alpha", one); // Call it. EXPECT_EQ(0, LibraryHandleTest::one_count); EXPECT_EQ(0, LibraryHandleTest::two_count); EXPECT_EQ(0, LibraryHandleTest::callout_value); int index = getServerHooks()->getIndex("alpha"); CalloutHandle dummy; int status = handle.callCallouts(index, dummy); EXPECT_EQ(0, status); EXPECT_EQ(0, LibraryHandleTest::one_count); EXPECT_EQ(1, LibraryHandleTest::two_count); EXPECT_EQ(-20, LibraryHandleTest::callout_value); } // Check that all callouts can be deregistered. TEST_F(LibraryHandleTest, DeregisterAll) { LibraryHandle handle(getServerHooks(), 1); // Register callouts for hook alpha... EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("alpha"))); handle.registerCallout("alpha", one); handle.registerCallout("alpha", two); EXPECT_TRUE(handle.calloutsPresent(getServerHooks()->getIndex("alpha"))); // ... and remove them. handle.deregisterAll("alpha"); EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("alpha"))); } } // Anonymous namespace