d_test_stubs.h 24 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#ifndef D_TEST_STUBS_H
#define D_TEST_STUBS_H

#include <asiolink/asiolink.h>
#include <cc/data.h>
#include <cc/session.h>
#include <config/ccsession.h>

#include <d2/d_controller.h>
24 25
#include <d2/d_cfg_mgr.h>

26 27 28 29 30
#include <gtest/gtest.h>

namespace isc {
namespace d2 {

31
/// @brief Provides a valid DHCP-DDNS configuration for testing basic
32 33 34 35
/// parsing fundamentals.
extern const char* valid_d2_config;


36
/// @brief Class is used to set a globally accessible value that indicates
37
/// a specific type of failure to simulate.  Test derivations of base classes
38
/// can exercise error handling code paths by testing for specific SimFailure
39 40 41 42
/// values at the appropriate places and then causing the error to "occur".
/// The class consists of an enumerated set of failures, and static methods
/// for getting, setting, and testing the current value.
class SimFailure {
43
public:
44
    enum FailureType {
45
        ftUnknown = -1,
46
        ftNoFailure = 0,
47
        ftCreateProcessException,
48 49 50 51
        ftCreateProcessNull,
        ftProcessInit,
        ftProcessConfigure,
        ftControllerCommand,
52
        ftProcessCommand,
53 54 55 56
        ftProcessShutdown,
        ftElementBuild,
        ftElementCommit,
        ftElementUnknown
57 58 59 60 61 62 63 64 65
    };

    /// @brief Sets the SimFailure value to the given value.
    ///
    /// @param value is the new value to assign to the global value.
    static void set(enum FailureType value) {
       failure_type_ = value;
    }

66
    /// @brief Gets the current global SimFailure value
67
    ///
68
    /// @return returns the current SimFailure value
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
    static enum FailureType get() {
       return (failure_type_);
    }

    /// @brief One-shot test of the SimFailure value. If the global
    /// SimFailure value is equal to the given value, clear the global
    /// value and return true.  This makes it convenient for code to
    /// test and react without having to explicitly clear the global
    /// value.
    ///
    /// @param value is the value against which the global value is
    /// to be compared.
    ///
    /// @return returns true if current SimFailure value matches the
    /// given value.
    static bool shouldFailOn(enum FailureType value) {
        if (failure_type_ == value) {
            clear();
            return (true);
        }

        return (false);
    }

93
    /// @brief Resets the failure type to none.
94 95 96 97
    static void clear() {
       failure_type_ = ftNoFailure;
    }

98
    /// @brief Static value for holding the failure type to simulate.
99 100 101 102 103 104 105 106 107 108 109 110
    static enum FailureType failure_type_;
};

/// @brief Test Derivation of the DProcessBase class.
///
/// This class is used primarily to server as a test process class for testing
/// DControllerBase.  It provides minimal, but sufficient implementation to
/// test the majority of DControllerBase functionality.
class DStubProcess : public DProcessBase {
public:

    /// @brief Static constant that defines a custom process command string.
111
    static const char* stub_proc_command_;
112 113 114 115

    /// @brief Constructor
    ///
    /// @param name name is a text label for the process. Generally used
116
    /// in log statements, but otherwise arbitrary.
117 118 119
    /// @param io_service is the io_service used by the caller for
    /// asynchronous event handling.
    ///
120
    /// @throw DProcessBaseError is io_service is NULL.
121 122
    DStubProcess(const char* name, IOServicePtr io_service);

123 124 125 126
    /// @brief Invoked after process instantiation to perform initialization.
    /// This implementation supports simulating an error initializing the
    /// process by throwing a DProcessBaseError if SimFailure is set to
    /// ftProcessInit.
127 128
    virtual void init();

129 130 131 132
    /// @brief Implements the process's event loop.
    /// This implementation is quite basic, surrounding calls to
    /// io_service->runOne() with a test of the shutdown flag. Once invoked,
    /// the method will continue until the process itself is exiting due to a
133 134
    /// request to shutdown or some anomaly forces an exit.
    /// @return  returns 0 upon a successful, "normal" termination, non-zero to
135 136
    /// indicate an abnormal termination.
    virtual void run();
137 138 139 140

