BIND 10 trac2974, updated. 923462dbabee6917d6693e62c80bd0f7c8fd980b [2974] All functions finally written and tested

BIND 10 source code commits bind10-changes at lists.isc.org
Wed May 29 18:54:50 UTC 2013


The branch, trac2974 has been updated
       via  923462dbabee6917d6693e62c80bd0f7c8fd980b (commit)
       via  e686a5339d6927d4ef7bf35a8e5d9c2ddfa98149 (commit)
      from  ba0004b0092cf2d7652b9a66212774d19f91517f (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 923462dbabee6917d6693e62c80bd0f7c8fd980b
Author: Stephen Morris <stephen at isc.org>
Date:   Wed May 29 19:54:19 2013 +0100

    [2974] All functions finally written and tested

commit e686a5339d6927d4ef7bf35a8e5d9c2ddfa98149
Author: Stephen Morris <stephen at isc.org>
Date:   Wed May 29 18:26:34 2013 +0100

    [2974] Added per-library packet context.

-----------------------------------------------------------------------

Summary of changes:
 src/lib/util/hooks/callout_handle.cc   |   87 ++++-
 src/lib/util/hooks/callout_handle.h    |  214 ++++++++++-
 src/lib/util/hooks/library_handle.cc   |   13 +
 src/lib/util/hooks/library_handle.h    |   33 +-
 src/lib/util/tests/Makefile.am         |    1 +
 src/lib/util/tests/handles_unittest.cc |  618 ++++++++++++++++++++++++++++++++
 6 files changed, 941 insertions(+), 25 deletions(-)
 create mode 100644 src/lib/util/tests/handles_unittest.cc

-----------------------------------------------------------------------
diff --git a/src/lib/util/hooks/callout_handle.cc b/src/lib/util/hooks/callout_handle.cc
index 57d1b6a..60b86d3 100644
--- a/src/lib/util/hooks/callout_handle.cc
+++ b/src/lib/util/hooks/callout_handle.cc
@@ -13,10 +13,10 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <util/hooks/callout_handle.h>
+#include <util/hooks/library_handle.h>
 
-#include <algorithm>
-#include <functional>
 #include <string>
+#include <utility>
 #include <vector>
 
 using namespace std;
@@ -25,14 +25,91 @@ using namespace isc::util;
 namespace isc {
 namespace util {
 
-// return the name of all context items.
+// Return the name of all argument items.
 
 vector<string>
 CalloutHandle::getArgumentNames() const {
 
     vector<string> names;
-    ArgumentCollection::const_iterator i;
-    for (i = arguments_.begin(); i != arguments_.end(); ++i) {
+    for (ElementCollection::const_iterator i = arguments_.begin();
+         i != arguments_.end(); ++i) {
+        names.push_back(i->first);
+    }
+
+    return (names);
+}
+
+// Return the "current" library handle.
+
+LibraryHandle&
+CalloutHandle::getLibraryHandle() const {
+    boost::shared_ptr<LibraryHandle> handle =
+        library_collection_->getLibraryHandle();
+
+    // Return refernce to this library handle.  This remains valid even
+    // after this method returns, because this object maintains a shared
+    // pointer to the LibraryHandleCollection, which in turn maintains
+    // a shared pointer to the LibraryHandle in question.
+
+    return (*handle);
+}
+
+// Check the current library index.
+
+int
+CalloutHandle::getLibraryIndex() const {
+    int curidx = library_collection_->getLibraryIndex();
+    if (curidx < 0) {
+        isc_throw(InvalidIndex, "current library handle index is not valid");
+    }
+
+    return (curidx);
+}
+
+// Return the context for the currently pointed-to library.  This version is
+// used by the "setContext()" method and creates a context for the current
+// library if it does not exist.
+
+CalloutHandle::ElementCollection&
+CalloutHandle::getContextForLibrary() {
+
+    int libindex = getLibraryIndex();
+
+    // Access a reference to the element collection for the given index,
+    // creating a new element collection if necessary, and return it.
+    return (context_collection_[libindex]);
+}
+
+// The "const" version of the above, used by the "getContext()" method.  If
+// the context for the current library doesn't exist, throw a
+// "NoSuchCalloutContext" exception.
+
+const CalloutHandle::ElementCollection&
+CalloutHandle::getContextForLibrary() const {
+
+    int libindex = getLibraryIndex();
+    ContextCollection::const_iterator libcontext =
+        context_collection_.find(libindex);
+    if (libcontext == context_collection_.end()) {
+        isc_throw(NoSuchCalloutContext, "unable to find callout context "
+                  "associated with the current library handle");
+    }
+
+    // Return a reference to the context's element collection.
+    return (libcontext->second);
+}
+
+// Return the name of all items in the context associated with the current]
+// library.
+
+vector<string>
+CalloutHandle::getContextNames() const {
+
+    vector<string> names;
+
+    const ElementCollection& elements = getContextForLibrary();
+    for (ElementCollection::const_iterator i = elements.begin();
+         i != elements.end(); ++i) {
         names.push_back(i->first);
     }
 
diff --git a/src/lib/util/hooks/callout_handle.h b/src/lib/util/hooks/callout_handle.h
index aea5ad8..59df050 100644
--- a/src/lib/util/hooks/callout_handle.h
+++ b/src/lib/util/hooks/callout_handle.h
@@ -36,27 +36,82 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-// Forward declaration of the handle collection class.
+/// @brief No such callout context item
+///
+/// Thrown if an attempt is made to get an item of data from this callout's
+/// context.
+class NoSuchCalloutContext : public Exception {
+public:
+    NoSuchCalloutContext(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+//
+
+// Forward declaration of the library handle and related collection classes.
+class LibraryHandle;
 class LibraryHandleCollection;
 
-/// @brief Per-Packet callout handle
+/// @brief Per-packet callout handle
 ///
 /// An object of this class is associated with every packet (or request)
 /// processed by the server.  It forms the principle means of passing 
 /// data between the server and the user-library callouts.
+///
+/// The class allows access to the following information:
+///
+/// - Arguments.  When the callouts associated with a hook are called, they
+///   are passed information by the server (and can return information to it)
+///   through name/value pairs.  Each of these pairs is an argument and the
+///   information is accessed through the {get,get}Argument() methods.
+///
+/// - Per-packet context.  Each packet has a context associated with it, this
+///   context being a per-library context.  In other words, As a packet passes
+///   through the callouts associated with a given library, the callouts can
+///   associate and retrieve information with the packet.  The per-library
+///   nature of the context means that the callouts within a given library can
+///   pass packet-specific information between one another, they cannot pass
+///   information to callous within another library.  Typically such context
+///   is created in the "context_create" callout and destroyed in the
+///   "context_destroy" callout.  The information is accessed through the
+///   {get,set}Context() methods.
+///
+/// - Per-library context.  Each library has a context associated with it that
+///   is independent of the packet.  All callouts in the library share the
+///   information in the context, regardless of the packet being processed.
+///   The context is typically createed in the library's "load()" method and
+///   destroyed in its "unload()" method.  The information is available by
+///   obtaining the handle to the library through this class's
+///   getLibraryHandle() method, then calling {get,set}Context() on the
+///   object returned.
 
 class CalloutHandle {
-private:
-    /// Typedef to allow abbreviation of iterator specification in methods
-    typedef std::map<std::string, boost::any> ArgumentCollection;
-
 public:
 
+    /// Typedef to allow abbreviation of iterator specification in methods.
+    /// The std::string is the argument name and the "boost::any" is the
+    /// corresponding value associated with it.
+    typedef std::map<std::string, boost::any> ElementCollection;
+
+    /// Typedef to allow abbreviations in specifications when accessing
+    /// context.  The ElementCollection is the name/value collection for
+    /// a particular context.  The "int" corresponds to the index of an
+    /// associated library handle - there is a 1:1 correspondence between
+    /// library handles and a name.value collection.
+    ///
+    /// The collection of contexts is stored in a map, as not every library
+    /// will require creating a context associated with each packet.  In
+    /// addition, the structure is more flexible in that the size does not
+    /// need to be set when the CalloutHandle is constructed.
+    typedef std::map<int, ElementCollection> ContextCollection;
+
+
     /// @brief Constructor
     ///
     /// @param manager Pointer to the collection of library handles.
     CalloutHandle(boost::shared_ptr<LibraryHandleCollection>& collection)
-        : arguments_(), collection_(collection), skip_(false)
+        : arguments_(), context_collection_(), library_collection_(collection),
+          skip_(false)
     {}
 
     /// @brief Set argument
@@ -85,7 +140,7 @@ public:
     ///        but the type of the element is not that expected
     template <typename T>
     void getArgument(const std::string& name, T& value) const {
-        ArgumentCollection::const_iterator element_ptr = arguments_.find(name);
+        ElementCollection::const_iterator element_ptr = arguments_.find(name);
         if (element_ptr == arguments_.end()) {
             isc_throw(NoSuchArgument, "unable to find argument with name " <<
                       name);
@@ -107,8 +162,8 @@ public:
     /// Deletes an argument of the given name.  If an argument of that name
     /// does not exist, the method is a no-op.
     ///
-    /// N.B. If the element is a raw pointer, the pointed-to data is
-    /// NOT deleted by this.
+    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
+    /// by this method.
     ///
     /// @param name Name of the element in the argument list to set.
     void deleteArgument(const std::string& name) {
@@ -118,6 +173,9 @@ public:
     /// @brief Delete all arguments
     ///
     /// Deletes all arguments associated with this context.
+    ///
+    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
+    /// deleted by this method.
     void deleteAllArguments() {
         arguments_.clear();
     }
@@ -125,8 +183,8 @@ public:
     /// @brief Set skip flag
     ///
     /// Sets the "skip" variable in the callout handle.  This variable is
-    /// interrogated by the server to see if the remaining callouts should be
-    /// bypassed.
+    /// interrogated by the server to see if the remaining callouts associated
+    /// with the current hook should be bypassed.
     ///
     /// @param skip New value of the "skip" flag.
     void setSkip(bool skip) {
@@ -142,14 +200,142 @@ public:
         return (skip_);
     }
 
+    /// @brief Access current library handle
+    ///
+    /// Returns a reference to the current library handle.  This function is
+    /// only available when called by a callout (which in turn is called
+    /// through the "callCallouts" method), as it is only then that the current
+    /// library index is valid.  A callout would use this method to get to
+    /// the context associated with the library in which it resides.
+    ///
+    /// @return Reference to the current library handle
+    ///
+    /// @throw InvalidIndex thrown if this method is called outside of the
+    ///        "callCallouts() method. (Exception is so-named because the
+    ///        index used to access the library handle in the collection
+    ///        is not valid at that point.)
+    LibraryHandle& getLibraryHandle() const;
+
+    /// @brief Set context
+    ///
+    /// Sets an element in the context associated with the current library.  If
+    /// an element of the name is already present, it is replaced.
+    ///
+    /// @param name Name of the element in the context to set
+    /// @param value Value to set
+    template <typename T>
+    void setContext(const std::string& name, T value) {
+        getContextForLibrary()[name] = value;
+    }
+
+    /// @brief Get context
+    ///
+    /// Gets an element from the context associated with the current library.
+    ///
+    /// @param name Name of the element in the context to get.
+    /// @param value [out] Value to set.  The type of "value" is important:
+    ///        it must match the type of the value set.
+    ///
+    /// @throw NoSuchCalloutContext Thrown if no context element with the name
+    ///        "name" is present.
+    /// @throw boost::bad_any_cast Thrown if the context element is present,
+    ///        but the type of the element is not that expected
+    template <typename T>
+    void getContext(const std::string& name, T& value) const {
+        const ElementCollection& lib_context = getContextForLibrary();
+
+        ElementCollection::const_iterator element_ptr = lib_context.find(name);
+        if (element_ptr == lib_context.end()) {
+            isc_throw(NoSuchCalloutContext, "unable to find callout context "
+                      "item " << name << " in the context associated with "
+                      "current library handle");
+        }
+
+        value = boost::any_cast<T>(element_ptr->second);
+    }
+    
+    /// @brief Get context names
+    ///
+    /// Returns a vector holding the names of items in the context associated
+    /// with the current library.
+    ///
+    /// @return Vector of strings reflecting argument names
+    std::vector<std::string> getContextNames() const;
+
+    /// @brief Delete context element
+    ///
+    /// Deletes an item of the given name from the context associated with the
+    /// current library.  If an item  of that name does not exist, the method is
+    /// a no-op.
+    ///
+    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
+    /// by this.
+    ///
+    /// @param name Name of the element in the argument list to set.
+    void deleteContext(const std::string& name) {
+        static_cast<void>(getContextForLibrary().erase(name));
+    }
+
+    /// @brief Delete all context items
+    ///
+    /// Deletes all items from the context associated with the current library.
+    ///
+    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
+    /// deleted by this.
+    void deleteAllContext() {
+        getContextForLibrary().clear();
+    }
+
 
 private:
+    /// @brief Check index
+    ///
+    /// Gets the current library index, throwing an exception if it is not set
+    /// or is invalid for the current library collection.
+    ///
+    /// @return Current library index, valid for this library collection.
+    int getLibraryIndex() const;
+
+    /// @brief Return reference to context for current library
+    ///
+    /// Called by all context-accessing functions, this checks if the current
+    /// library index is valid (throwing an exception if not).  If it is, it
+    /// returns a reference to the appropriate context, creating one if it does
+    /// not exist. (The last task allows the list of library handles to grow
+    /// dynamically - although only if handles are appended to the end of the
+    /// library handle collection.)
+    ///
+    /// @return Reference to the collection of name/value pairs associated
+    ///         with the current library.
+    ElementCollection& getContextForLibrary();
+
+    /// @brief Return reference to context for current library (const version)
+    ///
+    /// Called by all context-accessing functions, this checks if the current
+    /// library index is valid (throwing an exception if not).  If it is, it
+    /// returns a reference to the appropriate context, creating one if it does
+    /// not exist. (The last task allows the list of library handles to grow
+    /// dynamically - although only if handles are appended to the end of the
+    /// library handle collection.)
+    ///
+    /// @return Reference to the collection of name/value pairs associated
+    ///         with the current library.
+    ///
+    /// @throw NoSuchCalloutContext Thrown if there is no ElementCollection
+    ///        associated with the current library.
+    const ElementCollection& getContextForLibrary() const;
+
+    // Member variables
+
     /// Collection of arguments passed to the callouts
-    ArgumentCollection arguments_;
+    ElementCollection arguments_;
+
+    /// Context collection - there is one entry per library context.
+    ContextCollection context_collection_;
 
     /// Library handle collection, used to obtain the correct library handle
     /// during a call to a callout.
-    boost::shared_ptr<LibraryHandleCollection> collection_;
+    boost::shared_ptr<LibraryHandleCollection> library_collection_;
 
     /// "Skip" flag, indicating if the caller should bypass remaining callouts.
     bool skip_;
diff --git a/src/lib/util/hooks/library_handle.cc b/src/lib/util/hooks/library_handle.cc
index 261d0f4..6947a6f 100644
--- a/src/lib/util/hooks/library_handle.cc
+++ b/src/lib/util/hooks/library_handle.cc
@@ -153,6 +153,19 @@ LibraryHandle::getContextNames() const {
 
 // LibraryHandleCollection methods.
 
+// Return pointer to the current library handle.
+
+boost::shared_ptr<LibraryHandle>
+LibraryHandleCollection::getLibraryHandle() const {
+    if ((curidx_ < 0) || (curidx_ >= handles_.size())) {
+        isc_throw(InvalidIndex, "current library handle index of (" <<
+                  curidx_ << ") is not valid for the library handle vector "
+                  "of size " << handles_.size());
+    }
+
+    return (handles_[curidx_]);
+}
+
 // Check if a callout is present on a hook in any of the libraries.
 
 bool
diff --git a/src/lib/util/hooks/library_handle.h b/src/lib/util/hooks/library_handle.h
index acca0d4..22091ad 100644
--- a/src/lib/util/hooks/library_handle.h
+++ b/src/lib/util/hooks/library_handle.h
@@ -47,6 +47,18 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+/// @brief Invalid index
+///
+/// Thrown if an attempt is made to obtain a library handle but the current
+/// library handle index is invalid.  This will occur if the method
+/// LibraryHandleCollection::getLibraryHandle() is called outside of a callout.
+
+class InvalidIndex : public Exception {
+public:
+    InvalidIndex(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 // Forward declaration for CalloutHandle
 class CalloutHandle;
 
@@ -97,6 +109,7 @@ public:
     }
 
     /// @brief Get context
+
     ///
     /// Sets an element in the library context.  If the name does not exist,
     /// a "NoSuchContext" exception is thrown.
@@ -113,7 +126,7 @@ public:
     void getContext(const std::string& name, T& value) const {
         ContextCollection::const_iterator element_ptr = context_.find(name);
         if (element_ptr == context_.end()) {
-            isc_throw(NoSuchContext, "unable to find library context datum " <<
+            isc_throw(NoSuchContext, "unable to find library context item " <<
                       name << " in library handle");
         }
 
@@ -187,7 +200,6 @@ public:
     /// @throw NoSuchHook Thrown if the hook name is unrecognised.
     void deregisterAll(const std::string& name);
 
-
     /// @brief Checks if callouts are present
     ///
     /// @param index Hook index for which callouts are checked.
@@ -285,6 +297,18 @@ public:
         handles_.push_back(handle);
     }
 
+    /// @brief Return current library index
+    ///
+    /// Returns the value of the "current library index".  Although a callout
+    /// callout can retrieve this information, it is of limited use: the
+    /// value is intended for use by the CalloutHandle object in order to
+    /// access the per-library context.
+    ///
+    /// @return Current library index value
+    int getLibraryIndex() const {
+        return (curidx_);
+    }
+
     /// @brief Get current library handle
     ///
     /// Returns a pointer to the current library handle.  This method can
@@ -294,10 +318,7 @@ public:
     ///
     /// @return Pointer to current library handle. This is the handle for
     ///         which a callout is being called.
-    boost::shared_ptr<LibraryHandle> getLibraryHandle() const {
-        return (boost::shared_ptr<LibraryHandle>());
-        /// @todo Return the appropriate handle
-    }
+    boost::shared_ptr<LibraryHandle> getLibraryHandle() const;
 
     /// @brief Checks if callouts are present
     ///
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index 44dcb87..c968e56 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -30,6 +30,7 @@ run_unittests_SOURCES += fd_share_tests.cc
 run_unittests_SOURCES += fd_tests.cc
 run_unittests_SOURCES += filename_unittest.cc
 run_unittests_SOURCES += hex_unittest.cc
+run_unittests_SOURCES += handles_unittest.cc
 run_unittests_SOURCES += io_utilities_unittest.cc
 run_unittests_SOURCES += library_handle_unittest.cc
 run_unittests_SOURCES += library_handle_collection_unittest.cc
diff --git a/src/lib/util/tests/handles_unittest.cc b/src/lib/util/tests/handles_unittest.cc
new file mode 100644
index 0000000..94ac627
--- /dev/null
+++ b/src/lib/util/tests/handles_unittest.cc
@@ -0,0 +1,618 @@
+// 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 <util/hooks/callout_handle.h>
+#include <util/hooks/library_handle.h>
+#include <util/hooks/server_hooks.h>
+
+#include <boost/lexical_cast.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+
+/// @file
+/// CalloutHandle/LibraryCalloutHandle interaction tests
+///
+/// This file holds unit tests checking the interaction between the
+/// CalloutHandle and LibraryCalloutHandle[Collection] classes.  In particular,
+/// they check that:
+///
+/// - A CalloutHandle's context is shared between callouts from the same
+///   library, but there is a separate context for each library.
+///
+/// - The LibraryHandle retrieved by the CalloutHandle is the same for each
+///   callout in the library, but different for different libraries.
+///
+/// - The various methods manipulating the items in the CalloutHandle's context
+///   work correctly.
+///
+/// Some minor interactions between the two classes are checked in the unit
+/// tests for each class (mainly the use of the "skip" flag).
+
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+// The next set of functions define the callouts used by the tests.  They
+// manipulate the data in such a way that callouts called - and the order in
+// which they were called can be determined.  The functions also check that
+// the "callout context" and "library context" data areas are separate.
+//
+// Three libraries are assumed, and each supplies four callouts.  All callouts
+// manipulate four context elements - two in the CalloutHandle and two in the
+// LibraryHandle, the elements being called "string" and "int" (which describe
+// the type of data manipulated).
+//
+// For the string item, each callout shifts data to the left and inserts its own
+// data.  Data is a string of the form "nmwc", where "n" is the number of the
+// library, "m" is the callout number and "w" is an indication of what is being
+// altered (library context ["x"] or callout context ["c"]) and "y" is the
+// indication of what callout was passed as an argument ("x" or "b"). ("x" is
+// used instead of "l" to indicate that library context is being altered since
+// in the results, these single characters will be mixed with digits and "l"
+// " looks too much like "1".)  Hence we have:
+//
+// - "xa" if library context is being altered from a callout made with the
+//        first callout handle passed as argument.
+// - "xb" if library context is being altered from a callout made with the
+//        second callout handle passed as argument.
+// - "ca" if the first callout handle's context is being manipulated.
+// - "cb" if the second callout handle's context is being manipulated.
+//
+// For simplicity, and to cut down the number of functions actually written,
+// the callout indicator ("a" or "b") ) used in the in the CalloutHandle
+// functions is passed via a CalloutArgument.  The argument is named "string":
+// use of a name the same as that of one of the context elements serves as a
+// check that the argument name space and argument context space are separate.
+//
+// For integer data, the value starts at zero and an increment added on each
+// call.  This increment is equal to:
+//
+// 1000 * library number + 100 * callout_number + 10 * lib/callout + indicator
+//
+// where "lib/callout" is 1 for updating a library context and 2 for updating
+// a callout context, and "indicator" is 1 for callout a and 2 for callout b.
+// This gives a direct correspondence between the characters appended to the
+// string context item and the amount by which the integer context item is
+// incremented.  For example, the string "21cb" corresponds to a value of 2122.
+//
+// Although this gives less information than the string value, the reasons for
+// using it are:
+//
+// - It is a separate item in the context, so checks that the context can
+//   handle multiple items.
+// - It provides an item that can be deleted by the context deletion
+//   methods.
+
+// Values set in the LibraryHandle context.  There are three libraries, so
+// there are three sets of library context.  To avoid a static initialization
+// fiasco, encapsulate these in a function.
+
+std::string& resultLibraryString(int index) {
+    static std::string result_library_string[3];
+    return (result_library_string[index]);
+}
+
+int& resultLibraryInt(int index) {
+    static int result_library_int[3];
+    return (result_library_int[index]);
+}
+
+// Values set in the CalloutHandle context.  There are three libraries, so
+// there are three contexts for the callout, one for each library.
+
+std::string& resultCalloutString(int index) {
+    static std::string result_callout_string[3];
+    return (result_callout_string[index]);
+}
+
+int& resultCalloutInt(int index) {
+    static int result_callout_int[3];
+    return (result_callout_int[index]);
+}
+
+// A simple function to zero the results.
+
+static void zero_results() {
+    for (int i = 0; i < 3; ++i) {
+        resultLibraryString(i) = "";
+        resultLibraryInt(i) = 0;
+        resultCalloutString(i) = "";
+        resultCalloutInt(i) = 0;
+    }
+}
+
+
+// Library callouts.
+
+// Common code for setting the callout and library context values.
+
+int
+execute(CalloutHandle& callout_handle, int library_num, int callout_num) {
+
+    // Obtain the callout handle indicator.
+    string indicator;
+    callout_handle.getArgument("string", indicator);
+
+    // Create the basic data to be appended to the context value.
+    int idata = 1000 * library_num + 100 * callout_num;
+    string sdata = boost::lexical_cast<string>(10 * library_num + callout_num);
+
+    // Get the library context data.  As the context will not exist on the
+    // first call, catch the exception and create it. (In real life, the context
+    // should have been created by the libraries' "load()" function.)
+    string string_value = "";
+    try {
+        callout_handle.getLibraryHandle().getContext("string", string_value);
+    } catch (const NoSuchContext&) {
+        string_value = "";
+    }
+
+    int int_value = 0;
+    try {
+        callout_handle.getLibraryHandle().getContext("int", int_value);
+    } catch (const NoSuchContext&) {
+        int_value = 0;
+    }
+
+    // Update the context value with the library/callout indication (and the
+    // suffix "x" to denote library) and set it.
+    string_value += (sdata + string("x") + indicator);
+    callout_handle.getLibraryHandle().setContext("string", string_value);
+
+    int_value += (idata + 10 + (indicator == "a" ? 1 : 2));
+    callout_handle.getLibraryHandle().setContext("int", int_value);
+
+    // Get the context data. As before, this will not exist for the first
+    // callout called. (In real life, the library should create it when the
+    // "context_create" hook gets called before any packet processing takes
+    // place.)
+    string_value = "";
+    try {
+        callout_handle.getContext("string", string_value);
+    } catch (const NoSuchCalloutContext&) {
+        string_value = "";
+    }
+
+    int_value = 0;
+    try {
+        callout_handle.getContext("int", int_value);
+    } catch (const NoSuchCalloutContext&) {
+        int_value = 0;
+    }
+
+    // Update the values and set them.
+    string_value += (sdata + string("c") + indicator);
+    callout_handle.setContext("string", string_value);
+
+    int_value += (idata + 20 + (indicator == "a" ? 1 : 2));
+    callout_handle.setContext("int", int_value);
+
+    return (0);
+}
+
+// The following functions are the actual callouts - ther name is of the
+// form "callout_<library number>_<callout number>"
+
+int
+callout11(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 1));
+}
+
+int
+callout12(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 2));
+}
+
+int
+callout13(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 3));
+}
+
+int
+callout21(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 1));
+}
+
+int
+callout22(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 2));
+}
+
+int
+callout23(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 3));
+}
+
+int
+callout31(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 1));
+}
+
+int
+callout32(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 2));
+}
+
+int
+callout33(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 3));
+}
+
+// Common callout code for the fourth hook (which makes the data available for
+// checking).  It copies the library and callout context data to the global
+// variables.
+
+int printExecute(CalloutHandle& callout_handle, int library_num) {
+
+    // Print per-library context values.
+    callout_handle.getLibraryHandle()
+                  .getContext("string", resultLibraryString(library_num - 1));
+    callout_handle.getLibraryHandle()
+                  .getContext("int", resultLibraryInt(library_num - 1));
+
+
+    // Print callout context.
+    callout_handle.getContext("string", resultCalloutString(library_num - 1));
+    callout_handle.getContext("int", resultCalloutInt(library_num - 1));
+
+    return (0);
+}
+
+// These are the actual callouts.
+
+int
+print1(CalloutHandle& callout_handle) {
+    return (printExecute(callout_handle, 1));
+}
+
+int
+print2(CalloutHandle& callout_handle) {
+    return (printExecute(callout_handle, 2));
+}
+
+int
+print3(CalloutHandle& callout_handle) {
+    return (printExecute(callout_handle, 3));
+}
+
+// This test checks the many-faced nature of the context for both the
+// CalloutContext and the LibraryContext.
+
+TEST(HandlesTest, ContextAccessCheck) {
+    // Create the LibraryHandleCollection with a set of four callouts
+    // (the test does not use the ContextCreate and ContextDestroy callouts.)
+
+    boost::shared_ptr<ServerHooks> server_hooks(new ServerHooks());
+    const int one_index = server_hooks->registerHook("one");
+    const int two_index = server_hooks->registerHook("two");
+    const int three_index = server_hooks->registerHook("three");
+    const int four_index = server_hooks->registerHook("four");
+
+    // Create the library handle collection and the library handles.
+    boost::shared_ptr<LibraryHandleCollection>
+        collection(new LibraryHandleCollection());
+
+    boost::shared_ptr<LibraryHandle> handle(new LibraryHandle(server_hooks));
+    handle->registerCallout("one", callout11);
+    handle->registerCallout("two", callout12);
+    handle->registerCallout("three", callout13);
+    handle->registerCallout("four", print1);
+    collection->addLibraryHandle(handle);
+
+    handle.reset(new LibraryHandle(server_hooks));
+    handle->registerCallout("one", callout21);
+    handle->registerCallout("two", callout22);
+    handle->registerCallout("three", callout23);
+    handle->registerCallout("four", print2);
+    collection->addLibraryHandle(handle);
+
+    handle.reset(new LibraryHandle(server_hooks));
+    handle->registerCallout("one", callout31);
+    handle->registerCallout("two", callout32);
+    handle->registerCallout("three", callout33);
+    handle->registerCallout("four", print3);
+    collection->addLibraryHandle(handle);
+
+    // Create the callout handles and distinguish them by setting the "long"
+    // argument.
+    CalloutHandle callout_handle_a(collection);
+    callout_handle_a.setArgument("string", string("a"));
+
+    CalloutHandle callout_handle_b(collection);
+    callout_handle_b.setArgument("string", string("b"));
+
+    // Now call the callouts attached to the first three hooks.  Each hook is
+    // called twice (once for each callout handle) before the next hook is
+    // called.
+    collection->callCallouts(one_index, callout_handle_a);
+    collection->callCallouts(one_index, callout_handle_b);
+    collection->callCallouts(two_index, callout_handle_a);
+    collection->callCallouts(two_index, callout_handle_b);
+    collection->callCallouts(three_index, callout_handle_a);
+    collection->callCallouts(three_index, callout_handle_b);
+
+    // Get the results for each callout.  Explicitly zero the variables before
+    // getting the results so we are certain that the values are the results
+    // of the callouts.
+
+    zero_results();
+    collection->callCallouts(four_index, callout_handle_a);
+
+    // To explain the expected library context results:
+    //
+    // The first callCallouts() call above calls the callouts for hook "one"
+    // with callout handle "a".  This calls the callouts attached to hook "one"
+    // from library 1, then those attached to hook "one" from library 2, then
+    // from library 3.  The callout in library 1 appends "11xa" to the first
+    // library's context. The callout in library 2 appends "21xa" to that
+    // library's context.  Finally, the third library's context gets "31xa"
+    // appended to it.
+    //
+    // The next callCallouts() call repeats the calls to the callouts attached
+    // to hook "one", which result in "11xb", "21xb", "31xb" being appended to
+    // the context of libraries 1, 2, and 3 respectively.
+    //
+    // The expected integer values can be found by summing up the values
+    // corresponding to the elements of the strings.
+    //
+    // The process is then repeated for hooks "two" and "three", leading to
+    // the expected context values listed below.
+
+    EXPECT_EQ("11xa11xb12xa12xb13xa13xb", resultLibraryString(0));
+    EXPECT_EQ("21xa21xb22xa22xb23xa23xb", resultLibraryString(1));
+    EXPECT_EQ("31xa31xb32xa32xb33xa33xb", resultLibraryString(2));
+
+    EXPECT_EQ((1111 + 1112 + 1211 + 1212 + 1311 + 1312), resultLibraryInt(0));
+    EXPECT_EQ((2111 + 2112 + 2211 + 2212 + 2311 + 2312), resultLibraryInt(1));
+    EXPECT_EQ((3111 + 3112 + 3211 + 3212 + 3311 + 3312), resultLibraryInt(2));
+
+    // To explain the expected callout context results.
+    //
+    // The callout handle maintains a separate context for each library.  When
+    // the first call to callCallouts() is made, "11ca" gets appended to
+    // the context for library 1 maintained by by the callout handle, "21ca"
+    // gets appended to the context maintained for library 2, and "31ca" to
+    // the context maintained for library 3.
+    //
+    // The next call to callCallouts() calls the same callouts but for a
+    // different callout handle.  It also maintains three contexts (one for
+    // each library) and they will get "11cb", "21cb", "31cb" appended to
+    // them.  These don't affect the contexts maintained  by callout handle a.
+    //
+    // The process is then repeated for hooks "two" and "three", which append
+    // "12ca", "22ca" and "32ca" for hook "two" and "31ca", "32ca" and "33ca"
+    // for hook "three".
+    //
+    // At this point, we have only called the "print" function for callout
+    // handle "a", so the following results are checking the context values
+    // maintained in that callout handle.
+
+    EXPECT_EQ("11ca12ca13ca", resultCalloutString(0));
+    EXPECT_EQ("21ca22ca23ca", resultCalloutString(1));
+    EXPECT_EQ("31ca32ca33ca", resultCalloutString(2));
+
+    EXPECT_EQ((1121 + 1221 + 1321), resultCalloutInt(0));
+    EXPECT_EQ((2121 + 2221 + 2321), resultCalloutInt(1));
+    EXPECT_EQ((3121 + 3221 + 3321), resultCalloutInt(2));
+
+    // Repeat the checks for callout b.  The library handle context values
+    // should not change, but the context maintained by the callout handle
+    // should.
+
+    zero_results();
+    collection->callCallouts(four_index, callout_handle_b);
+
+    EXPECT_EQ("11xa11xb12xa12xb13xa13xb", resultLibraryString(0));
+    EXPECT_EQ("21xa21xb22xa22xb23xa23xb", resultLibraryString(1));
+    EXPECT_EQ("31xa31xb32xa32xb33xa33xb", resultLibraryString(2));
+
+    EXPECT_EQ((1111 + 1112 + 1211 + 1212 + 1311 + 1312), resultLibraryInt(0));
+    EXPECT_EQ((2111 + 2112 + 2211 + 2212 + 2311 + 2312), resultLibraryInt(1));
+    EXPECT_EQ((3111 + 3112 + 3211 + 3212 + 3311 + 3312), resultLibraryInt(2));
+
+    EXPECT_EQ("11cb12cb13cb", resultCalloutString(0));
+    EXPECT_EQ("21cb22cb23cb", resultCalloutString(1));
+    EXPECT_EQ("31cb32cb33cb", resultCalloutString(2));
+
+    EXPECT_EQ((1122 + 1222 + 1322), resultCalloutInt(0));
+    EXPECT_EQ((2122 + 2222 + 2322), resultCalloutInt(1));
+    EXPECT_EQ((3122 + 3222 + 3322), resultCalloutInt(2));
+}
+
+// Now repeat the test, but add a deletion callout to the list.  The "two"
+// hook of library 2 will have an additional callout to delete the "int"
+// element: the same hook for library 3 will delete both elements.  In
+// addition, the names of context elements for the libraries at this point
+// will be printed.
+
+// List of context item names.
+
+vector<string>&
+getItemNames(int index) {
+    static vector<string> context_items[3];
+    return (context_items[index]);
+}
+
+// Context item deletion functions.
+
+int
+deleteIntContextItem(CalloutHandle& handle) {
+    handle.deleteContext("int");
+    return (0);
+}
+
+int
+deleteAllContextItems(CalloutHandle& handle) {
+    handle.deleteAllContext();
+    return (0);
+}
+
+// Generic print function - copy names in sorted order.
+
+int
+printContextNamesExecute(CalloutHandle& handle, int library_num) {
+    const int index = library_num - 1;
+    getItemNames(index) = handle.getContextNames();
+    sort(getItemNames(index).begin(), getItemNames(index).end());
+    return (0);
+}
+
+int
+printContextNames1(CalloutHandle& handle) {
+    return (printContextNamesExecute(handle, 1));
+}
+
+int
+printContextNames2(CalloutHandle& handle) {
+    return (printContextNamesExecute(handle, 2));
+}
+
+int
+printContextNames3(CalloutHandle& handle) {
+    return (printContextNamesExecute(handle, 3));
+}
+
+// Perform the test including deletion of context items.
+
+TEST(HandlesTest, ContextDeletionCheck) {
+    // Create the LibraryHandleCollection with a set of four callouts
+    // (the test does not use the ContextCreate and ContextDestroy callouts.)
+
+    boost::shared_ptr<ServerHooks> server_hooks(new ServerHooks());
+    const int one_index = server_hooks->registerHook("one");
+    const int two_index = server_hooks->registerHook("two");
+    const int three_index = server_hooks->registerHook("three");
+    const int four_index = server_hooks->registerHook("four");
+
+    // Create the library handle collection and the library handles.
+    boost::shared_ptr<LibraryHandleCollection>
+        collection(new LibraryHandleCollection());
+
+    boost::shared_ptr<LibraryHandle> handle(new LibraryHandle(server_hooks));
+    handle->registerCallout("one", callout11);
+    handle->registerCallout("two", callout12);
+    handle->registerCallout("two", printContextNames1);
+    handle->registerCallout("three", callout13);
+    handle->registerCallout("four", print1);
+    collection->addLibraryHandle(handle);
+
+    handle.reset(new LibraryHandle(server_hooks));
+    handle->registerCallout("one", callout21);
+    handle->registerCallout("two", callout22);
+    handle->registerCallout("two", deleteIntContextItem);
+    handle->registerCallout("two", printContextNames2);
+    handle->registerCallout("three", callout23);
+    handle->registerCallout("four", print2);
+    collection->addLibraryHandle(handle);
+
+    handle.reset(new LibraryHandle(server_hooks));
+    handle->registerCallout("one", callout31);
+    handle->registerCallout("two", callout32);
+    handle->registerCallout("two", deleteAllContextItems);
+    handle->registerCallout("two", printContextNames3);
+    handle->registerCallout("three", callout33);
+    handle->registerCallout("four", print3);
+    collection->addLibraryHandle(handle);
+
+    // Create the callout handles and distinguish them by setting the "long"
+    // argument.
+    CalloutHandle callout_handle_a(collection);
+    callout_handle_a.setArgument("string", string("a"));
+
+    CalloutHandle callout_handle_b(collection);
+    callout_handle_b.setArgument("string", string("b"));
+
+    // Now call the callouts attached to the first three hooks.  Each hook is
+    // called twice (once for each callout handle) before the next hook is
+    // called.
+    collection->callCallouts(one_index, callout_handle_a);
+    collection->callCallouts(one_index, callout_handle_b);
+    collection->callCallouts(two_index, callout_handle_a);
+    collection->callCallouts(two_index, callout_handle_b);
+    collection->callCallouts(three_index, callout_handle_a);
+    collection->callCallouts(three_index, callout_handle_b);
+
+    // Get the results for each callout.  Explicitly zero the variables before
+    // getting the results so we are certain that the values are the results
+    // of the callouts.
+
+    zero_results();
+    collection->callCallouts(four_index, callout_handle_a);
+
+    // The logic by which the expected results are arrived at is described
+    // in the ContextAccessCheck test.  The results here are difference
+    // because context items have been modified along the way.
+    //
+    // As only the ContextHandle context is modified, the LibraryHandle
+    // context is unaltered.
+
+    EXPECT_EQ("11xa11xb12xa12xb13xa13xb", resultLibraryString(0));
+    EXPECT_EQ("21xa21xb22xa22xb23xa23xb", resultLibraryString(1));
+    EXPECT_EQ("31xa31xb32xa32xb33xa33xb", resultLibraryString(2));
+
+    EXPECT_EQ((1111 + 1112 + 1211 + 1212 + 1311 + 1312), resultLibraryInt(0));
+    EXPECT_EQ((2111 + 2112 + 2211 + 2212 + 2311 + 2312), resultLibraryInt(1));
+    EXPECT_EQ((3111 + 3112 + 3211 + 3212 + 3311 + 3312), resultLibraryInt(2));
+
+    // ContextHandle context results.
+
+    EXPECT_EQ("11ca12ca13ca", resultCalloutString(0));
+    EXPECT_EQ("21ca22ca23ca", resultCalloutString(1));
+    EXPECT_EQ(        "33ca", resultCalloutString(2));
+
+    EXPECT_EQ((1121 + 1221 + 1321), resultCalloutInt(0));
+    EXPECT_EQ((              2321), resultCalloutInt(1));
+    EXPECT_EQ((              3321), resultCalloutInt(2));
+
+    // Repeat the checks for callout b.  The library handle context values
+    // should not change, but the context maintained by the callout handle
+    // should.
+
+    zero_results();
+    collection->callCallouts(four_index, callout_handle_b);
+
+    EXPECT_EQ("11xa11xb12xa12xb13xa13xb", resultLibraryString(0));
+    EXPECT_EQ("21xa21xb22xa22xb23xa23xb", resultLibraryString(1));
+    EXPECT_EQ("31xa31xb32xa32xb33xa33xb", resultLibraryString(2));
+
+    EXPECT_EQ((1111 + 1112 + 1211 + 1212 + 1311 + 1312), resultLibraryInt(0));
+    EXPECT_EQ((2111 + 2112 + 2211 + 2212 + 2311 + 2312), resultLibraryInt(1));
+    EXPECT_EQ((3111 + 3112 + 3211 + 3212 + 3311 + 3312), resultLibraryInt(2));
+
+    EXPECT_EQ("11cb12cb13cb", resultCalloutString(0));
+    EXPECT_EQ("21cb22cb23cb", resultCalloutString(1));
+    EXPECT_EQ(        "33cb", resultCalloutString(2));
+
+    EXPECT_EQ((1122 + 1222 + 1322), resultCalloutInt(0));
+    EXPECT_EQ((              2322), resultCalloutInt(1));
+    EXPECT_EQ((              3322), resultCalloutInt(2));
+
+    // ... and check what the names of the context items are after the callouts
+    // for hook "two".  We know they are in sorted order.
+
+    EXPECT_EQ(2, getItemNames(0).size());
+    EXPECT_EQ(string("int"),    getItemNames(0)[0]);
+    EXPECT_EQ(string("string"), getItemNames(0)[1]);
+
+    EXPECT_EQ(1, getItemNames(1).size());
+    EXPECT_EQ(string("string"), getItemNames(1)[0]);
+
+    EXPECT_EQ(0, getItemNames(2).size());
+}
+
+} // Anonymous namespace
+



More information about the bind10-changes mailing list