Browse Source

[3156] Review comments addressed: part II - d2::StateMode revisions.

This commit replaces the state specification mechanism in d2::StateModel
with one derived from LabeledValue and LabeledValueSet.
Thomas Markwalder 11 years ago
parent
commit
86050220a6

+ 1 - 1
src/bin/d2/d2_messages.mes

@@ -253,7 +253,7 @@ in event loop.
 This is informational message issued when the application has been instructed
 This is informational message issued when the application has been instructed
 to shut down by the controller.
 to shut down by the controller.
 
 
-% DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR application encountered an unexpected error while carrying out a NameChangeRequest: %1 , %2
+% DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR application encountered an unexpected error while carrying out a NameChangeRequest: %1
 This is error message issued when the application fails to process a
 This is error message issued when the application fails to process a
 NameChangeRequest correctly. Some or all of the DNS updates requested as part
 NameChangeRequest correctly. Some or all of the DNS updates requested as part
 of this update did not succeed. This is a programmatic error and should be
 of this update did not succeed. This is a programmatic error and should be

+ 27 - 34
src/bin/d2/nc_trans.cc

@@ -83,7 +83,10 @@ NameChangeTransaction::operator()(DNSClient::Status status) {
 
 
 void
 void
 NameChangeTransaction::defineEvents() {
 NameChangeTransaction::defineEvents() {
+    // Call superclass impl first.
     StateModel::defineEvents();
     StateModel::defineEvents();
+
+    // Define NCT events.
     defineEvent(SELECT_SERVER_EVT, "SELECT_SERVER_EVT");
     defineEvent(SELECT_SERVER_EVT, "SELECT_SERVER_EVT");
     defineEvent(SERVER_SELECTED_EVT, "SERVER_SELECTED_EVT");
     defineEvent(SERVER_SELECTED_EVT, "SERVER_SELECTED_EVT");
     defineEvent(SERVER_IO_ERROR_EVT, "SERVER_IO_ERROR_EVT");
     defineEvent(SERVER_IO_ERROR_EVT, "SERVER_IO_ERROR_EVT");
@@ -95,7 +98,10 @@ NameChangeTransaction::defineEvents() {
 
 
 void
 void
 NameChangeTransaction::verifyEvents() {
 NameChangeTransaction::verifyEvents() {
+    // Call superclass impl first.
     StateModel::verifyEvents();
     StateModel::verifyEvents();
+
+    // Verify NCT events.
     getEvent(SELECT_SERVER_EVT);
     getEvent(SELECT_SERVER_EVT);
     getEvent(SERVER_SELECTED_EVT);
     getEvent(SERVER_SELECTED_EVT);
     getEvent(SERVER_IO_ERROR_EVT);
     getEvent(SERVER_IO_ERROR_EVT);
@@ -106,17 +112,31 @@ NameChangeTransaction::verifyEvents() {
 }
 }
 
 
 void
 void
-NameChangeTransaction::verifyStateHandlerMap() {
-    getStateHandler(READY_ST);
-    getStateHandler(SELECTING_FWD_SERVER_ST);
-    getStateHandler(SELECTING_REV_SERVER_ST);
-    getStateHandler(PROCESS_TRANS_OK_ST);
-    getStateHandler(PROCESS_TRANS_FAILED_ST);
+NameChangeTransaction::defineStates() {
+    // Call superclass impl first.
+    StateModel::defineStates();
+    // This class is "abstract" in that it does not supply handlers for its
+    // states, derivations must do that therefore they must define them.
+}
+
+void
+NameChangeTransaction::verifyStates() {
+    // Call superclass impl first.
+    StateModel::verifyStates();
+
+    // Verify NCT states. This ensures that derivations provide the handlers.
+    getState(READY_ST);
+    getState(SELECTING_FWD_SERVER_ST);
+    getState(SELECTING_REV_SERVER_ST);
+    getState(PROCESS_TRANS_OK_ST);
+    getState(PROCESS_TRANS_FAILED_ST);
 }
 }
 
 
 void
 void
-NameChangeTransaction::onModelFailure() {
+NameChangeTransaction::onModelFailure(const std::string& explanation) {
     setNcrStatus(dhcp_ddns::ST_FAILED);
     setNcrStatus(dhcp_ddns::ST_FAILED);
+    LOG_ERROR(dctl_logger, DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR)
+                  .arg(explanation);
 }
 }
 
 
 void
 void
@@ -226,32 +246,5 @@ NameChangeTransaction::getReverseChangeCompleted() const {
     return (reverse_change_completed_);
     return (reverse_change_completed_);
 }
 }
 
 
-const char*
-NameChangeTransaction::getStateLabel(const int state) const {
-    const char* str = "Unknown";
-    switch(state) {
-    case READY_ST:
-        str = "NameChangeTransaction::READY_ST";
-        break;
-    case SELECTING_FWD_SERVER_ST:
-        str = "NameChangeTransaction::SELECTING_FWD_SERVER_ST";
-        break;
-    case SELECTING_REV_SERVER_ST:
-        str = "NameChangeTransaction::SELECTING_REV_SERVER_ST";
-        break;
-    case PROCESS_TRANS_OK_ST:
-        str = "NameChangeTransaction::PROCESS_TRANS_OK_ST";
-        break;
-    case PROCESS_TRANS_FAILED_ST:
-        str = "NameChangeTransaction::PROCESS_TRANS_FAILED_ST";
-        break;
-    default:
-        str = StateModel::getStateLabel(state);
-        break;
-    }
-
-    return (str);
-}
-
 } // namespace isc::d2
 } // namespace isc::d2
 } // namespace isc
 } // namespace isc

+ 23 - 84
src/bin/d2/nc_trans.h

@@ -182,12 +182,12 @@ public:
     virtual void operator()(DNSClient::Status status);
     virtual void operator()(DNSClient::Status status);
 
 
 protected:
 protected:
-    /// @brief Adds events defined by NameChangeTransaction to the event set. 
+    /// @brief Adds events defined by NameChangeTransaction to the event set.
     ///
     ///
     /// This method adds the events common to NCR transaction processing to
     /// This method adds the events common to NCR transaction processing to
     /// the set of define events.  It invokes the superclass's implementation
     /// the set of define events.  It invokes the superclass's implementation
     /// first to maitain the hierarchical chain of event defintion.
     /// first to maitain the hierarchical chain of event defintion.
-    /// Derivations of NameChangeTransaction must invoke its implementation 
+    /// Derivations of NameChangeTransaction must invoke its implementation
     /// in like fashion.
     /// in like fashion.
     ///
     ///
     /// @throw StateModelError if an event definition is invalid or a duplicate.
     /// @throw StateModelError if an event definition is invalid or a duplicate.
@@ -198,58 +198,33 @@ protected:
     /// This method verifies that the events defined by both the superclass and
     /// This method verifies that the events defined by both the superclass and
     /// this class are defined.  As with defineEvents, this method calls the
     /// this class are defined.  As with defineEvents, this method calls the
     /// superclass's implementation first, to verify events defined by it and
     /// superclass's implementation first, to verify events defined by it and
-    /// then this implementation to verify events defined by 
+    /// then this implementation to verify events defined by
     /// NameChangeTransaction.
     /// NameChangeTransaction.
     ///
     ///
-    /// @throw StateModelError if an event value is undefined. 
+    /// @throw StateModelError if an event value is undefined.
     virtual void verifyEvents();
     virtual void verifyEvents();
 
 
-    /// @brief Populates the map of state handlers.
+    /// @brief Adds states defined by NameChangeTransaction to the state set.
     ///
     ///
-    /// This method is used by derivations to construct a map of states to
-    /// their appropriate state handlers (bound method pointers).  It is
-    /// invoked at the beginning of startTransaction().
+    /// This method adds the states common to NCR transaction processing to
+    /// the dictionary of states.  It invokes the superclass's implementation
+    /// first to maitain the hierarchical chain of state defintion.
+    /// Derivations of NameChangeTransaction must invoke its implementation
+    /// in like fashion.
     ///
     ///
-    /// Implementations should use the addToMap() method add entries to
-    /// the map.
-    /// @todo This method should be pure virtual but until there are
-    /// derivations for the update manager to use, we will provide a
-    /// temporary empty, implementation.  If we make it pure virtual now
-    /// D2UpdateManager will not compile.
-    virtual void initStateHandlerMap() {};
+    /// @throw StateModelError if an state definition is invalid or a duplicate.
+    virtual void defineStates();
 
 
-    /// @brief Validates the contents of the state handler map.
-    ///
-    /// This method is invoked immediately after initStateHandlerMap and
-    /// verifies that the state map includes handlers for all of the states
-    /// defined by NameChangeTransaction.  If the map is determined to be
-    /// invalid this method will throw a NameChangeTransactionError.
-    ///
-    /// Derivations should ALSO provide an implementation of this method. That
-    /// implementation should invoke this method, as well as verifying that all
-    /// of the derivation's states have handlers.
-    ///
-    /// A derivation's implementation of this function might look as follows:
-    ///
-    /// @code
+    /// @brief Validates the contents of the set of states.
     ///
     ///
-    ///     class DerivedTrans : public NameChangeTransaction {
-    ///         :
-    ///         void verifyStateHandlerMap() {
-    ///             // Verify derivations' states:
-    ///             getStateHandler(SOME_DERIVED_STATE_1);
-    ///             getStateHandler(SOME_DERIVED_STATE_2);
-    ///             :
-    ///             getStateHandler(SOME_DERIVED_STATE_N);
-    ///
-    ///             // Verify handlers for NameChangeTransaction states:
-    ///             NameChangeTransaction::verifyStateHandlerMap();
-    ///         }
-    ///
-    /// @endcode
+    /// This method verifies that the states defined by both the superclass and
+    /// this class are defined.  As with defineStates, this method calls the
+    /// superclass's implementation first, to verify states defined by it and
+    /// then this implementation to verify states defined by
+    /// NameChangeTransaction.
     ///
     ///
-    /// @throw NameChangeTransactionError if the map is invalid.
-    virtual void verifyStateHandlerMap();
+    /// @throw StateModelError if an event value is undefined.
+    virtual void verifyStates();
 
 
     /// @brief Handler for fatal model execution errors.
     /// @brief Handler for fatal model execution errors.
     ///
     ///
@@ -260,7 +235,9 @@ protected:
     /// error occurs the transaction is deemed inoperable, and futher model
     /// error occurs the transaction is deemed inoperable, and futher model
     /// execution cannot be performed.  It marks the transaction as failed by
     /// execution cannot be performed.  It marks the transaction as failed by
     /// setting the NCR status to dhcp_ddns::ST_FAILED
     /// setting the NCR status to dhcp_ddns::ST_FAILED
-    virtual void onModelFailure();
+    ///
+    /// @param explanation is text detailing the error
+    virtual void onModelFailure(const std::string& explanation);
 
 
     /// @brief Sets the update status to the given status value.
     /// @brief Sets the update status to the given status value.
     ///
     ///
@@ -388,44 +365,6 @@ public:
     /// @return True if the reverse change has been completed, false otherwise.
     /// @return True if the reverse change has been completed, false otherwise.
     bool getReverseChangeCompleted() const;
     bool getReverseChangeCompleted() const;
 
 
-    /// @brief Converts a state value into a text label.
-    ///
-    /// This method supplies labels for NameChangeTransaction's predefined
-    /// states. It is declared virtual to allow derivations to embed a call to
-    /// this method within their own implementation which would define labels
-    /// for its states.  An example implementation might look like the
-    /// following:
-    /// @code
-    ///
-    /// class DerivedTrans : public NameChangeTransaction {
-    ///     :
-    ///     static const int EXAMPLE_1_ST = NCT_STATE_MAX + 1;
-    ///     :
-    ///     const char* getStateLabel(const int state) const {
-    ///         const char* str = "Unknown";
-    ///         switch(state) {
-    ///         case EXAMPLE_1_ST:
-    ///             str = "DerivedTrans::EXAMPLE_1_ST";
-    ///             break;
-    ///         :
-    ///         default:
-    ///             // Not a derived state, pass it to NameChangeTransaction's
-    ///             // method.
-    ///             str = NameChangeTransaction::getStateLabel(state);
-    ///             break;
-    ///         }
-    ///
-    ///         return (str);
-    ///      }
-    ///
-    /// @endcode
-    ///
-    /// @param state is the state for which a label is desired.
-    ///
-    /// @return Returns a const char* containing the state label or
-    /// "Unknown" if the value cannot be mapped.
-    virtual const char* getStateLabel(const int state) const;
-
 private:
 private:
     /// @brief The IOService which should be used to for IO processing.
     /// @brief The IOService which should be used to for IO processing.
     isc::asiolink::IOService& io_service_;
     isc::asiolink::IOService& io_service_;

+ 136 - 82
src/bin/d2/state_model.cc

