// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include #include #include #include #include using namespace isc::hooks; using namespace std; namespace { /// @file /// @brief Holds the CalloutHandle argument tests /// /// Additional testing of the CalloutHandle - together with the interaction /// of the LibraryHandle - is done in the handles_unittests set of tests. class CalloutHandleTest : public ::testing::Test { public: /// @brief Constructor /// /// Sets up a callout manager to be referenced by the CalloutHandle in /// these tests. (The "4" for the number of libraries in the /// CalloutManager is arbitrary - it is not used in these tests.) CalloutHandleTest() : manager_(new CalloutManager(4)) {} /// Obtain hook manager boost::shared_ptr& getCalloutManager() { return (manager_); } private: /// Callout manager accessed by this CalloutHandle. boost::shared_ptr manager_; }; // *** Argument Tests *** // // The first set of tests check that the CalloutHandle can store and retrieve // arguments. These are very similar to the LibraryHandle context tests. // Test that we can store multiple values of the same type and that they // are distinct. TEST_F(CalloutHandleTest, ArgumentDistinctSimpleType) { CalloutHandle handle(getCalloutManager()); // Store and retrieve an int (random value). int a = 42; handle.setArgument("integer1", a); EXPECT_EQ(42, a); int b = 0; handle.getArgument("integer1", b); EXPECT_EQ(42, b); // Add another integer (another random value). int c = 142; handle.setArgument("integer2", c); EXPECT_EQ(142, c); int d = 0; handle.getArgument("integer2", d); EXPECT_EQ(142, d); // Add a short (random value). short e = -81; handle.setArgument("short", e); EXPECT_EQ(-81, e); short f = 0; handle.getArgument("short", f); EXPECT_EQ(-81, f); } // Test that trying to get an unknown argument throws an exception. TEST_F(CalloutHandleTest, ArgumentUnknownName) { CalloutHandle handle(getCalloutManager()); // Set an integer int a = 42; handle.setArgument("integer1", a); EXPECT_EQ(42, a); // Check we can retrieve it int b = 0; handle.getArgument("integer1", b); EXPECT_EQ(42, b); // Check that getting an unknown name throws an exception. int c = 0; EXPECT_THROW(handle.getArgument("unknown", c), NoSuchArgument); } // Test that trying to get an argument with an incorrect type throws an // exception. TEST_F(CalloutHandleTest, ArgumentIncorrectType) { CalloutHandle handle(getCalloutManager()); // Set an integer int a = 42; handle.setArgument("integer1", a); EXPECT_EQ(42, a); // Check we can retrieve it long b = 0; EXPECT_THROW(handle.getArgument("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(CalloutHandleTest, ComplexTypes) { CalloutHandle handle(getCalloutManager()); // 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.setArgument("aleph", aleph); Beta beth(11, 22); EXPECT_EQ(11, beth.c); EXPECT_EQ(22, beth.d); handle.setArgument("beth", beth); // Ensure we can extract the data correctly. Alpha aleph2; EXPECT_EQ(0, aleph2.a); EXPECT_EQ(0, aleph2.b); handle.getArgument("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.getArgument("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.getArgument("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(CalloutHandleTest, PointerTypes) { CalloutHandle handle(getCalloutManager()); // 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.setArgument("non_const_pointer", pa); handle.setArgument("const_pointer", pcb); Alpha* pa2 = 0; handle.getArgument("non_const_pointer", pa2); EXPECT_TRUE(pa == pa2); const Beta* pcb2 = 0; handle.getArgument("const_pointer", pcb2); EXPECT_TRUE(pcb == pcb2); // Check that the "const" is protected in the context. const Alpha* pca3; EXPECT_THROW(handle.getArgument("non_const_pointer", pca3), boost::bad_any_cast); Beta* pb3; EXPECT_THROW(handle.getArgument("const_pointer", pb3), boost::bad_any_cast); } // Check that we can get the names of the arguments. TEST_F(CalloutHandleTest, ContextItemNames) { CalloutHandle handle(getCalloutManager()); vector expected_names; expected_names.push_back("faith"); handle.setArgument("faith", 42); expected_names.push_back("hope"); handle.setArgument("hope", 43); expected_names.push_back("charity"); handle.setArgument("charity", 44); // Get the names and check against the expected names. We'll sort // both arrays to simplify the checking. vector actual_names = handle.getArgumentNames(); sort(actual_names.begin(), actual_names.end()); sort(expected_names.begin(), expected_names.end()); EXPECT_TRUE(expected_names == actual_names); } // Test that we can delete an argument. TEST_F(CalloutHandleTest, DeleteArgument) { CalloutHandle handle(getCalloutManager()); int one = 1; int two = 2; int three = 3; int four = 4; int value; // Return value handle.setArgument("one", one); handle.setArgument("two", two); handle.setArgument("three", three); handle.setArgument("four", four); // Delete "one". handle.getArgument("one", value); EXPECT_EQ(1, value); handle.deleteArgument("one"); EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument); handle.getArgument("two", value); EXPECT_EQ(2, value); handle.getArgument("three", value); EXPECT_EQ(3, value); handle.getArgument("four", value); EXPECT_EQ(4, value); // Delete "three". handle.getArgument("three", value); EXPECT_EQ(3, value); handle.deleteArgument("three"); EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument); handle.getArgument("two", value); EXPECT_EQ(2, value); EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument); handle.getArgument("four", value); EXPECT_EQ(4, value); } // Test that we can delete all arguments. TEST_F(CalloutHandleTest, DeleteAllArguments) { CalloutHandle handle(getCalloutManager()); int one = 1; int two = 2; int three = 3; int four = 4; int value; // Return value // Set the arguments. The previous test verifies that this works. handle.setArgument("one", one); handle.setArgument("two", two); handle.setArgument("three", three); handle.setArgument("four", four); // Delete all arguments... handle.deleteAllArguments(); // ... and check that none are left. EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument); EXPECT_THROW(handle.getArgument("two", value), NoSuchArgument); EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument); EXPECT_THROW(handle.getArgument("four", value), NoSuchArgument); } // Test the "status" field. TEST_F(CalloutHandleTest, StatusField) { CalloutHandle handle(getCalloutManager()); // Should be continue on construction. EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, handle.getStatus()); handle.setStatus(CalloutHandle::NEXT_STEP_SKIP); EXPECT_EQ(CalloutHandle::NEXT_STEP_SKIP, handle.getStatus()); handle.setStatus(CalloutHandle::NEXT_STEP_DROP); EXPECT_EQ(CalloutHandle::NEXT_STEP_DROP, handle.getStatus()); handle.setStatus(CalloutHandle::NEXT_STEP_CONTINUE); EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, handle.getStatus()); } // Tests that ScopedCalloutHandleState object resets CalloutHandle state // during construction and destruction. TEST_F(CalloutHandleTest, scopedState) { // Create pointer to the handle to be wrapped. CalloutHandlePtr handle(new CalloutHandle(getCalloutManager())); // Set two arguments and the non-default status. int one = 1; int two = 2; int three = 3; handle->setArgument("one", one); handle->setArgument("two", two); handle->setContext("three", three); handle->setStatus(CalloutHandle::NEXT_STEP_DROP); int value = 0; EXPECT_NO_THROW(handle->getArgument("one", value)); EXPECT_NO_THROW(handle->getArgument("two", value)); EXPECT_NO_THROW(handle->getContext("three", value)); EXPECT_EQ(CalloutHandle::NEXT_STEP_DROP, handle->getStatus()); { // Wrap the callout handle with the scoped state object, which should // reset the state of the handle. ScopedCalloutHandleState scoped_state(handle); // When state is reset, all arguments should be removed and the // default status should be set. EXPECT_THROW(handle->getArgument("one", value), NoSuchArgument); EXPECT_THROW(handle->getArgument("two", value), NoSuchArgument); EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, handle->getStatus()); // Context should be intact. ASSERT_NO_THROW(handle->getContext("three", value)); EXPECT_EQ(three, value); // Set the arguments and status again prior to the destruction of // the wrapper. handle->setArgument("one", one); handle->setArgument("two", two); handle->setStatus(CalloutHandle::NEXT_STEP_DROP); EXPECT_NO_THROW(handle->getArgument("one", value)); EXPECT_NO_THROW(handle->getArgument("two", value)); EXPECT_EQ(CalloutHandle::NEXT_STEP_DROP, handle->getStatus()); } // Arguments should be gone again and the status should be set to // a default value. EXPECT_THROW(handle->getArgument("one", value), NoSuchArgument); EXPECT_THROW(handle->getArgument("two", value), NoSuchArgument); EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, handle->getStatus()); // Context should be intact. ASSERT_NO_THROW(handle->getContext("three", value)); EXPECT_EQ(three, value); } // Further tests of the "skip" flag and tests of getting the name of the // hook to which the current callout is attached is in the "handles_unittest" // module. } // Anonymous namespace