d_test_stubs.h 32.3 KB
Newer Older
1
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
//
// 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

18 19
#include <asiolink/io_service.h>

20
#include <cc/data.h>
21
#include <cc/command_interpreter.h>
22 23

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

26 27
#include <log/logger_support.h>

28 29 30 31
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace boost::posix_time;

32 33
#include <gtest/gtest.h>

34 35 36 37
#include <fstream>
#include <iostream>
#include <sstream>

38 39 40
namespace isc {
namespace d2 {

41
/// @brief Provides a valid DHCP-DDNS configuration for testing basic
42 43 44 45
/// parsing fundamentals.
extern const char* valid_d2_config;


46
/// @brief Class is used to set a globally accessible value that indicates
47
/// a specific type of failure to simulate.  Test derivations of base classes
48
/// can exercise error handling code paths by testing for specific SimFailure
49 50 51 52
/// 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 {
53
public:
54
    enum FailureType {
55
        ftUnknown = -1,
56
        ftNoFailure = 0,
57
        ftCreateProcessException,
58 59 60 61
        ftCreateProcessNull,
        ftProcessInit,
        ftProcessConfigure,
        ftControllerCommand,
62
        ftProcessCommand,
63 64 65 66
        ftProcessShutdown,
        ftElementBuild,
        ftElementCommit,
        ftElementUnknown
67 68 69 70 71 72 73 74 75
    };

    /// @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;
    }

76
    /// @brief Gets the current global SimFailure value
77
    ///
78
    /// @return returns the current SimFailure value
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    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);
    }

103
    /// @brief Resets the failure type to none.
104 105 106 107
    static void clear() {
       failure_type_ = ftNoFailure;
    }

108
    /// @brief Static value for holding the failure type to simulate.
109 110 111 112 113 114 115 116 117 118 119 120
    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.
121
    static const char* stub_proc_command_;
122 123 124 125

    /// @brief Constructor
    ///
    /// @param name name is a text label for the process. Generally used
126
    /// in log statements, but otherwise arbitrary.
127 128 129
    /// @param io_service is the io_service used by the caller for
    /// asynchronous event handling.
    ///
130
    /// @throw DProcessBaseError is io_service is NULL.
131
    DStubProcess(const char* name, asiolink::IOServicePtr io_service);
132

133 134 135 136
    /// @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.
137 138
    virtual void init();

139 140 141 142
    /// @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
143 144
    /// request to shutdown or some anomaly forces an exit.
    /// @return  returns 0 upon a successful, "normal" termination, non-zero to
145 146
    /// indicate an abnormal termination.
    virtual void run();
147

148
    /// @brief Implements the process shutdown procedure.
149
    ///
150
    /// This sets the instance shutdown flag monitored by run()  and stops
151 152
    /// the IO service.
    virtual isc::data::ConstElementPtr shutdown(isc::data::ConstElementPtr);
153

154 155
    /// @brief Processes the given configuration.
    ///
156 157 158 159 160 161 162
    /// 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),
163
    /// and a string explanation of the outcome.
164 165 166
    virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr
                                                 config_set);

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

185 186 187
    /// @brief Returns configuration summary in the textual format.
    ///
    /// @return Always an empty string.
188
    virtual std::string getConfigSummary(const uint32_t) {
189 190 191
        return ("");
    }

192 193 194 195 196 197 198 199
    // @brief Destructor
    virtual ~DStubProcess();
};


/// @brief Test Derivation of the DControllerBase class.
///
/// DControllerBase is an abstract class and therefore requires a derivation
200 201 202 203
/// 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.
204 205
/// Obviously Bundy connectivity is not available under unit tests, so
/// testing here is limited to "failures" to communicate with Bundy.
206 207 208
class DStubController : public DControllerBase {
public:
    /// @brief Static singleton instance method. This method returns the
209 210
    /// base class singleton instance member.  It instantiates the singleton
    /// and sets the base class instance member upon first invocation.
211
    ///
212
    /// @return returns a pointer reference to the singleton instance.
213 214
    static DControllerBasePtr& instance();

215
    /// @brief Defines a custom controller command string. This is a
216
    /// custom command supported by DStubController.
217
    static const char* stub_ctl_command_;
218 219 220 221

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

223 224 225 226 227 228
    /// @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_;

229 230 231 232 233 234 235 236 237 238 239 240 241
    /// @brief Gets the list of signals that have been caught and processed.
    std::vector<int>& getProcessedSignals() {
        return (processed_signals_);
    }