@@ -20,6 +20,53 @@
 namespace isc {
 namespace isc {
 namespace d2 {
 namespace d2 {
 
 
+/********************************** State *******************************/
+
+State::State(const int value, const char* label, StateHandler handler)
+        : LabeledValue(value, label), handler_(handler) {
+}
+
+State::~State() {
+}
+
+void
+State::run() {
+        (handler_)();
+}
+
+/********************************** StateSet *******************************/
+
+StateSet::StateSet() {
+}
+
+StateSet::~StateSet() {
+}
+
+void
+StateSet::add(const int value, const char* label, StateHandler handler) {
+    try {
+        LabeledValueSet::add(LabeledValuePtr(new State(value, label, handler)));
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "StateSet: cannot add state :" << ex.what());
+    }
+
+}
+
+const StatePtr
+StateSet::getState(int value) {
+    if (!isDefined(value)) {
+        isc_throw(StateModelError," StateSet: state is undefined");
+    }
+
+    // Since we have to use dynamic casting, to get a state pointer
+    // we can't return a reference.
+    StatePtr state = boost::dynamic_pointer_cast<State>(get(value));
+    return (state);
+}
+
+/********************************** StateModel *******************************/
+
+
 // Common state model states
 // Common state model states
 const int StateModel::NEW_ST;
 const int StateModel::NEW_ST;
 const int StateModel::END_ST;
 const int StateModel::END_ST;
@@ -34,10 +81,10 @@ const int StateModel::FAIL_EVT;
 
 
 const int StateModel::SM_EVENT_MAX;
 const int StateModel::SM_EVENT_MAX;
 
 
-StateModel::StateModel() : state_handlers_(), state_(NEW_ST),
-                          prev_state_(NEW_ST), last_event_(NOP_EVT),
-                          next_event_(NOP_EVT), on_entry_flag_(false),
-                          on_exit_flag_(false) {
+StateModel::StateModel() : events_(), states_(), dictionaries_initted_(false),
+                          curr_state_(NEW_ST), prev_state_(NEW_ST),
+                          last_event_(NOP_EVT), next_event_(NOP_EVT),
+                          on_entry_flag_(false), on_exit_flag_(false) {
 }
 }
 
 
 StateModel::~StateModel(){
 StateModel::~StateModel(){
@@ -45,6 +92,7 @@ StateModel::~StateModel(){
 
 
 void
 void
 StateModel::startModel(const int start_state) {
 StateModel::startModel(const int start_state) {
+    // First let's build and verify the dictionary of events.
     try {
     try {
         defineEvents();
         defineEvents();
         verifyEvents();
         verifyEvents();
@@ -52,20 +100,29 @@ StateModel::startModel(const int start_state) {
         isc_throw(StateModelError, "Event set is invalid: " << ex.what());
         isc_throw(StateModelError, "Event set is invalid: " << ex.what());
     }
     }
 
 
-    // Initialize the state handler map first.
-    initStateHandlerMap();
+    // Next let's build and verify the dictionary of states.
+    try {
+        defineStates();
+        verifyStates();
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "State set is invalid: " << ex.what());
+    }
 
 
-    // Test validity of the handler map. This provides an opportunity to
-    // sanity check the map prior to attempting to execute the model.
-    verifyStateHandlerMap();
+    // Record that we are good to go.
+    dictionaries_initted_ = true;
 
 
-    // Set the current state to startng state and enter the run loop.
+    // Set the current state to starting state and enter the run loop.
     setState(start_state);
     setState(start_state);
     runModel(START_EVT);
     runModel(START_EVT);
 }
 }
 
 
 void
 void
 StateModel::runModel(unsigned int run_event) {
 StateModel::runModel(unsigned int run_event) {
+    /// If the dictionaries aren't built bail out.
+    if (!dictionaries_initted_) {
+       abortModel("runModel invoked before model has been initialized");
+    }
+
     try {
     try {
         // Seed the loop with the given event as the next to process.
         // Seed the loop with the given event as the next to process.
         postNextEvent(run_event);
         postNextEvent(run_event);
@@ -73,32 +130,35 @@ StateModel::runModel(unsigned int run_event) {
             // Invoke the current state's handler.  It should consume the
             // Invoke the current state's handler.  It should consume the
             // next event, then determine what happens next by setting
             // next event, then determine what happens next by setting
             // current state and/or the next event.
             // current state and/or the next event.
-            (getStateHandler(state_))();
+            getState(curr_state_)->run();
 
 
             // Keep going until a handler sets next event to a NOP_EVT.
             // Keep going until a handler sets next event to a NOP_EVT.
         } while (!isModelDone() && getNextEvent() != NOP_EVT);
         } while (!isModelDone() && getNextEvent() != NOP_EVT);
     }
     }
     catch (const std::exception& ex) {
     catch (const std::exception& ex) {
-        // The model has suffered an unexpected exception. This constitutes a
+        // The model has suffered an unexpected exception. This constitutes
         // a model violation and indicates a programmatic shortcoming.
         // a model violation and indicates a programmatic shortcoming.
         // In theory, the model should account for all error scenarios and
         // In theory, the model should account for all error scenarios and
-        // deal with them accordingly.  Log it and transition to END_ST with
-        // FAILED_EVT via abortModel.
-        LOG_ERROR(dctl_logger, DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR)
-                  .arg(ex.what()).arg(getContextStr());
-        abortModel();
+        // deal with them accordingly.  Transition to END_ST with FAILED_EVT
+        // via abortModel.
+        abortModel(ex.what());
     }
     }
 }
 }
 
 
 void
 void
+StateModel::nopStateHandler() {
+}
+
+
+void
 StateModel::defineEvent(unsigned int event_value, const char* label) {
 StateModel::defineEvent(unsigned int event_value, const char* label) {
     if (!isModelNew()) {
     if (!isModelNew()) {
-        // Don't allow for self-modifying maps.
+        // Don't allow for self-modifying models.
         isc_throw(StateModelError, "Events may only be added to a new model."
         isc_throw(StateModelError, "Events may only be added to a new model."
                    << event_value << " - " << label);
                    << event_value << " - " << label);
     }
     }
 
 
-    // add method may throw on duplicate or empty label.  
+    // Attempt to add the event to the set.
     try {
     try {
         events_.add(event_value, label);
         events_.add(event_value, label);
     } catch (const std::exception& ex) {
     } catch (const std::exception& ex) {
@@ -109,52 +169,41 @@ StateModel::defineEvent(unsigned int event_value, const char* label) {
 const EventPtr&
 const EventPtr&
 StateModel::getEvent(unsigned int event_value) {
 StateModel::getEvent(unsigned int event_value) {
     if (!events_.isDefined(event_value)) {
     if (!events_.isDefined(event_value)) {
-        isc_throw(StateModelError, 
+        isc_throw(StateModelError,
                   "Event value is not defined:" << event_value);
                   "Event value is not defined:" << event_value);
     }
     }
 
 
     return (events_.get(event_value));
     return (events_.get(event_value));
 }
 }
 
 
-StateHandler
-StateModel::getStateHandler(unsigned int state) {
-    StateHandlerMap::iterator it = state_handlers_.find(state);
-    // It is wrong to try to find a state that isn't mapped.
-    if (it == state_handlers_.end()) {
-        isc_throw(StateModelError, "No handler for state: "
-                  << state << " - " << getStateLabel(state));
-    }
-
-    return ((*it).second);
-}
-
 void
 void
-StateModel::addToStateHandlerMap(unsigned int state, StateHandler handler) {
+StateModel::defineState(unsigned int state_value, const char* label,
+    StateHandler handler) {
     if (!isModelNew()) {
     if (!isModelNew()) {
         // Don't allow for self-modifying maps.
         // Don't allow for self-modifying maps.
-        isc_throw(StateModelError,
-                  "State handler mappings can only be added to a new model."
-                   << state << " - " << getStateLabel(state));
+        isc_throw(StateModelError, "States may only be added to a new model."
+                   << state_value << " - " << label);
     }
     }
 
 
-    StateHandlerMap::iterator it = state_handlers_.find(state);
-    // Check for a duplicate attempt.  State's can't have more than one.
-    if (it != state_handlers_.end()) {
-        isc_throw(StateModelError,
-                  "Attempted duplicate entry in state handler map, state: "
-                   << state << " - " << getStateLabel(state));
+    // Attempt to add the state to the set.
+    try {
+        states_.add(state_value, label, handler);
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "Error adding state: " << ex.what());
     }
     }
+}
 
 
-    // Do not allow handlers for special states fo NEW_ST and END_ST.
-    if (state == NEW_ST || state == END_ST) {
-        isc_throw(StateModelError, "A handler is not supported for state: "
-                  << state << " - " << getStateLabel(state));
+const StatePtr
+StateModel::getState(unsigned int state_value) {
+    if (!states_.isDefined(state_value)) {
+        isc_throw(StateModelError,
+                  "State value is not defined:" << state_value);
     }
     }
 
 
-    state_handlers_[state] = handler;
+    return (states_.getState(state_value));
 }
 }
 
 
-void 
+void
 StateModel::defineEvents() {
 StateModel::defineEvents() {
     defineEvent(NOP_EVT, "NOP_EVT");
     defineEvent(NOP_EVT, "NOP_EVT");
     defineEvent(START_EVT, "START_EVT");
     defineEvent(START_EVT, "START_EVT");
@@ -170,6 +219,19 @@ StateModel::verifyEvents() {
     getEvent(FAIL_EVT);
     getEvent(FAIL_EVT);
 }
 }
 
 
+void
+StateModel::defineStates() {
+    defineState(NEW_ST, "NEW_ST",
+                boost::bind(&StateModel::nopStateHandler, this));
+    defineState(END_ST, "END_ST",
+                boost::bind(&StateModel::nopStateHandler, this));
+}
+
+void
+StateModel::verifyStates() {
+    getState(NEW_ST);
+    getState(END_ST);
+}
 
 
 void
 void
 StateModel::transition(unsigned int state, unsigned int event) {
 StateModel::transition(unsigned int state, unsigned int event) {
@@ -183,33 +245,37 @@ StateModel::endModel() {
 }
 }
 
 
 void
 void
-StateModel::abortModel() {
+StateModel::abortModel(const std::string& explanation) {
     transition(END_ST, FAIL_EVT);
     transition(END_ST, FAIL_EVT);
-    onModelFailure();
+
+    std::ostringstream stream ;
+    stream << explanation << " : " << getContextStr();
+    onModelFailure(stream.str());
 }
 }
 
 
 void
 void
 StateModel::setState(unsigned int state) {
 StateModel::setState(unsigned int state) {
-    // If the new state isn't NEW_ST or END_ST, make sure it has a handler.
-    if ((state && state != NEW_ST && state != END_ST)
-        && (state_handlers_.end() == state_handlers_.find(state))) {
-        isc_throw(StateModelError, "Attempt to set state to invalid stat :"
-                  << state << "=" << getStateLabel(state));
+    if (state != END_ST && !states_.isDefined(state)) {
+        isc_throw(StateModelError,
+                  "Attempt to set state to an undefined value: " << state );
     }
     }
 
 
-    prev_state_ = state_;
-    state_ = state;
+    prev_state_ = curr_state_;
+    curr_state_ = state;
 
 
     // Set the "do" flags if we are transitioning.
     // Set the "do" flags if we are transitioning.
-    on_entry_flag_ = ((state != END_ST) && (prev_state_ != state_));
+    on_entry_flag_ = ((state != END_ST) && (prev_state_ != curr_state_));
+
     // At this time they are calculated the same way.
     // At this time they are calculated the same way.
     on_exit_flag_ = on_entry_flag_;
     on_exit_flag_ = on_entry_flag_;
 }
 }
 
 
 void
 void
 StateModel::postNextEvent(unsigned int event_value) {
 StateModel::postNextEvent(unsigned int event_value) {
+    // Check for FAIL_EVT as special case of model error before events are
+    // defined.
     if (event_value != FAIL_EVT && !events_.isDefined(event_value)) {
     if (event_value != FAIL_EVT && !events_.isDefined(event_value)) {
-        isc_throw(StateModelError, 
+        isc_throw(StateModelError,
                   "Attempt to post an undefined event, value: " << event_value);
                   "Attempt to post an undefined event, value: " << event_value);
     }
     }
 
 
@@ -232,8 +298,8 @@ StateModel::doOnExit() {
 }
 }
 
 
 unsigned int
 unsigned int
-StateModel::getState() const {
-    return (state_);
+StateModel::getCurrState() const {
+    return (curr_state_);
 }
 }
 
 
 unsigned int
 unsigned int
@@ -252,12 +318,12 @@ StateModel::getNextEvent() const {
 }
 }
 bool
 bool
 StateModel::isModelNew() const {
 StateModel::isModelNew() const {
-    return (state_ == NEW_ST);
+    return (curr_state_ == NEW_ST);
 }
 }
 
 
 bool
 bool
 StateModel::isModelRunning() const {
 StateModel::isModelRunning() const {
-    return ((state_ != NEW_ST) && (state_ != END_ST));
+    return ((curr_state_ != NEW_ST) && (curr_state_ != END_ST));
 }
 }
 
 
 bool
 bool
@@ -267,7 +333,7 @@ StateModel::isModelWaiting() const {
 
 
 bool
 bool
 StateModel::isModelDone() const {
 StateModel::isModelDone() const {
-    return (state_ == END_ST);
+    return (curr_state_ == END_ST);
 }
 }
 
 
 bool
 bool
@@ -277,26 +343,19 @@ StateModel::didModelFail() const {
 
 
 const char*
 const char*
 StateModel::getStateLabel(const int state) const {
 StateModel::getStateLabel(const int state) const {
-    const char* str = "Unknown";
-    switch(state) {
-    case NEW_ST:
-        str = "StateModel::NEW_ST";
-        break;
-    case END_ST:
-        str = "StateModel::END_ST";
-        break;
-    default:
-        break;
-    }
+    return (states_.getLabel(state));
+}
 
 
-    return (str);
+const char*
+StateModel::getEventLabel(const int event) const {
+    return (events_.getLabel(event));
 }
 }
 
 
 std::string
 std::string
 StateModel::getContextStr() const {
 StateModel::getContextStr() const {
     std::ostringstream stream;
     std::ostringstream stream;
     stream << "current state: [ "
     stream << "current state: [ "
-            << state_ << " " << getStateLabel(state_)
+            << curr_state_ << " " << getStateLabel(curr_state_)
             << " ] next event: [ "
             << " ] next event: [ "
             << next_event_ << " " << getEventLabel(next_event_) << " ]";
             << next_event_ << " " << getEventLabel(next_event_) << " ]";
     return(stream.str());
     return(stream.str());
@@ -312,10 +371,5 @@ StateModel::getPrevContextStr() const {
     return(stream.str());
     return(stream.str());
 }
 }
 
 
-const char*
-StateModel::getEventLabel(const int event) const {
-    return (events_.getLabel(event));
-}
-
 } // namespace isc::d2
 } // namespace isc::d2
 } // namespace isc
 } // namespace isc

+ 206 - 141
src/bin/d2/state_model.h

@@ -39,7 +39,7 @@ public:
         isc::Exception(file, line, what) { };
         isc::Exception(file, line, what) { };
 };
 };
 
 
-/// @brief Define an Event. 
+/// @brief Define an Event.
 typedef LabeledValue Event;
 typedef LabeledValue Event;
 
 
 /// @brief Define Event pointer.
 /// @brief Define Event pointer.
@@ -48,22 +48,96 @@ typedef LabeledValuePtr EventPtr;
 /// @brief Defines a pointer to an instance method for handling a state.
 /// @brief Defines a pointer to an instance method for handling a state.
 typedef boost::function<void()> StateHandler;
 typedef boost::function<void()> StateHandler;
 
 
-/// @brief Defines a map of states to their handler methods.
-typedef std::map<unsigned int, StateHandler> StateHandlerMap;
+/// @brief Defines a State within the State Model.
+///
+/// This class provides the means to define a state within a set or dictionary
+/// of states, and assign the state an handler method to execute the state's
+/// actions.  It derives from LabeledValue which allows a set of states to be
+/// keyed by integer constants.
+class State : public LabeledValue {
+public:
+    /// @brief Constructor
+    ///
+    /// @param value is the numeric value of the state
+    /// @param label is the text label to assign to the state
+    /// @param handler is the bound instance method which handles the state's
+    /// action.
+    ///
+    /// A typical invocation might look this:
+    ///
+    /// @code
+    ///     State(SOME_INT_VAL, "SOME_INT_VAL",
+    ///            boost::bind(&StateModelDerivation::someHandler, this));
+    /// @endcode
+    ///
+    /// @throw StateModelError if label is null or blank.
+    State(const int value, const char* label, StateHandler handler);
+
+    /// @brief Destructor
+    virtual ~State();
+
+    /// @brief Invokes the State's handler.
+    void run();
+
+private:
+    /// @brief Bound instance method pointer to the state's handler method.
+    StateHandler handler_;
+};
+
+/// @brief Defines a shared pointer to a State.
+typedef boost::shared_ptr<State> StatePtr;
+
+/// @brief Implements a unique set or dictionary of states.
+///
+/// This class provides the means to construct and access a unique set of
+/// states.  This provide the ability to validate state values, look up their
+/// text labels, and their handlers.
+class StateSet : public LabeledValueSet {
+public:
+    /// @brief Constructor
+    StateSet();
+
+    /// @brief Destructor
+    virtual ~StateSet();
+
+    /// @brief Adds a state definition to the set of states.
+    ///
+    /// @param value is the numeric value of the state
+    /// @param label is the text label to assig to the state
+    /// @param handler is the bound instance method which handles the state's
+    ///
+    /// @throw StateModelError if the value is already defined in the set, or
+    /// if the label is null or blank.
+    void add(const int value, const char* label, StateHandler handler);
+
+    /// @brief Fetches a state for the given value.
+    ///
+    /// @param value the numeric value of the state desired
+    ///
+    /// @return A constant pointer the State found.
+    /// Note, this relies on dynamic cast and cannot return a pointer reference.
+    ///
+    /// @throw StateModelError if the value is undefined.
+    const StatePtr getState(int value);
+};
 
 
 /// @brief Implements a finite state machine.
 /// @brief Implements a finite state machine.
 ///
 ///
 /// StateModel is an abstract class that provides the structure and mechanics
 /// StateModel is an abstract class that provides the structure and mechanics
 /// of a basic finite state machine.
 /// of a basic finite state machine.
 ///
 ///
-/// The state model implementation used is a very basic approach. States
-/// and events described by simple integer constants. Each state must have a 
-/// state handler. State handlers are void methods which require no parameters.
-/// Each model instance contains a map of states to instance method pointers
-/// to their respective state handlers.  The model tracks the following
-/// context values:
+/// The state model implementation used is a very basic approach. The model
+/// uses numeric constants to identify events and states, and maintains
+/// dictionaries of defined events and states.  Event and state definitions
+/// include a text label for logging purposes.  Additionally, each state
+/// definition includes a state handler. State handlers are methods which
+/// implement the actions that need to occur when the model is "in" a given
+/// state.  The implementation provides methods to add entries to and verify
+/// the contents of both dictionaries.
 ///
 ///
-/// * state - The current state of the model
+/// During model execution, the following context is tracked:
+///
+/// * current state - The current state of the model
 /// * previous state -  The state the model was in prior to the current state
 /// * previous state -  The state the model was in prior to the current state
 /// * next event - The next event to be consumed
 /// * next event - The next event to be consumed
 /// * last event - The event most recently consumed
 /// * last event - The event most recently consumed
@@ -126,12 +200,23 @@ typedef std::map<unsigned int, StateHandler> StateHandlerMap;
 /// specifies the "start" state and it becomes the current state.
 /// specifies the "start" state and it becomes the current state.
 
 
 /// The first task undertaken by startModel is to initialize and verify the
 /// The first task undertaken by startModel is to initialize and verify the
-/// state handler map.  Two virtual methods, initializeStateHandlerMap and
-/// verifyStateHandlerMap, are provided for the express purpose of allowing
-/// derivations to populate the state handler map and then verify that map is
-/// contents are correct.
+/// the event and state dictionaries.  The following virtual methods are
+/// provided for this:
+///
+/// * defineEvents - define events
+/// * verifyEvents - verifies that the expected events are defined
+/// * defineStates - defines states
+/// * verifyStates - verifies that the expected states are defined
+///
+/// The concept behind the verify methods is to provide an initial sanity
+/// check of the dictionaries.  This should help avoid using undefined event
+/// or state values accidentally.
 ///
 ///
-/// Once the handler map has been properly initialized, the startModel method
+/// These methods are intended to be implemented by each "layer" in a StateModel
+/// derivation hierarchy.  This allows each layer to define additional events
+/// and states.
+///
+/// Once the dictionaries have been properly initialized, the startModel method
 /// invokes runModel with an event of START_EVT.  From this point forward and
 /// invokes runModel with an event of START_EVT.  From this point forward and
 /// until the model reaches the END_ST or fails, it is considered to be
 /// until the model reaches the END_ST or fails, it is considered to be
 /// "running".  If the model encounters a NOP_EVT then it is "running" and
 /// "running".  If the model encounters a NOP_EVT then it is "running" and
@@ -192,20 +277,21 @@ public:
 
 
     /// @brief Begins execution of the model.
     /// @brief Begins execution of the model.
     ///
     ///
-    /// This method invokes initStateHandlerMap() to initialize the map of state
-    /// handlers, followed by verifyStateHandlerMap which validates the map
-    /// contents. It then starts the  model execution setting the current
-    /// state to the given start state, and the event to START_EVT.
+    /// This method invokes the define and verify methods for both events and
+    /// states to initialize their respective dictionaries. It then starts
+    /// the model execution setting the current state to the given start state,
+    /// and the event to START_EVT.
     ///
     ///
     /// @param start_state is the state in which to begin execution.
     /// @param start_state is the state in which to begin execution.
+    ///
     /// @throw StateModelError or others indirectly, as this method calls
     /// @throw StateModelError or others indirectly, as this method calls
-    /// initializeStateHandlerMap and verifyStateHandlerMap.
+    /// dictionary define and verify methods.
     void startModel(const int start_state);
     void startModel(const int start_state);
 
 
     /// @brief Processes events through the state model
     /// @brief Processes events through the state model
     ///
     ///
     /// This method implements the state model "execution loop".  It uses
     /// This method implements the state model "execution loop".  It uses
-    /// the given event as the next event to process and begins invoking the
+    /// the given event as the next event to process and begins invoking
     /// the state handler for the current state.   As described above, the
     /// the state handler for the current state.   As described above, the
     /// invoked state handler consumes the next event and then determines the
     /// invoked state handler consumes the next event and then determines the
     /// next event and the current state as required to implement the business
     /// next event and the current state as required to implement the business
@@ -231,15 +317,22 @@ public:
     /// handler should call endModel.
     /// handler should call endModel.
     void endModel();
     void endModel();
 
 
+    /// @brief An empty state handler.
+    ///
+    /// This method is primarily used to permit special states, NEW_ST and
+    /// END_ST to be included in the state dictionary.  Currently it is an
+    /// empty method.
+    void nopStateHandler();
+
 protected:
 protected:
-    /// @brief Populates the set of events. 
+    /// @brief Populates the set of events.
     ///
     ///
     /// This method is used to construct the set of valid events. Each class
     /// This method is used to construct the set of valid events. Each class
-    /// within a StateModel derivation heirarchy uses this method to add any
+    /// within a StateModel derivation hierarchy uses this method to add any
     /// events it defines to the set.  Each derivation's implementation must
     /// events it defines to the set.  Each derivation's implementation must
-    /// also call it's superclass's implementation.  This allows each class 
-    /// within the heirarchy to make contributions to the set of defined 
-    /// events. Implementations use the method, defineEvent(), to add event 
+    /// also call it's superclass's implementation.  This allows each class
+    /// within the hierarchy to make contributions to the set of defined
+    /// events. Implementations use the method, defineEvent(), to add event
     /// definitions.  An example of the derivation's implementation follows:
     /// definitions.  An example of the derivation's implementation follows:
     ///
     ///
     /// @code
     /// @code
@@ -247,7 +340,7 @@ protected:
     ///     // Call the superclass implementation.
     ///     // Call the superclass implementation.
     ///     StateModelDerivation::defineEvents();
     ///     StateModelDerivation::defineEvents();
     ///
     ///
-    ///     // Add the events defined by the derivation. 
+    ///     // Add the events defined by the derivation.
     ///     defineEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
     ///     defineEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
     ///     defineEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
     ///     defineEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
     ///     :
     ///     :
@@ -268,7 +361,7 @@ protected:
     /// @brief Fetches the event referred to by value.
     /// @brief Fetches the event referred to by value.
     ///
     ///
     /// @param value is the numeric value of the event desired.
     /// @param value is the numeric value of the event desired.
-    /// 
+    ///
     /// @return returns a constant pointer reference to the event if found
     /// @return returns a constant pointer reference to the event if found
     ///
     ///
     /// @throw StateModelError if the event is not defined.
     /// @throw StateModelError if the event is not defined.
@@ -276,89 +369,95 @@ protected:
 
 
     /// @brief Validates the contents of the set of events.
     /// @brief Validates the contents of the set of events.
     ///
     ///
-    /// This method is invoked immediately after the defineEvents method and 
-    /// is used to verify that all the requred events are defined.  If the 
-    /// event set is determined to be invalid this method should throw a 
+    /// This method is invoked immediately after the defineEvents method and
+    /// is used to verify that all the required events are defined.  If the
+    /// event set is determined to be invalid this method should throw a
     /// StateModelError.  As with the defineEvents method, each class within
     /// StateModelError.  As with the defineEvents method, each class within
-    /// the a StateModel derivation heirarchy must supply an implementation
-    /// which calls it's superclass's implemenation as well as verifying any
+    /// a StateModel derivation hierarchy must supply an implementation
+    /// which calls it's superclass's implementation as well as verifying any
     /// events added by the derivation.  Validating an event is accomplished
     /// events added by the derivation.  Validating an event is accomplished
-    /// by simply attempting to fetch en event by its value from the the 
-    /// event set.  An example of the derivation's implementation follows:
+    /// by simply attempting to fetch an event by its value from the event set.
+    /// An example of the derivation's implementation follows:
     ///
     ///
     /// @code
     /// @code
     /// void StateModelDerivation::verifyEvents() {
     /// void StateModelDerivation::verifyEvents() {
     ///     // Call the superclass implementation.
     ///     // Call the superclass implementation.
     ///     StateModelDerivation::verifyEvents();
     ///     StateModelDerivation::verifyEvents();
     ///
     ///
-    ///     // Verify the events defined by the derivation. 
-    ///     events_.get(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
-    ///     events_.get(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
+    ///     // Verify the events defined by the derivation.
+    ///     getEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
+    ///     getEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
     ///     :
     ///     :
     /// }
     /// }
     /// @endcode
     /// @endcode
     virtual void verifyEvents();
     virtual void verifyEvents();
 
 
-    /// @brief Populates the map of state handlers.
+    /// @brief Populates the set of states.
+    ///
+    /// This method is used to construct the set of valid states. Each class
+    /// within a StateModel derivation hierarchy uses this method to add any
+    /// states it defines to the set.  Each derivation's implementation must
+    /// also call it's superclass's implementation.  This allows each class
+    /// within the hierarchy to make contributions to the set of defined
+    /// states. Implementations use the method, defineState(), to add state
+    /// definitions.  An example of the derivation's implementation follows:
     ///
     ///
-    /// This method is used by derivations to construct a map of states to
-    /// their appropriate state handlers (bound method pointers).  It is
-    /// invoked at the beginning of startModel().
+    /// @code
+    /// void StateModelDerivation::defineStates() {
+    ///     // Call the superclass implementation.
+    ///     StateModelDerivation::defineStates();
     ///
     ///
-    /// Implementations should use the addToStateHandlerMap() method add
-    /// entries to the map.
-    virtual void initStateHandlerMap() = 0;
+    ///     // Add the states defined by the derivation.
+    ///     defineState(SOME_ST, "SOME_ST",
+    ///                 boost::bind(&StateModelDerivation::someHandler, this));
+    ///     :
+    /// }
+    /// @endcode
+    virtual void defineStates();
 
 
-    /// @brief Validates the contents of the state handler map.
+    /// @brief Adds an state value and associated label to the set of states.
     ///
     ///
-    /// This method is invoked immediately after initStateHandlerMap and
-    /// provides an opportunity for derivations to verify that the map
-    /// is correct.  If the map is determined to be invalid this method
-    /// should throw a StateModelError.
+    /// @param value is the numeric value of the state
+    /// @param label is the text label of the state used in log messages and
+    /// exceptions.
     ///
     ///
-    /// The simplest implementation would include a call to getStateHandler,
-    /// for each state the derivation supports.  For example, an implementation
-    /// which included three states, READY_ST, DO_WORK_ST, and DONE_ST could
-    /// implement this function as follows:
+    /// @throw StateModelError if the model has already been started, if
+    /// the value is already defined, or if the label is null or empty.
+    void defineState(unsigned int value, const char* label,
+                     StateHandler handler);
+
+    /// @brief Fetches the state referred to by value.
     ///
     ///
-    /// @code
-    ///    void verifyStateHandlerMap() {
-    ///        getStateHandler(READY_ST);
-    ///        getStateHandler(DO_WORK_ST);
-    ///        getStateHandler(DONE_ST);
-    ///    }
-    /// @endcode
+    /// @param value is the numeric value of the state desired.
+    ///
+    /// @return returns a constant pointer to the state if found
     ///
     ///
-    virtual void verifyStateHandlerMap() = 0;
+    /// @throw StateModelError if the state is not defined.
+    const StatePtr getState(unsigned int value);
 
 
-    /// @brief Adds an entry to the state handler map.
+    /// @brief Validates the contents of the set of states.
     ///
     ///
-    /// This method attempts to add an entry to the handler map which maps
-    /// the given handler to the given state.  The state handler must be
-    /// a bound member pointer to a handler method of derivation instance.
-    /// The following code snippet shows an example derivation and call to
-    /// addToStateHandlerMap() within its initStateHandlerMap() method.
+    /// This method is invoked immediately after the defineStates method and
+    /// is used to verify that all the required states are defined.  If the
+    /// state set is determined to be invalid this method should throw a
+    /// StateModelError.  As with the defineStates method, each class within
+    /// a StateModel derivation hierarchy must supply an implementation
+    /// which calls it's superclass's implementation as well as verifying any
+    /// states added by the derivation.  Validating an state is accomplished
+    /// by simply attempting to fetch the state by its value from the state set.
+    /// An example of the derivation's implementation follows:
     ///
     ///
     /// @code
     /// @code
-    /// class ExampleModel : public StateModel {
-    /// public:
-    /// :
-    /// void readyHandler() {
-    /// }
+    /// void StateModelDerivation::verifyStates() {
+    ///     // Call the superclass implementation.
+    ///     StateModelDerivation::verifyStates();
     ///
     ///
-    /// void initStateHandlerMap() {
-    ///     addToStateHandlerMap(READY_ST,
-    ///        boost::bind(&ExampleModel::readyHandler, this));
+    ///     // Verify the states defined by the derivation.
+    ///     getState(SOME_CUSTOM_EVT_2);
     ///     :
     ///     :
-    ///
+    /// }
     /// @endcode
     /// @endcode
-    ///
-    /// @param state the value of the state to which to map
-    /// @param handler the bound method pointer to the handler for the state
-    ///
-    /// @throw StateModelError if the map already contains an entry
-    /// for the given state, or if the model is beyond the NEW_ST.
-    void addToStateHandlerMap(unsigned int state, StateHandler handler);
+    virtual void verifyStates();
 
 
     /// @brief Handler for fatal model execution errors.
     /// @brief Handler for fatal model execution errors.
     ///
     ///
@@ -367,20 +466,9 @@ protected:
     /// It provides derivations an opportunity to act accordingly by setting
     /// It provides derivations an opportunity to act accordingly by setting
     /// the appropriate status or taking other remedial action.   This allows
     /// the appropriate status or taking other remedial action.   This allows
     /// the model execution loop to remain exception safe.
     /// the model execution loop to remain exception safe.
-    virtual void onModelFailure() = 0;
-
-    /// @brief Return the state handler for a given state.
-    ///
-    /// This method looks up the state handler for the given state from within
-    /// the state handler map.
     ///
     ///
-    /// @param state is the state constant of the desired handler.
-    ///
-    /// @return A StateHandler (bound member pointer) for the method that
-    /// handles the given state for this model.
-    ///
-    /// @throw StateModelError
-    StateHandler getStateHandler(unsigned int state);
+    /// @param explanation text detailing the error and state machine context
+    virtual void onModelFailure(const std::string& explanation) = 0;
 
 
     /// @brief Sets up the model to transition into given state with a given
     /// @brief Sets up the model to transition into given state with a given
     /// event.
     /// event.
@@ -402,7 +490,9 @@ protected:
     /// any sort of inconsistency such as attempting to reference an invalid
     /// any sort of inconsistency such as attempting to reference an invalid
     /// state, or if the next event is not valid for the current state, or a
     /// state, or if the next event is not valid for the current state, or a
     /// state handler throws an uncaught exception.
     /// state handler throws an uncaught exception.
-    void abortModel();
+    ///
+    /// @param explanation is text detailing the reason for aborting.
+    void abortModel(const std::string& explanation);
 
 
     /// @brief Sets the current state to the given state value.
     /// @brief Sets the current state to the given state value.
     ///
     ///
@@ -425,7 +515,7 @@ protected:
     ///
     ///
     /// @param the numeric event value to post as the next event.
     /// @param the numeric event value to post as the next event.
     ///
     ///
-    /// @throw StateModelError if the event is undefined 
+    /// @throw StateModelError if the event is undefined
     void postNextEvent(unsigned int event);
     void postNextEvent(unsigned int event);
 
 
     /// @brief Checks if on entry flag is true.
     /// @brief Checks if on entry flag is true.
@@ -458,7 +548,7 @@ public:
     /// loop.
     /// loop.
     ///
     ///
     /// @return An unsigned int representing the current state.
     /// @return An unsigned int representing the current state.
-    unsigned int getState() const;
+    unsigned int getCurrState() const;
 
 
     /// @brief Fetches the model's previous state.
     /// @brief Fetches the model's previous state.
     ///
     ///
@@ -507,49 +597,21 @@ public:
     /// event indicates a model violation, FAILED_EVT.
     /// event indicates a model violation, FAILED_EVT.
     bool didModelFail() const;
     bool didModelFail() const;
 
 
-    /// @brief Converts a state value into a text label.
-    ///
-    /// This method supplies labels for StateModel's predefined states. It is
-    /// declared virtual to allow derivations to embed a call to this method
-    /// within their own implementation which would define labels for its
-    /// states.  An example implementation might look like the following:
-    /// @code
-    ///
-    /// class DerivedModel : public StateModel {
-    ///     :
-    ///     static const int EXAMPLE_1_ST = SM_STATE_MAX + 1;
-    ///     :
-    ///     const char* getStateLabel(const int state) const {
-    ///         const char* str = "Unknown";
-    ///         switch(state) {
-    ///         case EXAMPLE_1_ST:
-    ///             str = "DerivedModel::EXAMPLE_1_ST";
-    ///             break;
-    ///         :
-    ///         default:
-    ///             // Not a derived state, pass it down to StateModel's method.
-    ///             str = StateModel::getStateLabel(state);
-    ///             break;
-    ///         }
-    ///
-    ///         return (str);
-    ///      }
-    ///
-    /// @endcode
-    ///
-    /// @param state is the state for which a label is desired.
-    ///
-    /// @return Returns a const char* containing the state label or
-    /// "Unknown" if the value cannot be mapped.
-    virtual const char* getStateLabel(const int state) const;
-
     /// @brief Fetches the label associated with an event value.
     /// @brief Fetches the label associated with an event value.
     ///
     ///
     /// @param event is the numeric event value for which the label is desired.
     /// @param event is the numeric event value for which the label is desired.
     ///
     ///
     /// @return Returns a const char* containing the event label or
     /// @return Returns a const char* containing the event label or
     /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
     /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
-    virtual const char* getEventLabel(const int event) const;
+    const char* getEventLabel(const int event) const;
+
+    /// @brief Fetches the label associated with an state value.
+    ///
+    /// @param state is the numeric state value for which the label is desired.
+    ///
+    /// @return Returns a const char* containing the state label or
+    /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
+    const char* getStateLabel(const int state) const;
 
 
     /// @brief Convenience method which returns a string rendition of the
     /// @brief Convenience method which returns a string rendition of the
     /// current state and next event.
     /// current state and next event.
@@ -572,14 +634,17 @@ public:
     std::string getPrevContextStr() const;
     std::string getPrevContextStr() const;
 
 
 private:
 private:
-    /// @brief Contains the set of defined Events.
+    /// @brief The dictionary of valid events.
     LabeledValueSet events_;
     LabeledValueSet events_;
 
 
-    /// @brief Contains a map of states to their state handlers.
-    StateHandlerMap state_handlers_;
+    /// @brief The dictionary of valid states.
+    StateSet states_;
+
+    /// @brief Indicates if the event and state dictionaries have been initted.
+    bool dictionaries_initted_;
 
 
     /// @brief The current state within the model's state model.
     /// @brief The current state within the model's state model.
-    unsigned int state_;
+    unsigned int curr_state_;
 
 
     /// @brief The previous state within the model's state model.
     /// @brief The previous state within the model's state model.
     unsigned int prev_state_;
     unsigned int prev_state_;

+ 41 - 135
src/bin/d2/tests/nc_trans_unittests.cc

@@ -131,65 +131,63 @@ public:
         }
         }
     }
     }
 
 
+    /// @brief Construct the event dictionary.
     virtual void defineEvents() {
     virtual void defineEvents() {
+        // Invoke the base call implementation first.
         NameChangeTransaction::defineEvents();
         NameChangeTransaction::defineEvents();
+
+        // Define our events.
         defineEvent(SEND_UPDATE_EVT, "SEND_UPDATE_EVT");
         defineEvent(SEND_UPDATE_EVT, "SEND_UPDATE_EVT");
     }
     }
 
 
+    /// @brief Verify the event dictionary.
     virtual void verifyEvents() {
     virtual void verifyEvents() {
+        // Invoke the base call implementation first.
         NameChangeTransaction::verifyEvents();
         NameChangeTransaction::verifyEvents();
+
+        // Define our events.
         getEvent(SEND_UPDATE_EVT);
         getEvent(SEND_UPDATE_EVT);
     }
     }
 
 
-    /// @brief Initializes the state handler map.
-    void initStateHandlerMap() {
-        addToStateHandlerMap(READY_ST,
+    /// @brief Construct the state dictionary.
+    virtual void defineStates() {
+        // Invoke the base call implementation first.
+        NameChangeTransaction::defineStates();
+
+        // Define our states.
+        defineState(READY_ST, "READY_ST",
                              boost::bind(&NameChangeStub::readyHandler, this));
                              boost::bind(&NameChangeStub::readyHandler, this));
 
 
-        addToStateHandlerMap(SELECTING_FWD_SERVER_ST,
+        defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
                              boost::bind(&NameChangeStub::dummyHandler, this));
                              boost::bind(&NameChangeStub::dummyHandler, this));
 
 
-        addToStateHandlerMap(SELECTING_REV_SERVER_ST,
+        defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
                              boost::bind(&NameChangeStub::dummyHandler, this));
                              boost::bind(&NameChangeStub::dummyHandler, this));
 
 
-        addToStateHandlerMap(DOING_UPDATE_ST,
+        defineState(DOING_UPDATE_ST, "DOING_UPDATE_ST",
                              boost::bind(&NameChangeStub::doingUpdateHandler,
                              boost::bind(&NameChangeStub::doingUpdateHandler,
                                          this));
                                          this));
 
 
-        addToStateHandlerMap(PROCESS_TRANS_OK_ST,
+        defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
                              boost::bind(&NameChangeStub::
                              boost::bind(&NameChangeStub::
                                          processTransDoneHandler, this));
                                          processTransDoneHandler, this));
 
 
-        addToStateHandlerMap(PROCESS_TRANS_FAILED_ST,
+        defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
                              boost::bind(&NameChangeStub::
                              boost::bind(&NameChangeStub::
                                          processTransDoneHandler, this));
                                          processTransDoneHandler, this));
     }
     }
 
 