    /// @brief Implements the process shutdown procedure. Currently this is
    /// limited to setting the instance shutdown flag, which is monitored in
    /// run().
141
    virtual void shutdown();
142

143 144
    /// @brief Processes the given configuration.
    ///
145 146 147 148 149 150 151
    /// This implementation fails if SimFailure is set to ftProcessConfigure.
    /// Otherwise it will complete successfully.  It does not check the content
    /// of the inbound configuration.
    ///
    /// @param config_set a new configuration (JSON) for the process
    /// @return an Element that contains the results of configuration composed
    /// of an integer status value (0 means successful, non-zero means failure),
152
    /// and a string explanation of the outcome.
153 154 155
    virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr
                                                 config_set);

156 157
    /// @brief Executes the given command.
    ///
158
    /// This implementation will recognizes one "custom" process command,
159 160
    /// stub_proc_command_.  It will fail if SimFailure is set to
    /// ftProcessCommand.
161 162 163
    ///
    /// @param command is a string label representing the command to execute.
    /// @param args is a set of arguments (if any) required for the given
164
    /// command.
165
    /// @return an Element that contains the results of command composed
166
    /// of an integer status value and a string explanation of the outcome.
167
    /// The status value is:
168 169
    /// COMMAND_SUCCESS if the command is recognized and executes successfully.
    /// COMMAND_ERROR if the command is recognized but fails to execute.
170
    /// COMMAND_INVALID if the command is not recognized.
171
    virtual isc::data::ConstElementPtr command(const std::string& command,
172 173 174 175 176 177 178 179 180 181
                                               isc::data::ConstElementPtr args);

    // @brief Destructor
    virtual ~DStubProcess();
};


/// @brief Test Derivation of the DControllerBase class.
///
/// DControllerBase is an abstract class and therefore requires a derivation
182 183 184 185
/// for testing.  It allows testing the majority of the base class code
/// without polluting production derivations (e.g. D2Process).  It uses
/// DStubProcess as its application process class.  It is a full enough
/// implementation to support running both stand alone and integrated.
186 187 188 189 190
/// Obviously BIND10 connectivity is not available under unit tests, so
/// testing here is limited to "failures" to communicate with BIND10.
class DStubController : public DControllerBase {
public:
    /// @brief Static singleton instance method. This method returns the
191 192
    /// base class singleton instance member.  It instantiates the singleton
    /// and sets the base class instance member upon first invocation.
193
    ///
194
    /// @return returns a pointer reference to the singleton instance.
195 196
    static DControllerBasePtr& instance();

197
    /// @brief Defines a custom controller command string. This is a
198
    /// custom command supported by DStubController.
199
    static const char* stub_ctl_command_;
200 201 202 203

    /// @brief Defines a custom command line option supported by
    /// DStubController.
    static const char* stub_option_x_;
204

205 206 207 208 209 210
    /// @brief Defines the app name used to construct the controller
    static const char* stub_app_name_;