    /// @brief Controls whether signals are processed in full or merely
    /// recorded.
    ///
    /// If true, signal handling will stop after recording the signal.
    /// Otherwise the base class signal handler,
    /// DControllerBase::processSignals will also be invoked. This switch is
    /// useful for ensuring that IOSignals are delivered as expected without
    /// incurring the full impact such as reconfiguring or shutting down.
242 243
    ///
    /// @param value boolean which if true enables record-only behavior
244 245 246 247
    void recordSignalOnly(bool value) {
       record_signal_only_ = value;
    }

248 249 250 251 252
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
253
    /// any prefixing hyphen(s)
254 255
    /// @optarg optarg is the argument value (if one) associated with the option
    ///
256
    /// @return returns true if the option is "x", otherwise ti returns false.
257 258
    virtual bool customOption(int option, char *optarg);

259 260 261
    /// @brief Instantiates an instance of DStubProcess.
    ///
    /// This implementation will fail if SimFailure is set to
262
    /// ftCreateProcessException OR ftCreateProcessNull.
263
    ///
264 265
    /// @return returns a pointer to the new process instance (DProcessBase*)
    /// or NULL if SimFailure is set to ftCreateProcessNull.
266
    /// @throw throws std::runtime_error if SimFailure is set to
267 268 269
    /// ftCreateProcessException.
    virtual DProcessBase* createProcess();

270 271 272
    /// @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
273 274 275 276
    /// 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
277
    /// command.
278
    /// @return an Element that contains the results of command composed
279
    /// of an integer status value and a string explanation of the outcome.
280
    /// The status value is:
281 282
    /// COMMAND_SUCCESS if the command is recognized and executes successfully.
    /// COMMAND_ERROR if the command is recognized but fails to execute.
283 284 285 286
    /// COMMAND_INVALID if the command is not recognized.
    virtual isc::data::ConstElementPtr customControllerCommand(
            const std::string& command, isc::data::ConstElementPtr args);

287 288 289
    /// @brief Provides a string of the additional command line options
    /// supported by DStubController.  DStubController supports one
    /// addition option, stub_option_x_.
290 291
    ///
    /// @return returns a string containing the option letters.
292
    virtual const std::string getCustomOpts() const;
293

294 295 296 297 298 299 300 301
    /// @brief Application-level "signal handler"
    ///
    /// Overrides the base class implementation such that this method
    /// is invoked each time an IOSignal is processed.  It records the
    /// signal received and unless we are in record-only behavior, it
    /// in invokes the base class implementation.
    ///
    /// @param signum OS signal value received
302 303
    virtual void processSignal(int signum);

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

308 309 310 311 312 313
    /// @brief Vector to record the signal values received.
    std::vector<int> processed_signals_;

    /// @brief Boolean for controlling if signals are merely recorded.
    bool record_signal_only_;

314 315 316 317
public:
    virtual ~DStubController();
};

318 319 320
/// @brief Defines a pointer to a DStubController.
typedef boost::shared_ptr<DStubController> DStubControllerPtr;