-    void verifyStateHandlerMap() {
-        getStateHandler(READY_ST);
-        getStateHandler(DOING_UPDATE_ST);
-        // Call base class verification.
-        NameChangeTransaction::verifyStateHandlerMap();
-    }
-
-    const char* getStateLabel(const int state) const {
-        const char* str = "Unknown";
-        switch(state) {
-        case NameChangeStub::DOING_UPDATE_ST:
-            str = "NameChangeStub::DOING_UPDATE_ST";
-            break;
-        default:
-            str = NameChangeTransaction::getStateLabel(state);
-            break;
-        }
+    /// @brief Verify the event dictionary.
+    virtual void verifyStates() {
+        // Invoke the base call implementation first.
+        NameChangeTransaction::verifyStates();
 
 
-        return (str);
+        // Define our states.
+        getState(DOING_UPDATE_ST);
     }
     }
 
 
     // Expose the protected methods to be tested.
     // Expose the protected methods to be tested.
     using StateModel::runModel;
     using StateModel::runModel;
-    using StateModel::getStateHandler;
-
     using NameChangeTransaction::initServerSelection;
     using NameChangeTransaction::initServerSelection;
     using NameChangeTransaction::selectNextServer;
     using NameChangeTransaction::selectNextServer;
     using NameChangeTransaction::getCurrentServer;
     using NameChangeTransaction::getCurrentServer;