    /// @brief Defines the executable name used to construct the controller
    static const char* stub_bin_name_;

211 212 213 214 215
protected:
    /// @brief Handles additional command line options that are supported
    /// by DStubController.  This implementation supports an option "-x".
    ///
    /// @param option is the option "character" from the command line, without
216
    /// any prefixing hyphen(s)
217 218
    /// @optarg optarg is the argument value (if one) associated with the option
    ///
219
    /// @return returns true if the option is "x", otherwise ti returns false.
220 221
    virtual bool customOption(int option, char *optarg);

222 223 224
    /// @brief Instantiates an instance of DStubProcess.
    ///
    /// This implementation will fail if SimFailure is set to
225
    /// ftCreateProcessException OR ftCreateProcessNull.
226
    ///
227 228
    /// @return returns a pointer to the new process instance (DProcessBase*)
    /// or NULL if SimFailure is set to ftCreateProcessNull.
229
    /// @throw throws std::runtime_error if SimFailure is set to
230 231 232
    /// ftCreateProcessException.
    virtual DProcessBase* createProcess();

233 234 235
    /// @brief Executes custom controller commands are supported by
    /// DStubController. This implementation supports one custom controller
    /// command, stub_ctl_command_.  It will fail if SimFailure is set
236 237 238 239
    /// to ftControllerCommand.
    ///
    /// @param command is a string label representing the command to execute.
    /// @param args is a set of arguments (if any) required for the given
240
    /// command.
241
    /// @return an Element that contains the results of command composed
242
    /// of an integer status value and a string explanation of the outcome.
243
    /// The status value is:
244 245
    /// COMMAND_SUCCESS if the command is recognized and executes successfully.
    /// COMMAND_ERROR if the command is recognized but fails to execute.
246 247 248 249
    /// COMMAND_INVALID if the command is not recognized.
    virtual isc::data::ConstElementPtr customControllerCommand(
            const std::string& command, isc::data::ConstElementPtr args);

250 251 252
    /// @brief Provides a string of the additional command line options
    /// supported by DStubController.  DStubController supports one
    /// addition option, stub_option_x_.
253 254
    ///
    /// @return returns a string containing the option letters.
255
    virtual const std::string getCustomOpts() const;
256 257 258 259 260 261 262 263 264

private:
    /// @brief Constructor is private to protect singleton integrity.
    DStubController();

public:
    virtual ~DStubController();
};

265
/// @brief Abstract Test fixture class that wraps a DControllerBase. This class
266
/// is a friend class of DControllerBase which allows it access to class
267 268
/// content to facilitate testing.  It provides numerous wrapper methods for
/// the protected and private methods and member of the base class.
269 270 271
class DControllerTest : public ::testing::Test {
public:

272
    /// @brief Defines a function pointer for controller singleton fetchers.
273 274
    typedef DControllerBasePtr& (*InstanceGetter)();

275 276 277
    /// @brief Static storage of the controller class's singleton fetcher.
    /// We need this this statically available for callbacks.
    static InstanceGetter instanceGetter_;
278

279 280 281 282 283 284 285 286
    /// @brief Constructor
    ///
    /// @param instance_getter is a function pointer to the static instance
    /// method of the DControllerBase derivation under test.
    DControllerTest(InstanceGetter instance_getter) {
        // Set the static fetcher member, then invoke it via getController.
        // This ensures the singleton is instantiated.
        instanceGetter_ = instance_getter;
287 288 289
        getController();
    }

290 291 292 293
    /// @brief Destructor
    /// Note the controller singleton is destroyed. This is essential to ensure
    /// a clean start between tests.
    virtual ~DControllerTest() {
294 295 296
        getController().reset();
    }

297 298 299
    /// @brief Convenience method that destructs and then recreates the
    /// controller singleton under test.  This is handy for tests within
    /// tests.
300
    void resetController() {
301 302
        getController().reset();
        getController();
303 304
    }

305 306 307
    /// @brief Static method which returns the instance of the controller
    /// under test.
    /// @return returns a reference to the controller instance.
308 309 310 311
    static DControllerBasePtr& getController() {
        return ((*instanceGetter_)());
    }

312 313 314 315 316 317 318 319 320 321 322 323
    /// @brief Returns true if the Controller's app name matches the
    /// given value.
    ///
    /// @param should_be is the value to compare against.
    ///
    /// @return returns true if the values are equal.
    bool checkAppName(const std::string& should_be) {
        return (getController()->getAppName().compare(should_be) == 0);
    }

    /// @brief Returns true if the Controller's service name matches the
    /// given value.
324 325 326 327
    ///
    /// @param should_be is the value to compare against.
    ///
    /// @return returns true if the values are equal.
328 329
    bool checkBinName(const std::string& should_be) {
        return (getController()->getBinName().compare(should_be) == 0);
330 331
    }

332 333 334 335 336 337
    /// @brief Returns true if the Controller's spec file name matches the
    /// given value.
    ///
    /// @param should_be is the value to compare against.
    ///
    /// @return returns true if the values are equal.
338 339 340 341
    bool checkSpecFileName(const std::string& should_be) {
        return (getController()->getSpecFileName().compare(should_be) == 0);
    }

342 343 344
    /// @brief Tests the existence of the Controller's application process.
    ///
    /// @return returns true if the process instance exists.
345 346 347 348
    bool checkProcess() {
        return (getController()->process_);
    }

349 350 351
    /// @brief Tests the existence of the Controller's IOService.
    ///
    /// @return returns true if the IOService exists.
352 353 354 355
    bool checkIOService() {
        return (getController()->io_service_);
    }

356 357 358
    /// @brief Gets the Controller's IOService.
    ///
    /// @return returns a reference to the IOService
359 360 361 362
    IOServicePtr& getIOService() {
        return (getController()->io_service_);
    }

363 364 365 366 367 368
    /// @brief Compares stand alone flag with the given value.
    ///
    /// @param value
    ///
    /// @return returns true if the stand alone flag is equal to the given
    /// value.
369 370 371 372
    bool checkStandAlone(bool value) {
        return (getController()->isStandAlone() == value);
    }

373 374 375 376
    /// @brief Sets the controller's stand alone flag to the given value.
    ///
    /// @param value is the new value to assign.
    ///
377 378 379 380
    void setStandAlone(bool value) {
        getController()->setStandAlone(value);
    }

381 382 383 384 385
    /// @brief Compares verbose flag with the given value.
    ///
    /// @param value
    ///
    /// @return returns true if the verbose flag is equal to the given value.
386 387 388 389
    bool checkVerbose(bool value) {
        return (getController()->isVerbose() == value);
    }

390 391
    /// @Wrapper to invoke the Controller's parseArgs method.  Please refer to
    /// DControllerBase::parseArgs for details.
392 393 394 395
    void parseArgs(int argc, char* argv[]) {
        getController()->parseArgs(argc, argv);
    }

396 397
    /// @Wrapper to invoke the Controller's init method.  Please refer to
    /// DControllerBase::init for details.
398 399 400 401
    void initProcess() {
        getController()->initProcess();
    }

402 403
    /// @Wrapper to invoke the Controller's establishSession method.  Please
    /// refer to DControllerBase::establishSession for details.
404 405 406 407
    void establishSession() {
        getController()->establishSession();
    }

408 409
    /// @Wrapper to invoke the Controller's launch method.  Please refer to
    /// DControllerBase::launch for details.
410
    void launch(int argc, char* argv[]) {
411
        optind = 1;
412
        getController()->launch(argc, argv, true);
413 414
    }

415 416
    /// @Wrapper to invoke the Controller's disconnectSession method.  Please
    /// refer to DControllerBase::disconnectSession for details.
417 418 419 420
    void disconnectSession() {
        getController()->disconnectSession();
    }

421 422 423
    /// @Wrapper to invoke the Controller's updateConfig method.  Please
    /// refer to DControllerBase::updateConfig for details.
    isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
424 425 426 427
                                            new_config) {
        return (getController()->updateConfig(new_config));
    }

428 429 430
    /// @Wrapper to invoke the Controller's executeCommand method.  Please
    /// refer to DControllerBase::executeCommand for details.
    isc::data::ConstElementPtr executeCommand(const std::string& command,
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
                                       isc::data::ConstElementPtr args){
        return (getController()->executeCommand(command, args));
    }