321
/// @brief Abstract Test fixture class that wraps a DControllerBase. This class
322
/// is a friend class of DControllerBase which allows it access to class
323 324
/// content to facilitate testing.  It provides numerous wrapper methods for
/// the protected and private methods and member of the base class.
325 326 327
class DControllerTest : public ::testing::Test {
public:

328
    /// @brief Defines a function pointer for controller singleton fetchers.
329 330
    typedef DControllerBasePtr& (*InstanceGetter)();

331 332 333
    /// @brief Static storage of the controller class's singleton fetcher.
    /// We need this this statically available for callbacks.
    static InstanceGetter instanceGetter_;
334

335 336 337 338
    /// @brief Constructor
    ///
    /// @param instance_getter is a function pointer to the static instance
    /// method of the DControllerBase derivation under test.
339 340
    DControllerTest(InstanceGetter instance_getter)
         : write_timer_(), new_cfg_content_() {
341 342 343
        // Set the static fetcher member, then invoke it via getController.
        // This ensures the singleton is instantiated.
        instanceGetter_ = instance_getter;
344 345 346
        getController();
    }

347 348 349 350
    /// @brief Destructor
    /// Note the controller singleton is destroyed. This is essential to ensure
    /// a clean start between tests.
    virtual ~DControllerTest() {
351 352 353 354 355
        // Some unit tests update the logging configuration which has a side
        // effect that all subsequent tests print the output to stdout. This
        // is to ensure that the logging settings are back to default.
        isc::log::setDefaultLoggingOutput();

356 357 358 359
        if (write_timer_) {
            write_timer_->cancel();
        }

360
        getController().reset();
361
        static_cast<void>(unlink(CFG_TEST_FILE));
362 363
    }

364 365 366
    /// @brief Convenience method that destructs and then recreates the
    /// controller singleton under test.  This is handy for tests within
    /// tests.
367
    void resetController() {
368 369
        getController().reset();
        getController();
370 371
    }

372 373 374
    /// @brief Static method which returns the instance of the controller
    /// under test.
    /// @return returns a reference to the controller instance.
375 376 377 378
    static DControllerBasePtr& getController() {
        return ((*instanceGetter_)());
    }

379 380 381 382 383 384 385 386 387 388 389 390
    /// @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.
391 392 393 394
    ///
    /// @param should_be is the value to compare against.
    ///
    /// @return returns true if the values are equal.
395 396
    bool checkBinName(const std::string& should_be) {
        return (getController()->getBinName().compare(should_be) == 0);
397 398
    }

399 400 401 402 403 404
    /// @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.
405 406 407 408
    bool checkSpecFileName(const std::string& should_be) {
        return (getController()->getSpecFileName().compare(should_be) == 0);
    }

409 410 411
    /// @brief Tests the existence of the Controller's application process.
    ///
    /// @return returns true if the process instance exists.
412 413 414 415
    bool checkProcess() {
        return (getController()->process_);
    }

416 417 418
    /// @brief Tests the existence of the Controller's IOService.
    ///
    /// @return returns true if the IOService exists.
419 420 421 422
    bool checkIOService() {
        return (getController()->io_service_);
    }

423 424 425
    /// @brief Gets the Controller's IOService.
    ///
    /// @return returns a reference to the IOService
426
    asiolink::IOServicePtr& getIOService() {
427 428 429
        return (getController()->io_service_);
    }

430
    /// @brief Compares verbose flag with the given value.
431 432 433
    ///
    /// @param value
    ///
434 435 436
    /// @return returns true if the verbose flag is equal to the given value.
    bool checkVerbose(bool value) {
        return (getController()->isVerbose() == value);
437 438
    }

439
    /// @brief Compares configuration file name with the given value.
440
    ///
441
    /// @param value file name to compare against
442 443
    ///
    /// @return returns true if the verbose flag is equal to the given value.
444
    bool checkConfigFileName(const std::string& value) {
445
        return (getController()->getConfigFile() == value);
446 447
    }

448 449
    /// @Wrapper to invoke the Controller's parseArgs method.  Please refer to
    /// DControllerBase::parseArgs for details.
450 451 452 453
    void parseArgs(int argc, char* argv[]) {
        getController()->parseArgs(argc, argv);
    }

454 455
    /// @Wrapper to invoke the Controller's init method.  Please refer to
    /// DControllerBase::init for details.
456 457 458 459
    void initProcess() {
        getController()->initProcess();
    }

460 461
    /// @Wrapper to invoke the Controller's launch method.  Please refer to
    /// DControllerBase::launch for details.
462
    void launch(int argc, char* argv[]) {
463
        optind = 1;
464
        getController()->launch(argc, argv, true);
465 466
    }

467 468 469
    /// @Wrapper to invoke the Controller's updateConfig method.  Please
    /// refer to DControllerBase::updateConfig for details.
    isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
470 471 472 473
                                            new_config) {
        return (getController()->updateConfig(new_config));
    }

474 475 476
    /// @Wrapper to invoke the Controller's executeCommand method.  Please
    /// refer to DControllerBase::executeCommand for details.
    isc::data::ConstElementPtr executeCommand(const std::string& command,
477 478 479 480 481 482 483 484
                                       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;
485
        getController()->executeCommand(SHUT_DOWN_COMMAND, arg_set);
486 487 488 489 490 491
    }

    /// @brief Callback that throws an exception.
    static void genFatalErrorCallback() {
        isc_throw (DProcessBaseError, "simulated fatal error");
    }
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

    /// @brief writes specified content to a well known file
    ///
    /// Writes given JSON content to CFG_TEST_FILE. It will wrap the
    /// content within a JSON element whose name is equal to the controller's
    /// app name or the given module name if not blank:
    ///
    /// @code
    ///    { "<app_name>" : <content> }
    /// @endcod
    ///
    /// suffix the content within a JSON element with the given module
    /// name or  wrapped by a JSON
    /// element  . Tests will
    /// attempt to read that file.
    ///
    /// @param content JSON text to be written to file
    /// @param module_name  content content to be written to file
    void writeFile(const std::string& content,
511
                   const std::string& module_name = "");