@@ -377,22 +375,23 @@ TEST_F(NameChangeTransactionTest, accessors) {
     EXPECT_TRUE(name_change->getReverseChangeCompleted());
     EXPECT_TRUE(name_change->getReverseChangeCompleted());
 }
 }
 
 
-/// @brief Tests state map initialization and validation.
-/// This tests the basic concept of state map initialization and verification
-/// by manually invoking the map methods normally by StateModel::startModel.
-TEST_F(NameChangeTransactionTest, stubStateMapInit) {
+/// @brief Tests event and state dictionary construction and verification.
+TEST_F(NameChangeTransactionTest, dictionaryCheck) {
     NameChangeStubPtr name_change;
     NameChangeStubPtr name_change;
     ASSERT_NO_THROW(name_change = makeCannedTransaction());
     ASSERT_NO_THROW(name_change = makeCannedTransaction());
 
 
-    // Verify that the map validation throws prior to the map being
-    // initialized.
-    ASSERT_THROW(name_change->verifyStateHandlerMap(), StateModelError);
+    // Verify that the event and state dictionary validation fails prior
+    // dictionary construction.
+    ASSERT_THROW(name_change->verifyEvents(), StateModelError);
+    ASSERT_THROW(name_change->verifyStates(), StateModelError);
 
 
-    // Call initStateHandlerMap to initialize the state map.
-    ASSERT_NO_THROW(name_change->initStateHandlerMap());
+    // Construct both dictionaries.
+    ASSERT_NO_THROW(name_change->defineEvents());
+    ASSERT_NO_THROW(name_change->defineStates());
 
 
-    // Verify that the map validation succeeds now that the map is initialized.
-    ASSERT_NO_THROW(name_change->verifyStateHandlerMap());
+    // Verify both event and state dictionaries now pass validation.
+    ASSERT_NO_THROW(name_change->verifyEvents());
+    ASSERT_NO_THROW(name_change->verifyStates());
 }
 }
 
 
 /// @brief Tests server selection methods.
 /// @brief Tests server selection methods.