    /// @brief Callback that will generate shutdown command via the
    /// command callback function.
    static void genShutdownCallback() {
        isc::data::ElementPtr arg_set;
        DControllerBase::commandHandler(SHUT_DOWN_COMMAND, arg_set);
    }

    /// @brief Callback that throws an exception.
    static void genFatalErrorCallback() {
        isc_throw (DProcessBaseError, "simulated fatal error");
    }
};

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
/// @brief Simple parser derivation for testing the basics of configuration
/// parsing.
class TestParser : public isc::dhcp::DhcpConfigParser {
public:

    /// @brief Constructor
    ///
    /// See @ref DhcpConfigParser class for details.
    ///
    /// @param param_name name of the parsed parameter
    TestParser(const std::string& param_name);

    /// @brief Destructor
    virtual ~TestParser();

    /// @brief Builds parameter value.
    ///
    /// See @ref DhcpConfigParser class for details.
    ///
    /// @param new_config pointer to the new configuration
    /// @throw throws DCfgMgrBaseError if the SimFailure is set to
    /// ftElementBuild. This allows for the simulation of an
    /// exception during the build portion of parsing an element.
    virtual void build(isc::data::ConstElementPtr new_config);

    /// @brief Commits the parsed value to storage.
    ///
    /// See @ref DhcpConfigParser class for details.
    ///
    /// @throw throws DCfgMgrBaseError if SimFailure is set to ftElementCommit.
    /// This allows for the simulation of an exception during the commit
    /// portion of parsing an element.
    virtual void commit();

private:
    /// name of the parsed parameter
    std::string param_name_;

    /// pointer to the parsed value of the parameter
    isc::data::ConstElementPtr value_;
};

/// @brief Test Derivation of the DCfgContextBase class.
///
/// This class is used to test basic functionality of configuration context.
/// It adds an additional storage container "extra values" to mimic an
/// application extension of configuration storage.  This permits testing that
/// both the base class content as well as the application content is
/// correctly copied during cloning.  This is vital to configuration backup
/// and rollback during configuration parsing.
class DStubContext : public DCfgContextBase {
public:

    /// @brief Constructor
    DStubContext();

    /// @brief Destructor
    virtual ~DStubContext();

    /// @brief Fetches the value for a given "extra" configuration parameter
    /// from the context.
    ///
    /// @param name is the name of the parameter to retrieve.
    /// @param value is an output parameter in which to return the retrieved
    /// value.
    /// @throw throws DhcpConfigError if the context does not contain the
    /// parameter.
    void getExtraParam(const std::string& name, uint32_t& value);

    /// @brief Fetches the extra storage.
    ///
    /// @return returns a pointer to the extra storage.
    isc::dhcp::Uint32StoragePtr getExtraStorage();