512

513 514 515 516 517
    /// @brief Method used as timer callback to invoke writeFile.
    ///
    /// Wraps a call to writeFile passing in new_cfg_content_.  This allows
    /// the method to be bound as an IntervalTimer callback.
    virtual void timedWriteCallback();
518

519 520 521 522 523 524 525 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
    /// @brief Schedules the given content to overwrite the config file.
    ///
    /// Creates a one-shot IntervalTimer whose callback will overwrite the
    /// configuration with the given content.  This allows the configuration
    /// file to replaced write_time_ms after DControllerBase::launch() has
    /// invoked runProcess().
    ///
    /// @param config JSON string containing the deisred content for the config
    /// file.
    /// @param write_time_ms time in milliseconds to delay before writing the
    /// file.
    void scheduleTimedWrite(const std::string& config, int write_time_ms);

    /// @brief Convenience method for invoking standard, valid launch
    ///
    /// This method sets up a timed run of the DController::launch.  It does
    /// the following:
    /// - It creates command line argument variables argc/argv
    /// - Invokes writeFile to create the config file with the given content
    /// - Schedules a shutdown time timer to call DController::executeShutdown
    /// after the interval
    /// - Records the start time
    /// - Invokes DController::launch() with the command line arguments
    /// - After launch returns, it calculates the elapsed time and returns it
    ///
    /// @param config configuration file content to write before calling launch
    /// @param run_time_ms  maximum amount of time to allow runProcess() to
    /// continue.
    /// @param[out] elapsed_time the actual time in ms spent in launch().
    void runWithConfig(const std::string& config, int run_time_ms,
                       time_duration& elapsed_time);

    /// @brief Fetches the controller's process
    ///
    /// @return A pointer to the process which may be null if it has not yet
    /// been instantiated.
    DProcessBasePtr getProcess();

    /// @brief Fetches the process's configuration manager
    ///
    /// @return A pointer to the manager which may be null if it has not yet
    /// been instantiated.
    DCfgMgrBasePtr getCfgMgr();

    /// @brief Fetches the configuration manager's context
    ///
    /// @return A pointer to the context which may be null if it has not yet
    /// been instantiated.
    DCfgContextBasePtr getContext();

    /// @brief Timer used for delayed configuration file writing.
    asiolink::IntervalTimerPtr write_timer_;

    /// @brief String which contains the content delayed file writing will use.
    std::string new_cfg_content_;
574

575
    /// @brief Name of a config file used during tests
576
    static const char* CFG_TEST_FILE;
577 578
};

579 580 581 582 583 584
/// @brief a collection of elements that store uint32 values
typedef isc::dhcp::ValueStorage<isc::data::ConstElementPtr> ObjectStorage;
typedef boost::shared_ptr<ObjectStorage> ObjectStoragePtr;

/// @brief Simple parser derivation for parsing object elements.
class ObjectParser : public isc::dhcp::DhcpConfigParser {
585 586 587 588 589 590 591
public:

    /// @brief Constructor
    ///
    /// See @ref DhcpConfigParser class for details.
    ///
    /// @param param_name name of the parsed parameter
592
    ObjectParser(const std::string& param_name, ObjectStoragePtr& object_values);
593 594

    /// @brief Destructor
595
    virtual ~ObjectParser();
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621

    /// @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_;
622 623 624

    /// Pointer to the storage where committed value is stored.
    ObjectStoragePtr object_values_;
625 626
};

627

628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
/// @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();

645 646 647 648 649
    /// @brief Creates a clone of a DStubContext.
    ///
    /// @return returns a pointer to the new clone.
    virtual DCfgContextBasePtr clone();

650 651 652 653 654 655 656 657
    /// @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.
658 659
    void getObjectParam(const std::string& name,
                        isc::data::ConstElementPtr& value);
660

661
    ObjectStoragePtr& getObjectStorage();
662 663 664 665 666 667 668 669 670

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

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

671 672
    /// @brief Stores non-scalar configuration elements
    ObjectStoragePtr object_values_;
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
};

/// @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.
708 709
    /// @param pos position within the configuration text (or file) of element
    /// to be parsed.  This is passed for error messaging.