@@ -511,106 +510,14 @@ TEST_F(NameChangeTransactionTest, serverSelectionTest) {
     EXPECT_EQ (passes, num_servers);
     EXPECT_EQ (passes, num_servers);
 }
 }
 
 
-/// @brief Tests the ability to decode state values into text labels.
-TEST_F(NameChangeTransactionTest, stateLabels) {
-    NameChangeStubPtr name_change;
-    ASSERT_NO_THROW(name_change = makeCannedTransaction());
-
-    // Verify StateModel labels.
-    EXPECT_EQ("StateModel::NEW_ST",
-              name_change->getStateLabel(StateModel::NEW_ST));
-    EXPECT_EQ("StateModel::END_ST",
-              name_change->getStateLabel(StateModel::END_ST));
-
-    // Verify NameChangeTransaction labels
-    EXPECT_EQ("NameChangeTransaction::READY_ST",
-              name_change->getStateLabel(NameChangeTransaction::READY_ST));
-    EXPECT_EQ("NameChangeTransaction::SELECTING_FWD_SERVER_ST",
-              name_change->getStateLabel(NameChangeTransaction::
-                                        SELECTING_FWD_SERVER_ST));
-    EXPECT_EQ("NameChangeTransaction::SELECTING_REV_SERVER_ST",
-              name_change->getStateLabel(NameChangeTransaction::
-                                         SELECTING_REV_SERVER_ST));
-    EXPECT_EQ("NameChangeTransaction::PROCESS_TRANS_OK_ST",
-              name_change->getStateLabel(NameChangeTransaction::
-                                         PROCESS_TRANS_OK_ST));
-    EXPECT_EQ("NameChangeTransaction::PROCESS_TRANS_FAILED_ST",
-              name_change->getStateLabel(NameChangeTransaction::
-                                         PROCESS_TRANS_FAILED_ST));
-
-    // Verify Stub states
-    EXPECT_EQ("NameChangeStub::DOING_UPDATE_ST",
-              name_change->getStateLabel(NameChangeStub::DOING_UPDATE_ST));
-
-    // Verify unknown state.
-    EXPECT_EQ("Unknown", name_change->getStateLabel(-1));
-}
-
-/// @brief Tests the ability to decode event values into text labels.
-TEST_F(NameChangeTransactionTest, eventLabels) {
-    NameChangeStubPtr name_change;
-    ASSERT_NO_THROW(name_change = makeCannedTransaction());
-
-    // Manually invoke event definition.
-    ASSERT_NO_THROW(name_change->defineEvents());
-    ASSERT_NO_THROW(name_change->verifyEvents());
-
-    // Verify StateModel labels.
-    EXPECT_EQ("NOP_EVT",
-              std::string(name_change->getEventLabel(StateModel::NOP_EVT)));
-    EXPECT_EQ("START_EVT",
-              std::string(name_change->getEventLabel(StateModel::START_EVT)));
-    EXPECT_EQ("END_EVT",
-              std::string(name_change->getEventLabel(StateModel::END_EVT)));
-    EXPECT_EQ("FAIL_EVT",
-              std::string(name_change->getEventLabel(StateModel::FAIL_EVT)));
-
-    // Verify NameChangeTransactionLabels
-    EXPECT_EQ("SELECT_SERVER_EVT",
-              std::string(name_change->getEventLabel(NameChangeTransaction::
-                                         SELECT_SERVER_EVT)));
-    EXPECT_EQ("SERVER_SELECTED_EVT",
-              std::string(name_change->getEventLabel(NameChangeTransaction::
-                                         SERVER_SELECTED_EVT)));
-    EXPECT_EQ("SERVER_IO_ERROR_EVT",
-              std::string(name_change->getEventLabel(NameChangeTransaction::
-                                         SERVER_IO_ERROR_EVT)));
-    EXPECT_EQ("NO_MORE_SERVERS_EVT",
-              std::string(name_change->getEventLabel(NameChangeTransaction::
-                                         NO_MORE_SERVERS_EVT)));
-    EXPECT_EQ("IO_COMPLETED_EVT",
-              std::string(name_change->getEventLabel(NameChangeTransaction::
-                                         IO_COMPLETED_EVT)));
-    EXPECT_EQ("UPDATE_OK_EVT",
-              std::string(name_change->getEventLabel(NameChangeTransaction::
-                                         UPDATE_OK_EVT)));
-    EXPECT_EQ("UPDATE_FAILED_EVT",
-              std::string(name_change->getEventLabel(NameChangeTransaction::
-                                         UPDATE_FAILED_EVT)));
-
-    // Verify stub class labels.
-    EXPECT_EQ("SEND_UPDATE_EVT",
-              std::string(name_change->getEventLabel(NameChangeStub::
-                                                     SEND_UPDATE_EVT)));
-
-    // Verify undefined event label.
-    EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL, name_change->getEventLabel(-1));
-}
-
 /// @brief Tests that the transaction will be "failed" upon model errors.
 /// @brief Tests that the transaction will be "failed" upon model errors.
 TEST_F(NameChangeTransactionTest, modelFailure) {
 TEST_F(NameChangeTransactionTest, modelFailure) {
     NameChangeStubPtr name_change;
     NameChangeStubPtr name_change;
     ASSERT_NO_THROW(name_change = makeCannedTransaction());
     ASSERT_NO_THROW(name_change = makeCannedTransaction());
 
 
-    // We will cause a model failure by attempting to submit an event to
-    // NEW_ST.  Let's make sure that state is NEW_ST and that NEW_ST has no
-    // handler.
-    ASSERT_EQ(NameChangeTransaction::NEW_ST, name_change->getState());
-    ASSERT_THROW(name_change->getStateHandler(NameChangeTransaction::NEW_ST),
-                 StateModelError);
-
-    // Now call runStateModel() which should not throw.
-    EXPECT_NO_THROW(name_change->runModel(NameChangeTransaction::START_EVT));
+    // Now call runModel() with an undefined event which should not throw,
+    // but should result in a failed model and failed transaction.
+    EXPECT_NO_THROW(name_change->runModel(9999));
 
 
     // Verify that the model reports are done but failed.
     // Verify that the model reports are done but failed.
     EXPECT_TRUE(name_change->isModelDone());
     EXPECT_TRUE(name_change->isModelDone());
@@ -689,5 +596,4 @@ TEST_F(NameChangeTransactionTest, failedUpdateTest) {
     EXPECT_FALSE(name_change->getForwardChangeCompleted());
     EXPECT_FALSE(name_change->getForwardChangeCompleted());
 }
 }
 
 
-
 }
 }

+ 371 - 241
src/bin/d2/tests/state_model_unittests.cc

@@ -56,11 +56,17 @@ public:
     ///@brief Event issued when all the work is done.
     ///@brief Event issued when all the work is done.
     static const int ALL_DONE_EVT = SM_EVENT_MAX + 3;
     static const int ALL_DONE_EVT = SM_EVENT_MAX + 3;
 
 
+    ///@brief Event used to trigger an attempt to transition to bad state
+    static const int FORCE_UNDEFINED_ST_EVT = SM_EVENT_MAX + 4;
+
+    ///@brief Event used to trigger an attempt to transition to bad state
+    static const int SIMULATE_ERROR_EVT = SM_EVENT_MAX + 5;
+
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
     /// Parameters match those needed by StateModel.
     /// Parameters match those needed by StateModel.
-    StateModelTest() : dummy_called_(false), model_failure_called_(false),
-                      work_completed_(false) {
+    StateModelTest() : dummy_called_(false), work_completed_(false),
+                       failure_explanation_("") {
     }
     }
     /// @brief Destructor
     /// @brief Destructor
     virtual ~StateModelTest() {
     virtual ~StateModelTest() {
@@ -79,13 +85,12 @@ public:
         dummy_called_ = true;
         dummy_called_ = true;
     }
     }
 
 
-    /// @brief Returns indication of whether or not the model failed to execute.
+    /// @brief Returns the failure explanation string.
     ///
     ///
-    /// If true, this indicates that the test model suffered an error
-    /// during execution such as an invalid state, event, or exception thrown by
-    /// a state handler. The flag is only set true by onModelFailure() method.
-    bool getModelFailureCalled() {
-        return (model_failure_called_);
+    /// This value is set only via onModelFailure and it stores whatever
+    /// explanation that method was passed.
+    const std::string& getFailureExplanation() {
+        return (failure_explanation_);
     }
     }
 
 
     /// @brief Returns indication of whether or not the model succeeded.
     /// @brief Returns indication of whether or not the model succeeded.
@@ -131,6 +136,12 @@ public:
             work_completed_ = true;
             work_completed_ = true;
             transition(DONE_ST, ALL_DONE_EVT);
             transition(DONE_ST, ALL_DONE_EVT);
             break;
             break;
+        case FORCE_UNDEFINED_ST_EVT:
+            transition(9999, ALL_DONE_EVT);
+            break;
+        case SIMULATE_ERROR_EVT:
+            throw std::logic_error("Simulated Unexpected Error");
+            break;
         default:
         default:
             // its bogus
             // its bogus
             isc_throw(StateModelError, "doWorkHandler:invalid event: "
             isc_throw(StateModelError, "doWorkHandler:invalid event: "
@@ -154,79 +165,116 @@ public:
         }
         }
     }
     }
 
 
+    /// @brief Construct the event dictionary.
     virtual void defineEvents() {
     virtual void defineEvents() {
+        // Invoke the base call implementation first.
         StateModel::defineEvents();
         StateModel::defineEvents();
+
+        // Define our events.
         defineEvent(WORK_START_EVT, "WORK_START_EVT");
         defineEvent(WORK_START_EVT, "WORK_START_EVT");
         defineEvent(WORK_DONE_EVT , "WORK_DONE_EVT");
         defineEvent(WORK_DONE_EVT , "WORK_DONE_EVT");
         defineEvent(ALL_DONE_EVT, "ALL_DONE_EVT");
         defineEvent(ALL_DONE_EVT, "ALL_DONE_EVT");
+        defineEvent(FORCE_UNDEFINED_ST_EVT, "FORCE_UNDEFINED_ST_EVT");
+        defineEvent(SIMULATE_ERROR_EVT, "SIMULATE_ERROR_EVT");
     }
     }
 
 
+    /// @brief Verify the event dictionary.
     virtual void verifyEvents() {
     virtual void verifyEvents() {
+        // Invoke the base call implementation first.
         StateModel::verifyEvents();
         StateModel::verifyEvents();
+
+        // Verify our events.
         getEvent(WORK_START_EVT);
         getEvent(WORK_START_EVT);
         getEvent(WORK_DONE_EVT);
         getEvent(WORK_DONE_EVT);
         getEvent(ALL_DONE_EVT);
         getEvent(ALL_DONE_EVT);
+        getEvent(FORCE_UNDEFINED_ST_EVT);
+        getEvent(SIMULATE_ERROR_EVT);
     }
     }
 
 
-    /// @brief Initializes the state handler map.
-    virtual void initStateHandlerMap() {
-        addToStateHandlerMap(DUMMY_ST,
-            boost::bind(&StateModelTest::dummyHandler, this));
+    /// @brief Construct the state dictionary.
+    virtual void defineStates() {
+        // Invoke the base call implementation first.
+        StateModel::defineStates();
+
+        // Define our states.
+        defineState(DUMMY_ST, "DUMMY_ST",
+                    boost::bind(&StateModelTest::dummyHandler, this));
 
 
-        addToStateHandlerMap(READY_ST,
+        defineState(READY_ST, "READY_ST",
             boost::bind(&StateModelTest::readyHandler, this));
             boost::bind(&StateModelTest::readyHandler, this));
 
 
-        addToStateHandlerMap(DO_WORK_ST,
+        defineState(DO_WORK_ST, "DO_WORK_ST",
             boost::bind(&StateModelTest::doWorkHandler, this));
             boost::bind(&StateModelTest::doWorkHandler, this));
 
 
-        addToStateHandlerMap(DONE_ST,
+        defineState(DONE_ST, "DONE_ST",
             boost::bind(&StateModelTest::doneWorkHandler, this));
             boost::bind(&StateModelTest::doneWorkHandler, this));
     }
     }
 
 