    /// @brief Creates a clone of a DStubContext.
    ///
524 525
    /// @return returns a pointer to the new clone.
    virtual DCfgContextBasePtr clone();
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585

protected:
    /// @brief Copy constructor
    DStubContext(const DStubContext& rhs);

private:
    /// @brief Private assignment operator, not implemented.
    DStubContext& operator=(const DStubContext& rhs);

    /// @brief Extra storage for uint32 parameters.
    isc::dhcp::Uint32StoragePtr extra_values_;
};

/// @brief Defines a pointer to DStubContext.
typedef boost::shared_ptr<DStubContext> DStubContextPtr;

/// @brief Test Derivation of the DCfgMgrBase class.
///
/// This class is used to test basic functionality of configuration management.
/// It supports the following configuration elements:
///
/// "bool_test" - Boolean element, tests parsing and committing a boolean
///               configuration parameter.
/// "uint32_test" - Uint32 element, tests parsing and committing a uint32_t
///               configuration parameter.
/// "string_test" - String element, tests parsing and committing a string
///               configuration parameter.
/// "extra_test" - "Extra" element, tests parsing and committing an extra
///               configuration parameter. (This is used to demonstrate
///               derivation's addition of storage to configuration context.
///
/// It also keeps track of the element ids that are parsed in the order they
/// are parsed.  This is used to test ordered and non-ordered parsing.
class DStubCfgMgr : public DCfgMgrBase {
public:
    /// @brief Constructor
    DStubCfgMgr();

    /// @brief Destructor
    virtual ~DStubCfgMgr();

    /// @brief Given an element_id returns an instance of the appropriate
    /// parser. It supports the element ids as described in the class brief.
    ///
    /// @param element_id is the string name of the element as it will appear
    /// in the configuration set.
    ///
    /// @return returns a ParserPtr to the parser instance.
    /// @throw throws DCfgMgrBaseError if SimFailure is ftElementUnknown.
    virtual isc::dhcp::ParserPtr
    createConfigParser(const std::string& element_id);

    /// @brief A list for remembering the element ids in the order they were
    /// parsed.
    ElementIdList parsed_order_;
};

/// @brief Defines a pointer to DStubCfgMgr.
typedef boost::shared_ptr<DStubCfgMgr> DStubCfgMgrPtr;

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
/// @brief Test fixture base class for any fixtures which test parsing.
/// It provides methods for converting JSON strings to configuration element
/// sets and checking parse results
class ConfigParseTest : public ::testing::Test {
public:

    /// @brief Constructor
    ConfigParseTest(){
    }

    /// @brief Destructor
    ~ConfigParseTest() {
    }

    /// @brief Converts a given JSON string into an Element set and stores the
    /// result the member variable, config_set_.
    ///
    /// @param json_text contains the configuration text in JSON format to
    /// convert.
605 606 607
    /// @return returns AssertionSuccess if there were no parsing errors,
    /// AssertionFailure otherwise.
    ::testing::AssertionResult fromJSON(std::string& json_text) {
608 609
        try  {
            config_set_ = isc::data::Element::fromJSON(json_text);
610
        } catch (const isc::Exception &ex) {
611 612 613
            return (::testing::AssertionFailure(::testing::Message() << 
                                                "JSON text failed to parse:" 
                                                << ex.what())); 
614 615
        }

616
        return (::testing::AssertionSuccess());
617 618
    }

619

620 621 622 623 624
    /// @brief Compares the status in the  parse result stored in member
    /// variable answer_ to a given value.
    ///
    /// @param should_be is an integer against which to compare the status.
    ///
625 626 627
    /// @return returns AssertionSuccess if there were no parsing errors,
    /// AssertionFailure otherwise.
    ::testing::AssertionResult checkAnswer(int should_be) {
628 629 630
        int rcode = 0;
        isc::data::ConstElementPtr comment;
        comment = isc::config::parseAnswer(rcode, answer_);
631
        if (rcode == should_be) {
632
            return (testing::AssertionSuccess());
633
        }
634

635 636 637
        return (::testing::AssertionFailure(::testing::Message() << 
                                            "checkAnswer rcode:" << rcode 
                                            << " comment: " << *comment));
638 639 640 641 642 643 644 645 646 647 648 649 650
    }

    /// @brief Configuration set being tested.
    isc::data::ElementPtr config_set_;

    /// @brief Results of most recent element parsing.
    isc::data::ConstElementPtr answer_;
};

/// @brief Defines a small but valid DHCP-DDNS compliant configuration for
/// testing configuration parsing fundamentals.
extern const char* valid_d2_config;

651
}; // namespace isc::d2
652 653 654
}; // namespace isc

#endif