710 711 712 713
    ///
    /// @return returns a ParserPtr to the parser instance.
    /// @throw throws DCfgMgrBaseError if SimFailure is ftElementUnknown.
    virtual isc::dhcp::ParserPtr
714 715 716
    createConfigParser(const std::string& element_id,
                       const isc::data::Element::Position& pos
                       = isc::data::Element::Position());
717

718 719 720
    /// @brief Returns a summary of the configuration in the textual format.
    ///
    /// @return Always an empty string.
721
    virtual std::string getConfigSummary(const uint32_t) {
722 723 724
        return ("");
    }

725 726 727
    /// @brief A list for remembering the element ids in the order they were
    /// parsed.
    ElementIdList parsed_order_;
728 729 730

    /// @todo
    virtual DCfgContextBasePtr createNewContext();
731 732 733 734 735
};

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

736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
/// @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.
755 756
    /// @return returns AssertionSuccess if there were no parsing errors,
    /// AssertionFailure otherwise.
757
    ::testing::AssertionResult fromJSON(const std::string& json_text) {
758 759
        try  {
            config_set_ = isc::data::Element::fromJSON(json_text);
760
        } catch (const isc::Exception &ex) {
761 762 763
            return (::testing::AssertionFailure(::testing::Message() <<
                                                "JSON text failed to parse:"
                                                << ex.what()));
764 765
        }

766
        return (::testing::AssertionSuccess());
767 768 769 770 771 772 773
    }

    /// @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.
    ///
774 775 776
    /// @return returns AssertionSuccess if there were no parsing errors,
    /// AssertionFailure otherwise.
    ::testing::AssertionResult checkAnswer(int should_be) {
777 778 779 780 781 782
        return (checkAnswer(answer_, should_be));
    }

    /// @brief Compares the status in the given parse result to a given value.
    ///
    /// @param answer Element set containing an integer response and string
783
    /// comment.
784 785 786 787 788 789
    /// @param should_be is an integer against which to compare the status.
    ///
    /// @return returns AssertionSuccess if there were no parsing errors,
    /// AssertionFailure otherwise.
    ::testing::AssertionResult checkAnswer(isc::data::ConstElementPtr answer,
                                           int should_be) {
790 791
        int rcode = 0;
        isc::data::ConstElementPtr comment;
792
        comment = isc::config::parseAnswer(rcode, answer);
793
        if (rcode == should_be) {
794
            return (testing::AssertionSuccess());
795
        }
796

797 798
        return (::testing::AssertionFailure(::testing::Message() <<
                                            "checkAnswer rcode:" << rcode
799
                                            << " comment: " << *comment));
800 801 802 803 804 805 806 807 808
    }

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

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

809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
/// @brief Implements a time-delayed signal
///
/// Given an IOService, a signal number, and a time period, this class will
/// send (raise) the signal to the current process.
class TimedSignal {
public:
    /// @brief Constructor
    ///
    /// @param io_service  IOService to run the timer
    /// @param signum OS signal value (e.g. SIGHUP, SIGUSR1 ...)
    /// @param milliseconds time in milliseconds to wait until the signal is
    /// raised.
    /// @param mode selects between a one-shot signal or a signal which repeats
    /// at "milliseconds" interval.
    TimedSignal(asiolink::IOService& io_service, int signum, int milliseconds,
                const asiolink::IntervalTimer::Mode& mode =
                asiolink::IntervalTimer::ONE_SHOT)
        : timer_(new asiolink::IntervalTimer(io_service)) {
        timer_->setup(SendSignalCallback(signum), milliseconds, mode);
    }

    /// @brief Cancels the given timer.
    void cancel() {
        if (timer_) {
            timer_->cancel();
        }
    }

    /// @brief Destructor.
    ~TimedSignal() {
        cancel();
    }

    /// @brief Callback for the TimeSignal's internal timer.
    class SendSignalCallback: public std::unary_function<void, void> {
    public:

        /// @brief Constructor
        ///
        /// @param signum OS signal value of the signal to send
        SendSignalCallback(int signum) : signum_(signum) {
        }

        /// @brief Callback method invoked when the timer expires
        ///
        /// Calls raise with the given signal which should generate that
        /// signal to the given process.
        void operator()() {
            ASSERT_EQ(0, raise(signum_));
            return;
        }

    private:
        /// @brief Stores the OS signal value to send.
        int signum_;
    };

private:
    /// @brief Timer which controls when the signal is sent.
    asiolink::IntervalTimerPtr timer_;
};

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

875
}; // namespace isc::d2
876 877 878
}; // namespace isc

#endif