-    /// @brief Validates the contents of the state handler map.
-    virtual void verifyStateHandlerMap() {
-        getStateHandler(DUMMY_ST);
-        getStateHandler(READY_ST);
-        getStateHandler(DO_WORK_ST);
-        getStateHandler(DONE_ST);
+    /// @brief Verify the state dictionary.
+    virtual void verifyStates() {
+        // Invoke the base call implementation first.
+        StateModel::verifyStates();
+
+        // Verify our states.
+        getState(DUMMY_ST);
+        getState(READY_ST);
+        getState(DO_WORK_ST);
+        getState(DONE_ST);
     }
     }
 
 
-    /// @brief  Handler called when the model suffers an execution error.
-    virtual void onModelFailure() {
-        model_failure_called_ = true;
+    /// @brief  Manually construct the event and state dictionaries.
+    /// This allows testing without running startModel.
+    void initDictionaires() {
+        ASSERT_NO_THROW(defineEvents());
+        ASSERT_NO_THROW(verifyEvents());
+        ASSERT_NO_THROW(defineStates());
+        ASSERT_NO_THROW(verifyStates());
     }
     }
 
 
-    const char* getStateLabel(const int state) const {
-        const char* str = "Unknown";
-        switch(state) {
-        case DUMMY_ST:
-            str = "StateModelTest::DUMMY_ST";
-            break;
-        case READY_ST:
-            str = "StateModelTest::READY_ST";
-            break;
-        case DO_WORK_ST:
-            str = "StateModelTest::DO_WORK_ST";
-            break;
-        case DONE_ST:
-            str = "StateModelTest::DONE_ST";
-            break;
-        default:
-            str = StateModel::getStateLabel(state);
-            break;
+    /// @brief Tests the event dictionary entry for the given event value.
+    bool checkEvent(const int value, const std::string& label) {
+        EventPtr event;
+        try  {
+            event = getEvent(value);
+            EXPECT_TRUE(event);
+            EXPECT_EQ(value, event->getValue());
+            EXPECT_EQ(label, std::string(event->getLabel()));
+        } catch (const std::exception& ex) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /// @brief Tests the state dictionary entry for the given state value.
+    bool checkState(const int value, const std::string& label) {
+        EventPtr state;
+        try  {
+            state = getState(value);
+            EXPECT_TRUE(state);
+            EXPECT_EQ(value, state->getValue());
+            EXPECT_EQ(label, std::string(state->getLabel()));
+        } catch (const std::exception& ex) {
+            return false;
         }
         }
 
 
-        return (str);
+        return true;
+    }
+
+
+    /// @brief  Handler called when the model suffers an execution error.
+    virtual void onModelFailure(const std::string& explanation) {
+        failure_explanation_ = explanation;
     }
     }
 
 
     /// @brief Indicator of whether or not the DUMMY_ST handler has been called.
     /// @brief Indicator of whether or not the DUMMY_ST handler has been called.
     bool dummy_called_;
     bool dummy_called_;
 
 
-    /// @brief Indicator of whether or not onModelError has been called.
-    bool model_failure_called_;
-
     /// @brief Indicator of whether or not DONE_ST handler has been called.
     /// @brief Indicator of whether or not DONE_ST handler has been called.
     bool work_completed_;
     bool work_completed_;
+
+    /// @brief Stores the failure explanation
+    std::string failure_explanation_;
 };
 };
 
 
 // Declare them so gtest can see them.
 // Declare them so gtest can see them.
@@ -238,129 +286,173 @@ const int StateModelTest::WORK_START_EVT;
 const int StateModelTest::WORK_DONE_EVT;
 const int StateModelTest::WORK_DONE_EVT;
 const int StateModelTest::ALL_DONE_EVT;
 const int StateModelTest::ALL_DONE_EVT;
 
 
-/// @brief Tests the fundamental methods used for state handler mapping.
-/// Verifies the ability to search for and add entries in the state handler map.
-TEST_F(StateModelTest, basicStateMapping) {
-    // After construction, the state map should be empty. Verify that
-    // getStateHandler will throw when, state cannot be found.
-    EXPECT_THROW(getStateHandler(READY_ST), StateModelError);
+/// @brief Checks the fundamentals of defining and retrieving events.
+TEST_F(StateModelTest, eventDefinition) {
+    // After construction, the event dictionary should be empty. Verify that
+    // getEvent will throw when event is not defined.
+    EXPECT_THROW(getEvent(NOP_EVT), StateModelError);
 
 
     // Verify that we can add a handler to the map.
     // Verify that we can add a handler to the map.
-    ASSERT_NO_THROW(addToStateHandlerMap(READY_ST,
-                                         boost::bind(&StateModelTest::
-                                                     dummyHandler, this)));
+    ASSERT_NO_THROW(defineEvent(NOP_EVT, "NOP_EVT"));
 
 
-    // Verify that we can find the handler by its state.
-    StateHandler retreived_handler;
-    EXPECT_NO_THROW(retreived_handler = getStateHandler(READY_ST));
+    // Verify that we can find the event by value and its content is correct.
+    EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
 
 
-    // Now we will Verify that retrieved handler executes the correct method.
-    // Make sure the dummy called flag is false prior to invocation.
-    EXPECT_FALSE(getDummyCalled());
-
-    // Invoke the retreived handler.
-    ASSERT_NO_THROW((retreived_handler)());
+    // Verify that we cannot add a duplicate.
+    ASSERT_THROW(defineEvent(NOP_EVT, "NOP_EVT"), StateModelError);
 
 
-    // Verify the dummy called flag is now true.
-    EXPECT_TRUE(getDummyCalled());
+    // Verify that we can still find the event.
+    EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
+}
 
 
-    // Verify that we cannot add a duplicate.
-    EXPECT_THROW(addToStateHandlerMap(READY_ST,
-                                      boost::bind(&StateModelTest::readyHandler,
-                                                  this)),
-                 StateModelError);
+/// @brief Tests event dictionary construction and verification.
+TEST_F(StateModelTest, eventDictionary) {
+    // After construction, the event dictionary should be empty.
+    // Make sure that verifyEvents() throws.
+    EXPECT_THROW(verifyEvents(), StateModelError);
+
+    // Construct the dictionary and verify it.
+    EXPECT_NO_THROW(defineEvents());
+    EXPECT_NO_THROW(verifyEvents());
+
+    // Verify base class events are defined.
+    EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
+    EXPECT_TRUE(checkEvent(START_EVT, "START_EVT"));
+    EXPECT_TRUE(checkEvent(END_EVT, "END_EVT"));
+    EXPECT_TRUE(checkEvent(FAIL_EVT, "FAIL_EVT"));
+
+    // Verify stub class events are defined.
+    EXPECT_TRUE(checkEvent(WORK_START_EVT, "WORK_START_EVT"));
+    EXPECT_TRUE(checkEvent(WORK_DONE_EVT, "WORK_DONE_EVT"));
+    EXPECT_TRUE(checkEvent(ALL_DONE_EVT, "ALL_DONE_EVT"));
+    EXPECT_TRUE(checkEvent(FORCE_UNDEFINED_ST_EVT, "FORCE_UNDEFINED_ST_EVT"));
+    EXPECT_TRUE(checkEvent(SIMULATE_ERROR_EVT, "SIMULATE_ERROR_EVT"));
+
+    // Verify that undefined events are handled correctly.
+    EXPECT_THROW(getEvent(9999), StateModelError);
+    EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL, getEventLabel(9999));
+}
 
 
-    // Verify that we can still find the handler by its state.
-    EXPECT_NO_THROW(getStateHandler(READY_ST));
+/// @brief General testing of event context accessors.
+/// Most if not all of these are also tested as a byproduct off larger tests.
+TEST_F(StateModelTest, eventContextAccessors) {
+    // Construct the event definitions, normally done by startModel.
+    ASSERT_NO_THROW(defineEvents());
+    ASSERT_NO_THROW(verifyEvents());
 
 
+    // Verify the post-construction values.
+    EXPECT_EQ(NOP_EVT, getNextEvent());
+    EXPECT_EQ(NOP_EVT, getLastEvent());
 
 
-    // Verify that we cannot add a handler for NEW_ST.
-    EXPECT_THROW(addToStateHandlerMap(NEW_ST,
-                                      boost::bind(&StateModelTest::dummyHandler,
-                                                  this)),
-                 StateModelError);
+    // Call setEvent which will update both next event and last event.
+    EXPECT_NO_THROW(postNextEvent(START_EVT));
 
 
-    // Verify that we cannot add a handler for END_ST.
-    EXPECT_THROW(addToStateHandlerMap(END_ST,
-                                      boost::bind(&StateModelTest::dummyHandler,
-                                                  this)),
-                 StateModelError);
-}
+    // Verify the values are what we expect.
+    EXPECT_EQ(START_EVT, getNextEvent());
+    EXPECT_EQ(NOP_EVT, getLastEvent());
 
 
-/// @brief Tests state map initialization and validation.
-/// This tests the basic concept of state map initialization and verification
-/// by manually invoking the map methods normally called by startModel.
-TEST_F(StateModelTest, stateMapInit) {
-    // Verify that the map validation throws prior to the map being
-    // initialized.
-    EXPECT_THROW(verifyStateHandlerMap(), StateModelError);
+    // Call setEvent again.
+    EXPECT_NO_THROW(postNextEvent(WORK_START_EVT));
 
 
-    // Call initStateHandlerMap to initialize the state map.
-    ASSERT_NO_THROW(initStateHandlerMap());
+    // Verify the values are what we expect.
+    EXPECT_EQ(WORK_START_EVT, getNextEvent());
+    EXPECT_EQ(START_EVT, getLastEvent());
 
 
-    // Verify that the map validation succeeds now that the map is initialized.
-    EXPECT_NO_THROW(verifyStateHandlerMap());
+    // Verify that posting an undefined event throws.
+    EXPECT_THROW(postNextEvent(9999), StateModelError);
 }
 }
 
 
-/// @brief Tests the ability to decode state values into text labels.
-TEST_F(StateModelTest, stateLabels) {
-    // Verify base class labels.
-    EXPECT_EQ("StateModel::NEW_ST", getStateLabel(NEW_ST));
-    EXPECT_EQ("StateModel::END_ST", getStateLabel(END_ST));
-
-    // Verify stub class labels.
-    EXPECT_EQ("StateModelTest::DUMMY_ST", getStateLabel(DUMMY_ST));
-    EXPECT_EQ("StateModelTest::READY_ST", getStateLabel(READY_ST));
-    EXPECT_EQ("StateModelTest::DO_WORK_ST", getStateLabel(DO_WORK_ST));
-    EXPECT_EQ("StateModelTest::DONE_ST", getStateLabel(DONE_ST));
+/// @brief Tests the fundamental methods used for state handler mapping.
+/// Verifies the ability to search for and add entries in the state handler map.
+TEST_F(StateModelTest, stateDefinition) {
+    // After construction, the state dictionary should be empty. Verify that
+    // getState will throw when, state is not defined.
+    EXPECT_THROW(getState(READY_ST), StateModelError);
+
+    // Verify that we can add a state to the dictionary.
+    ASSERT_NO_THROW(defineState(READY_ST, "READY_ST",
+                                boost::bind(&StateModelTest::dummyHandler,
+                                            this)));
+
+    // Verify that we can find the state by its value.
+    StatePtr state;
+    EXPECT_NO_THROW(state = getState(READY_ST));
+    EXPECT_TRUE(state);
+
+    // Verify the state's value and label.
+    EXPECT_EQ(READY_ST, state->getValue());
+    EXPECT_EQ("READY_ST", std::string(state->getLabel()));
+
+    // Now verify that retrieved state's handler executes the correct method.
+    // Make sure the dummy called flag is false prior to invocation.
+    EXPECT_FALSE(getDummyCalled());
 
 
-    // Verify unknown state.
-    EXPECT_EQ("Unknown", getStateLabel(-1));
-}
+    // Invoke the state's handler.
+    EXPECT_NO_THROW(state->run());
 
 
-/// @brief Tests the ability to decode event values into text labels.
-TEST_F(StateModelTest, eventLabels) {
-    // Verify base class labels.
-    ASSERT_NO_THROW(defineEvents());
-    ASSERT_NO_THROW(verifyEvents());
+    // Verify the dummy called flag is now true.
+    EXPECT_TRUE(getDummyCalled());
 
 
-    EXPECT_EQ("NOP_EVT", std::string(getEventLabel(NOP_EVT)));
-    EXPECT_EQ("START_EVT", std::string(getEventLabel(START_EVT)));
-    EXPECT_EQ("END_EVT", std::string(getEventLabel(END_EVT)));
-    EXPECT_EQ("FAIL_EVT", std::string(getEventLabel(FAIL_EVT)));
+    // Verify that we cannot add a duplicate.
+    EXPECT_THROW(defineState(READY_ST, "READY_ST",
+                             boost::bind(&StateModelTest::readyHandler, this)),
+                 StateModelError);
 
 
-    // Verify stub class labels.
-    EXPECT_EQ("WORK_START_EVT", std::string(getEventLabel(WORK_START_EVT)));
-    EXPECT_EQ("WORK_DONE_EVT", std::string(getEventLabel(WORK_DONE_EVT)));
-    EXPECT_EQ("ALL_DONE_EVT", std::string(getEventLabel(ALL_DONE_EVT)));
+    // Verify that we can still find the state.
+    EXPECT_NO_THROW(getState(READY_ST));
+}
 
 
-    // Verify unknown state.
-    EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL, getEventLabel(-1));
+/// @brief Tests state dictionary initialization and validation.
+/// This tests the basic concept of state dictionary initialization and
+/// verification by manually invoking the methods normally called by startModel.
+TEST_F(StateModelTest, stateDictionary) {
+    // Verify that the map validation throws prior to the dictionary being
+    // initialized.
+    EXPECT_THROW(verifyStates(), StateModelError);
+
+    // Construct the dictionary and verify it.
+    ASSERT_NO_THROW(defineStates());
+    EXPECT_NO_THROW(verifyStates());
+
+    // Verify the base class states.
+    EXPECT_TRUE(checkState(NEW_ST, "NEW_ST"));
+    EXPECT_TRUE(checkState(END_ST, "END_ST"));
+
+    // Verify stub class states.
+    EXPECT_TRUE(checkState(DUMMY_ST, "DUMMY_ST"));
+    EXPECT_TRUE(checkState(READY_ST, "READY_ST"));
+    EXPECT_TRUE(checkState(DO_WORK_ST, "DO_WORK_ST"));
+    EXPECT_TRUE(checkState(DONE_ST, "DONE_ST"));
+
+    // Verify that undefined states are handled correctly.
+    EXPECT_THROW(getState(9999), StateModelError);
+    EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL,
+              std::string(getStateLabel(9999)));
 }
 }
 
 
-/// @brief General testing of member accessors.
+/// @brief General testing of state context accessors.
 /// Most if not all of these are also tested as a byproduct off larger tests.
 /// Most if not all of these are also tested as a byproduct off larger tests.
-TEST_F(StateModelTest, stateAccessors) {
+TEST_F(StateModelTest, stateContextAccessors) {
     // setState will throw unless we initialize the handler map.
     // setState will throw unless we initialize the handler map.
-    ASSERT_NO_THROW(initStateHandlerMap());
-    ASSERT_NO_THROW(verifyStateHandlerMap());
+    ASSERT_NO_THROW(defineStates());
+    ASSERT_NO_THROW(verifyStates());
 
 
     // Verify post-construction state values.
     // Verify post-construction state values.
-    EXPECT_EQ(NEW_ST, getState());
+    EXPECT_EQ(NEW_ST, getCurrState());
     EXPECT_EQ(NEW_ST, getPrevState());
     EXPECT_EQ(NEW_ST, getPrevState());
 
 
     // Call setState which will update both state and previous state.
     // Call setState which will update both state and previous state.
     EXPECT_NO_THROW(setState(READY_ST));
     EXPECT_NO_THROW(setState(READY_ST));
 
 
     // Verify the values are what we expect.
     // Verify the values are what we expect.
-    EXPECT_EQ(READY_ST, getState());
+    EXPECT_EQ(READY_ST, getCurrState());
     EXPECT_EQ(NEW_ST, getPrevState());
     EXPECT_EQ(NEW_ST, getPrevState());
 
 
     // Call setState again.
     // Call setState again.
     EXPECT_NO_THROW(setState(DO_WORK_ST));
     EXPECT_NO_THROW(setState(DO_WORK_ST));
 
 
     // Verify the values are what we expect.
     // Verify the values are what we expect.
-    EXPECT_EQ(DO_WORK_ST, getState());
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
     EXPECT_EQ(READY_ST, getPrevState());
     EXPECT_EQ(READY_ST, getPrevState());
 
 
     // Verify that calling setState with an state that has no handler
     // Verify that calling setState with an state that has no handler
@@ -372,45 +464,39 @@ TEST_F(StateModelTest, stateAccessors) {
 
 
     // Verify that calling setState with END_ST is ok.
     // Verify that calling setState with END_ST is ok.
     EXPECT_NO_THROW(setState(END_ST));
     EXPECT_NO_THROW(setState(END_ST));
-}
-
-TEST_F(StateModelTest, eventAccessors) {
-    // Construct the event definitions, normally done by startModel.
-    ASSERT_NO_THROW(defineEvents());
-    ASSERT_NO_THROW(verifyEvents());
 
 
-    EXPECT_EQ(NOP_EVT, getNextEvent());
-    EXPECT_EQ(NOP_EVT, getLastEvent());
+    // Verify that calling setState with an undefined state throws.
+    EXPECT_THROW(setState(9999), StateModelError);
+}
 
 
-    // Call setEvent which will update both next event and last event.
-    EXPECT_NO_THROW(postNextEvent(START_EVT));
+/// @brief Checks that invoking runModel prior to startModel is not allowed.
+TEST_F(StateModelTest, runBeforeStart) {
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
 
 
-    // Verify the values are what we expect.
-    EXPECT_EQ(START_EVT, getNextEvent());
-    EXPECT_EQ(NOP_EVT, getLastEvent());
+    // Attempt to call runModel before startModel.  This should result in an
+    // orderly model failure.
+    ASSERT_NO_THROW(runModel(START_EVT));
 
 
-    // Call setEvent again.
-    EXPECT_NO_THROW(postNextEvent(WORK_START_EVT));
-    EXPECT_EQ(WORK_START_EVT, getNextEvent());
-    EXPECT_EQ(START_EVT, getLastEvent());
+    // Check that state and event are correct.
+    EXPECT_EQ(END_ST, getCurrState());
+    EXPECT_EQ(FAIL_EVT, getNextEvent());
 
 
-    // Verify the values are what we expect.
+    // Verify that failure explanation is not empty.
+    EXPECT_FALSE(getFailureExplanation().empty());
 }
 }
 
 
+/// @brief Tests that the endModel may be used to transition the model to
+/// a normal conclusion.
 TEST_F(StateModelTest, transitionWithEnd) {
 TEST_F(StateModelTest, transitionWithEnd) {
-    // Construct the event definitions, normally done by startModel.
-    ASSERT_NO_THROW(defineEvents());
-    ASSERT_NO_THROW(verifyEvents());
-
-    // Manually init the handlers map.
-    ASSERT_NO_THROW(initStateHandlerMap());
-    ASSERT_NO_THROW(verifyStateHandlerMap());
+    // Init dictionaries manually, normally done by startModel.
+    initDictionaires();
 
 
     // call transition to move from NEW_ST to DUMMY_ST with START_EVT
     // call transition to move from NEW_ST to DUMMY_ST with START_EVT
     EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
     EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
 
 
     //  Verify that state and event members are as expected.
     //  Verify that state and event members are as expected.
-    EXPECT_EQ(DUMMY_ST, getState());
+    EXPECT_EQ(DUMMY_ST, getCurrState());
     EXPECT_EQ(NEW_ST, getPrevState());
     EXPECT_EQ(NEW_ST, getPrevState());
     EXPECT_EQ(START_EVT, getNextEvent());
     EXPECT_EQ(START_EVT, getNextEvent());
     EXPECT_EQ(NOP_EVT, getLastEvent());
     EXPECT_EQ(NOP_EVT, getLastEvent());
@@ -419,48 +505,42 @@ TEST_F(StateModelTest, transitionWithEnd) {
     EXPECT_NO_THROW(endModel());
     EXPECT_NO_THROW(endModel());
 
 
     // Verify state and event members are correctly set.
     // Verify state and event members are correctly set.
-    EXPECT_EQ(END_ST, getState());
+    EXPECT_EQ(END_ST, getCurrState());
     EXPECT_EQ(DUMMY_ST, getPrevState());
     EXPECT_EQ(DUMMY_ST, getPrevState());
     EXPECT_EQ(END_EVT, getNextEvent());
     EXPECT_EQ(END_EVT, getNextEvent());
     EXPECT_EQ(START_EVT, getLastEvent());
     EXPECT_EQ(START_EVT, getLastEvent());
 }
 }
 
 
+/// @brief Tests that the abortModel may be used to transition the model to
+/// failed conclusion.
 TEST_F(StateModelTest, transitionWithAbort) {
 TEST_F(StateModelTest, transitionWithAbort) {
-    // Construct the event definitions, normally done by startModel.
-    ASSERT_NO_THROW(defineEvents());
-    ASSERT_NO_THROW(verifyEvents());
-
-    // Manually init the handlers map.
-    ASSERT_NO_THROW(initStateHandlerMap());
-    ASSERT_NO_THROW(verifyStateHandlerMap());
+    // Init dictionaries manually, normally done by startModel.
+    initDictionaires();
 
 
     // call transition to move from NEW_ST to DUMMY_ST with START_EVT
     // call transition to move from NEW_ST to DUMMY_ST with START_EVT
     EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
     EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
 
 
     //  Verify that state and event members are as expected.
     //  Verify that state and event members are as expected.
-    EXPECT_EQ(DUMMY_ST, getState());
+    EXPECT_EQ(DUMMY_ST, getCurrState());
     EXPECT_EQ(NEW_ST, getPrevState());
     EXPECT_EQ(NEW_ST, getPrevState());
     EXPECT_EQ(START_EVT, getNextEvent());
     EXPECT_EQ(START_EVT, getNextEvent());
     EXPECT_EQ(NOP_EVT, getLastEvent());
     EXPECT_EQ(NOP_EVT, getLastEvent());
 
 
     // Call endModel to transition us to the end of the model.
     // Call endModel to transition us to the end of the model.
-    EXPECT_NO_THROW(abortModel());
+    EXPECT_NO_THROW(abortModel("test invocation"));
 
 
     // Verify state and event members are correctly set.
     // Verify state and event members are correctly set.
-    EXPECT_EQ(END_ST, getState());
+    EXPECT_EQ(END_ST, getCurrState());
     EXPECT_EQ(DUMMY_ST, getPrevState());
     EXPECT_EQ(DUMMY_ST, getPrevState());
     EXPECT_EQ(FAIL_EVT, getNextEvent());
     EXPECT_EQ(FAIL_EVT, getNextEvent());
     EXPECT_EQ(START_EVT, getLastEvent());
     EXPECT_EQ(START_EVT, getLastEvent());
 }
 }
 
 
+/// @brief Tests that the boolean indicators for on state entry and exit
+/// work properly.
 TEST_F(StateModelTest, doFlags) {
 TEST_F(StateModelTest, doFlags) {
-    // Construct the event definitions, normally done by startModel.
-    ASSERT_NO_THROW(defineEvents());
-    ASSERT_NO_THROW(verifyEvents());
-
-    // Manually init the handlers map.
-    ASSERT_NO_THROW(initStateHandlerMap());
-    ASSERT_NO_THROW(verifyStateHandlerMap());
+    // Init dictionaries manually, normally done by startModel.
+    initDictionaires();
 
 
     // Verify that "do" flags are false.
     // Verify that "do" flags are false.
     EXPECT_FALSE(doOnEntry());
     EXPECT_FALSE(doOnEntry());
@@ -486,14 +566,12 @@ TEST_F(StateModelTest, doFlags) {
 
 
 }
 }
 
 
+/// @brief Verifies that the model status methods accurately reflect the model
+/// status.  It also verifies that the dictionaries can be modified before
+/// the model is running but not after.
 TEST_F(StateModelTest, statusMethods) {
 TEST_F(StateModelTest, statusMethods) {
-    // Construct the event definitions, normally done by startModel.
-    ASSERT_NO_THROW(defineEvents());
-    ASSERT_NO_THROW(verifyEvents());
-
-    // Manually init the handlers map.
-    ASSERT_NO_THROW(initStateHandlerMap());
-    ASSERT_NO_THROW(verifyStateHandlerMap());
+    // Init dictionaries manually, normally done by startModel.
+    initDictionaires();
 
 
     // After construction, state model is "new", all others should be false.
     // After construction, state model is "new", all others should be false.
     EXPECT_TRUE(isModelNew());
     EXPECT_TRUE(isModelNew());
@@ -502,19 +580,23 @@ TEST_F(StateModelTest, statusMethods) {
     EXPECT_FALSE(isModelDone());
     EXPECT_FALSE(isModelDone());
     EXPECT_FALSE(didModelFail());
     EXPECT_FALSE(didModelFail());
 
 
-    // verify that you can add to the before the model has started.
-    EXPECT_NO_THROW(addToStateHandlerMap(9998,
-                                         boost::bind(&StateModelTest::
-                                                     readyHandler, this)));
+    // Verify that events and states can be added before the model is started.
+    EXPECT_NO_THROW(defineEvent(9998, "9998"));
+    EXPECT_NO_THROW(defineState(9998, "9998",
+                                boost::bind(&StateModelTest::readyHandler,
+                                            this)));
 
 
-    // call transition to move from NEW_ST to DUMMY_ST with START_EVT
+    // "START" the model.
+    // Fake out starting the model by calling transition to move from NEW_ST
+    // to DUMMY_ST with START_EVT.  If we used startModel this would blow by
+    // the status  of "running" but not "waiting".
     EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
     EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
 
 
 
 
-    // verify that you cannot add to the map once the model has started.
-    EXPECT_THROW(addToStateHandlerMap(9999,
-                                      boost::bind(&StateModelTest::readyHandler,
-                                                  this)),
+    // Verify that events and states cannot be added after the model is started.
+    EXPECT_THROW(defineEvent(9999, "9999"), StateModelError);
+    EXPECT_THROW(defineState(9999, "9999",
+                             boost::bind(&StateModelTest::readyHandler, this)),
                  StateModelError);
                  StateModelError);
 
 
     // The state and event combos set above, should show the model as
     // The state and event combos set above, should show the model as
@@ -547,20 +629,20 @@ TEST_F(StateModelTest, statusMethods) {
     EXPECT_FALSE(didModelFail());
     EXPECT_FALSE(didModelFail());
 }
 }
 
 
+/// @brief Tests that the model status methods are correct after a model
+/// failure.
 TEST_F(StateModelTest, statusMethodsOnFailure) {
 TEST_F(StateModelTest, statusMethodsOnFailure) {
-    // Construct the event definitions, normally done by startModel.
+    // Construct the event and state definitions, normally done by startModel.
     ASSERT_NO_THROW(defineEvents());
     ASSERT_NO_THROW(defineEvents());
     ASSERT_NO_THROW(verifyEvents());
     ASSERT_NO_THROW(verifyEvents());
-
-    // Manually init the handlers map.
-    ASSERT_NO_THROW(initStateHandlerMap());
-    ASSERT_NO_THROW(verifyStateHandlerMap());
+    ASSERT_NO_THROW(defineStates());
+    ASSERT_NO_THROW(verifyStates());
 
 
     // call transition to move from NEW_ST to DUMMY_ST with START_EVT
     // call transition to move from NEW_ST to DUMMY_ST with START_EVT
     EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
     EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
 
 
     // Call endModel to transition us to the end of the model.
     // Call endModel to transition us to the end of the model.
-    EXPECT_NO_THROW(abortModel());
+    EXPECT_NO_THROW(abortModel("test invocation"));
 
 
     // With state set to END_ST, model should be done.
     // With state set to END_ST, model should be done.
     EXPECT_FALSE(isModelNew());
     EXPECT_FALSE(isModelNew());
@@ -570,44 +652,92 @@ TEST_F(StateModelTest, statusMethodsOnFailure) {
     EXPECT_TRUE(didModelFail());
     EXPECT_TRUE(didModelFail());
 }
 }
 
 
+/// @brief Checks that the context strings accurately reflect context and
+/// are safe to invoke.
 TEST_F(StateModelTest, contextStrs) {
 TEST_F(StateModelTest, contextStrs) {
-    // Construct the event definitions, normally done by startModel.
+    // Verify context methods do not throw prior to dictionary init.
+    ASSERT_NO_THROW(getContextStr());
+    ASSERT_NO_THROW(getPrevContextStr());
+
+    // Construct the event and state definitions, normally done by startModel.
     ASSERT_NO_THROW(defineEvents());
     ASSERT_NO_THROW(defineEvents());
     ASSERT_NO_THROW(verifyEvents());
     ASSERT_NO_THROW(verifyEvents());
-
-    // Manually init the handlers map.
-    ASSERT_NO_THROW(initStateHandlerMap());
-    ASSERT_NO_THROW(verifyStateHandlerMap());
+    ASSERT_NO_THROW(defineStates());
+    ASSERT_NO_THROW(verifyStates());
 
 
     // transition uses setState and setEvent, testing it tests all three.
     // transition uses setState and setEvent, testing it tests all three.
     EXPECT_NO_THROW(transition(READY_ST, START_EVT));
     EXPECT_NO_THROW(transition(READY_ST, START_EVT));
 
 
-    std::string ctx_str = getContextStr();
+    // Verify the current context string depicts correct state and event.
+    std::string ctx_str;
+    ASSERT_NO_THROW(ctx_str = getContextStr());
     EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(READY_ST)));
     EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(READY_ST)));
     EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(START_EVT)));
     EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(START_EVT)));
 
 
-    ctx_str = getPrevContextStr();
+    // Verify the previous context string depicts correct state and event.
+    ASSERT_NO_THROW(ctx_str = getPrevContextStr());
     EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(NEW_ST)));
     EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(NEW_ST)));
     EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(NOP_EVT)));
     EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(NOP_EVT)));
 }
 }
 
 
-/// @brief Tests that invalid states are handled gracefully.
-/// This test verifies that attempting to execute a state which has no handler
-/// results in a failed model.
-TEST_F(StateModelTest, invalidState) {
-    // First, verify state is NEW_ST and that NEW_ST has no handler.
-    // This is the state that runModel will attempt to execute.
-    ASSERT_EQ(NEW_ST, getState());
-    ASSERT_THROW(getStateHandler(NEW_ST), StateModelError);
-
-    // Verify that the StateModelTest's outcome flags are both false.
-    EXPECT_FALSE(getModelFailureCalled());
+/// @brief Tests that undefined states are handled gracefully.
+/// This test verifies that attempting to transition to an undefined state,
+/// which constitutes a model violation, results in an orderly model failure.
+TEST_F(StateModelTest, undefinedState) {
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
     EXPECT_FALSE(getWorkCompleted());
     EXPECT_FALSE(getWorkCompleted());
 
 
-    // Now call runModel() which should not throw, but should result
-    // in a failed model and a call to onModelFailure().
-    //EXPECT_NO_THROW(runModel(START_EVT));
-    (runModel(START_EVT));
+    // First, lets execute the state model to a known valid point, by
+    // calling startModel. This should run the model through to DO_WORK_ST.
+    ASSERT_NO_THROW(startModel(READY_ST));
+
+    // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
+    EXPECT_EQ(NOP_EVT, getNextEvent());
+
+    // Resume the model with next event set to cause the DO_WORK_ST handler
+    // to transition to an undefined state. This should cause it to return
+    // without throwing and yield a failed model.
+    EXPECT_NO_THROW(runModel(FORCE_UNDEFINED_ST_EVT));
+
+    // Verify that status methods are correct: model is done but failed.
+    EXPECT_FALSE(isModelNew());
+    EXPECT_FALSE(isModelRunning());
+    EXPECT_FALSE(isModelWaiting());
+    EXPECT_TRUE(isModelDone());
+    EXPECT_TRUE(didModelFail());
+
+    // Verify that failure explanation is not empty.
+    EXPECT_FALSE(getFailureExplanation().empty());
+
+    // Verify that work completed flag is still false.
+    EXPECT_FALSE(getWorkCompleted());
+}
+
+/// @brief Tests that an unexpected exception thrown by a state handler is
+/// handled gracefully.  State models are supposed to account for and handle
+/// all errors that they actions (i.e. handlers) may cause.  In the event they
+/// do not, this constitutes a model violation.  This test verifies such
+/// violations are handled correctly and result in an orderly model failure.
+TEST_F(StateModelTest, unexpectedError) {
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
+    EXPECT_FALSE(getWorkCompleted());
+
+    // First, lets execute the state model to a known valid point, by
+    // calling startModel with a start state of READY_ST.
+    // This should run the model through to DO_WORK_ST.
+    ASSERT_NO_THROW(startModel(READY_ST));
+
+    // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
+    EXPECT_EQ(NOP_EVT, getNextEvent());
+
+    // Resume the model with next event set to cause the DO_WORK_ST handler
+    // to transition to an undefined state. This should cause it to return
+    // without throwing and yield a failed model.
+    EXPECT_NO_THROW(runModel(SIMULATE_ERROR_EVT));
 
 
     // Verify that status methods are correct: model is done but failed.
     // Verify that status methods are correct: model is done but failed.
     EXPECT_FALSE(isModelNew());
     EXPECT_FALSE(isModelNew());
@@ -616,19 +746,20 @@ TEST_F(StateModelTest, invalidState) {
     EXPECT_TRUE(isModelDone());
     EXPECT_TRUE(isModelDone());
     EXPECT_TRUE(didModelFail());
     EXPECT_TRUE(didModelFail());
 
 
-    // Verify that model failure flag is true.
-    EXPECT_TRUE(getModelFailureCalled());
+    // Verify that failure explanation is not empty.
+    EXPECT_FALSE(getFailureExplanation().empty());
 
 
     // Verify that work completed flag is still false.
     // Verify that work completed flag is still false.
     EXPECT_FALSE(getWorkCompleted());
     EXPECT_FALSE(getWorkCompleted());
 }
 }
 
 
-/// @brief Tests that invalid events are handled gracefully.
-/// This test verifies that submitting an invalid event to the state machine
-/// results in a failed transaction.
-TEST_F(StateModelTest, invalidEvent) {
-    // Verify that the StateModelTest's outcome flags are both false.
-    EXPECT_FALSE(getModelFailureCalled());
+/// @brief Tests that undefined events are handled gracefully.
+/// This test verifies that submitting an undefined event to the state machine
+/// results, which constitutes a model violation, results in an orderly model
+/// failure.
+TEST_F(StateModelTest, undefinedEvent) {
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
     EXPECT_FALSE(getWorkCompleted());
     EXPECT_FALSE(getWorkCompleted());
 
 
     // First, lets execute the state model to a known valid point, by
     // First, lets execute the state model to a known valid point, by
@@ -637,13 +768,12 @@ TEST_F(StateModelTest, invalidEvent) {
     ASSERT_NO_THROW(startModel(READY_ST));
     ASSERT_NO_THROW(startModel(READY_ST));
 
 
     // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
     // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
-    EXPECT_EQ(DO_WORK_ST, getState());
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
     EXPECT_EQ(NOP_EVT, getNextEvent());
     EXPECT_EQ(NOP_EVT, getNextEvent());
 
 
-    // Submitting an invalid event to a valid state, should cause runModel to
-    // return without throwing and yield a failed model. (Current state is
-    // DO_WORK_ST, during which START_EVT, is invalid).
-    EXPECT_NO_THROW(runModel(START_EVT));
+    // Attempting to post an undefined event within runModel should cause it
+    // to return without throwing and yield a failed model.
+    EXPECT_NO_THROW(runModel(9999));
 
 
     // Verify that status methods are correct: model is done but failed.
     // Verify that status methods are correct: model is done but failed.
     EXPECT_FALSE(isModelNew());
     EXPECT_FALSE(isModelNew());
@@ -652,15 +782,15 @@ TEST_F(StateModelTest, invalidEvent) {
     EXPECT_TRUE(isModelDone());
     EXPECT_TRUE(isModelDone());
     EXPECT_TRUE(didModelFail());
     EXPECT_TRUE(didModelFail());
 
 
-    // Verify that model failure flag is true.
-    EXPECT_TRUE(getModelFailureCalled());
+    // Verify that failure explanation is not empty.
+    EXPECT_FALSE(getFailureExplanation().empty());
 
 
     // Verify that work completed flag is still false.
     // Verify that work completed flag is still false.
     EXPECT_FALSE(getWorkCompleted());
     EXPECT_FALSE(getWorkCompleted());
 }
 }
 
 
 /// @brief Test the basic mechanics of state model execution.
 /// @brief Test the basic mechanics of state model execution.
-/// This test exercises the ability to execute state model from state to
+/// This test exercises the ability to execute state model from start to
 /// finish, including the handling of a asynchronous IO operation.
 /// finish, including the handling of a asynchronous IO operation.
 TEST_F(StateModelTest, stateModelTest) {
 TEST_F(StateModelTest, stateModelTest) {
     // Verify that status methods are correct: model is new.
     // Verify that status methods are correct: model is new.
@@ -669,8 +799,8 @@ TEST_F(StateModelTest, stateModelTest) {
     EXPECT_FALSE(isModelWaiting());
     EXPECT_FALSE(isModelWaiting());
     EXPECT_FALSE(isModelDone());
     EXPECT_FALSE(isModelDone());
 
 
-    // Verify that the StateModelTest's outcome flags are both false.
-    EXPECT_FALSE(getModelFailureCalled());
+    // Verify that the failure explanation is empty and work is not done.
+    EXPECT_TRUE(getFailureExplanation().empty());
     EXPECT_FALSE(getWorkCompleted());
     EXPECT_FALSE(getWorkCompleted());
 
 
     // Launch the transaction by calling startModel.  The state model
     // Launch the transaction by calling startModel.  The state model
@@ -680,7 +810,7 @@ TEST_F(StateModelTest, stateModelTest) {
 
 
     // Verify that we are now in state of DO_WORK_ST, the last event was
     // Verify that we are now in state of DO_WORK_ST, the last event was
     // WORK_START_EVT, the next event is NOP_EVT.
     // WORK_START_EVT, the next event is NOP_EVT.
-    EXPECT_EQ(DO_WORK_ST, getState());
+    EXPECT_EQ(DO_WORK_ST, getCurrState());
     EXPECT_EQ(WORK_START_EVT, getLastEvent());
     EXPECT_EQ(WORK_START_EVT, getLastEvent());
     EXPECT_EQ(NOP_EVT, getNextEvent());
     EXPECT_EQ(NOP_EVT, getNextEvent());
 
 
@@ -691,7 +821,7 @@ TEST_F(StateModelTest, stateModelTest) {
     // Verify that the state model has progressed through to completion:
     // Verify that the state model has progressed through to completion:
     // it is in the DONE_ST, the status is ST_COMPLETED, and the next event
     // it is in the DONE_ST, the status is ST_COMPLETED, and the next event
     // is NOP_EVT.
     // is NOP_EVT.
-    EXPECT_EQ(END_ST, getState());
+    EXPECT_EQ(END_ST, getCurrState());
     EXPECT_EQ(END_EVT, getNextEvent());
     EXPECT_EQ(END_EVT, getNextEvent());
 
 
     // Verify that status methods are correct: model done.
     // Verify that status methods are correct: model done.
@@ -700,8 +830,8 @@ TEST_F(StateModelTest, stateModelTest) {
     EXPECT_FALSE(isModelWaiting());
     EXPECT_FALSE(isModelWaiting());
     EXPECT_TRUE(isModelDone());
     EXPECT_TRUE(isModelDone());
 
 
-    // Verify that model failure flag is false.
-    EXPECT_FALSE(getModelFailureCalled());
+    // Verify that failure explanation is empty.
+    EXPECT_TRUE(getFailureExplanation().empty());
 
 
     // Verify that work completed flag is true.
     // Verify that work completed flag is true.
     EXPECT_TRUE(getWorkCompleted());
     EXPECT_TRUE(getWorkCompleted());