d_controller_unittests.cc 14.3 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 24 25 26 27 28 29
// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <config/ccsession.h>
#include <d_test_stubs.h>
#include <d2/spec_config.h>

#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>

#include <config.h>
#include <sstream>

using namespace boost::posix_time;

namespace isc {
namespace d2 {

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
/// @brief Test fixture class for testing DControllerBase class. This class
/// derives from DControllerTest and wraps a DStubController.  DStubController
/// has been constructed to exercise DControllerBase.
class DStubControllerTest : public DControllerTest {
public:

    /// @brief Constructor.
    /// Note the constructor passes in the static DStubController instance
    /// method.
    DStubControllerTest() : DControllerTest (DStubController::instance) {
    }

    virtual ~DStubControllerTest() {
    }
};

/// @brief Basic Controller instantiation testing.
/// Verfies that the controller singleton gets created and that the
/// basic derivation from the base class is intact.
49
TEST_F(DStubControllerTest, basicInstanceTesting) {
50
    // Verify that the singleton exists and it is the correct type.
51 52
    DControllerBasePtr& controller = DControllerTest::getController();
    ASSERT_TRUE(controller);
53 54 55
    ASSERT_NO_THROW(boost::dynamic_pointer_cast<DStubController>(controller));

    // Verify that controller's name is correct.
56
    EXPECT_TRUE(checkName(D2_MODULE_NAME));
57 58

    // Verify that controller's spec file name is correct.
59
    EXPECT_TRUE(checkSpecFileName(D2_SPECFILE_LOCATION));
60 61

    // Verify that controller's IOService exists.
62 63
    EXPECT_TRUE(checkIOService());

64
    // Verify that the Process does NOT exist.
65 66 67
    EXPECT_FALSE(checkProcess());
}

68 69 70 71 72 73
/// @brief Tests basic command line processing.
/// Verifies that:
/// 1. Standard command line options are supported.
/// 2. Custom command line options are supported.
/// 3. Invalid options are detected.
/// 4. Extraneous command line information is detected.
74 75
TEST_F(DStubControllerTest, commandLineArgs) {

76
    // Verify that both flags are false initially.
77 78 79
    EXPECT_TRUE(checkStandAlone(false));
    EXPECT_TRUE(checkVerbose(false));

80 81 82
    // Verify that standard options can be parsed without error.
    char* argv[] = { (char*)"progName", (char*)"-s", (char*)"-v" };
    int argc = 3;
83 84
    EXPECT_NO_THROW(parseArgs(argc, argv));

85
    // Verify that flags are now true.
86 87 88
    EXPECT_TRUE(checkStandAlone(true));
    EXPECT_TRUE(checkVerbose(true));

89 90 91 92
    // Verify that the custom command line option is parsed without error.
    char xopt[3]="";
    sprintf (xopt, "-%c", *DStubController::stub_option_x_);
    char* argv1[] = { (char*)"progName", xopt};
93 94 95
    argc = 2;
    EXPECT_NO_THROW (parseArgs(argc, argv1));

96
    // Verify that an unknown option is detected.
97 98 99
    char* argv2[] = { (char*)"progName", (char*)"-bs" };
    argc = 2;
    EXPECT_THROW (parseArgs(argc, argv2), InvalidUsage);
100 101 102 103 104 105 106

    // Verify that extraneous information is detected.
    char* argv3[] = { (char*)"progName", (char*)"extra", (char*)"information" };
    argc = 3;
    EXPECT_THROW (parseArgs(argc, argv3), InvalidUsage);


107 108
}

109 110 111 112 113 114
/// @brief Tests application process creation and initialization.
/// Verifies that:
/// 1. An error during process creation is handled.
/// 2. A NULL returned by process creation is handled.
/// 3. An error during process initialization is handled.
/// 4. Process can be successfully created and initialized.
115
TEST_F(DStubControllerTest, initProcessTesting) {
116
    // Verify that a failure during process creation is caught.
117 118 119 120
    SimFailure::set(SimFailure::ftCreateProcessException);
    EXPECT_THROW(initProcess(), DControllerBaseError);
    EXPECT_FALSE(checkProcess());

121
    // Verify that a NULL returned by process creation is handled.
122 123 124 125
    SimFailure::set(SimFailure::ftCreateProcessNull);
    EXPECT_THROW(initProcess(), DControllerBaseError);
    EXPECT_FALSE(checkProcess());

126
    // Re-create controller, verify that we are starting clean
127
    resetController();
128 129 130
    EXPECT_FALSE(checkProcess());

    // Verify that an error during process initialization is handled.
131 132 133
    SimFailure::set(SimFailure::ftProcessInit);
    EXPECT_THROW(initProcess(), DProcessBaseError);

134
    // Re-create controller, verify that we are starting clean
135 136 137
    resetController();
    EXPECT_FALSE(checkProcess());

138
    // Verify that the application process can created and initialized.
139 140 141 142
    ASSERT_NO_THROW(initProcess());
    EXPECT_TRUE(checkProcess());
}

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
/// @brief Tests launch handling of invalid command line.
/// This test launches with an invalid command line which should exit with
/// an status of d2::INVALID_USAGE.
TEST_F(DStubControllerTest, launchInvalidUsage) {
    // Command line to run integrated
    char* argv[] = { (char*)"progName",(char*) "-z" };
    int argc = 2;

    // Launch the controller in integrated mode.
    int rcode = launch(argc, argv);

    // Verify session failure exit status.
    EXPECT_EQ(d2::INVALID_USAGE, rcode);
}

/// @brief Tests launch handling of failure in application process
/// initialization.  This test launches with a valid command line but with
/// SimFailure set to fail during process creation.  Launch exit code should
/// be d2::PROCESS_INIT_ERROR.
TEST_F(DStubControllerTest, launchProcessInitError) {
    // Command line to run integrated
    char* argv[] = { (char*)"progName", (char*)"-s", (char*)"-v" };
    int argc = 3;

    // Launch the controller in stand alone mode.
    SimFailure::set(SimFailure::ftCreateProcessException);
    int rcode = launch(argc, argv);
170

171 172
    // Verify session failure exit status.
    EXPECT_EQ(d2::PROCESS_INIT_ERROR, rcode);
173 174
}

175 176 177 178 179 180
/// @brief Tests launch and normal shutdown (stand alone mode).
/// This creates an interval timer to generate a normal shutdown and then
/// launches with a valid, stand-alone command line and no simulated errors.
/// Launch exit code should be d2::NORMAL_EXIT.
TEST_F(DStubControllerTest, launchNormalShutdown) {
    // command line to run standalone
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
    char* argv[] = { (char*)"progName", (char*)"-s", (char*)"-v" };
    int argc = 3;

    // Use an asiolink IntervalTimer and callback to generate the
    // shutdown invocation. (Note IntervalTimer setup is in milliseconds).
    isc::asiolink::IntervalTimer timer(*getIOService());
    timer.setup(genShutdownCallback, 2 * 1000);

    // Record start time, and invoke launch().
    ptime start = microsec_clock::universal_time();
    int rcode = launch(argc, argv);

    // Record stop time.
    ptime stop = microsec_clock::universal_time();

    // Verify normal shutdown status.
197
    EXPECT_EQ(d2::NORMAL_EXIT, rcode);
198 199 200 201 202 203 204 205 206

    // Verify that duration of the run invocation is the same as the
    // timer duration.  This demonstrates that the shutdown was driven
    // by an io_service event and callback.
    time_duration elapsed = stop - start;
    EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 &&
                elapsed.total_milliseconds() <= 2100);
}

207 208 209 210
/// @brief Tests launch with an operational error during application execution.
/// This test creates an interval timer to generate a runtime exception during
/// the process event loop. It launches wih a valid, stand-alone command line
/// and no simulated errors.  Launch exit code should be d2::RUN_ERROR.
211
TEST_F(DStubControllerTest, launchRuntimeError) {
212
    // command line to run standalone
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
    char* argv[] = { (char*)"progName", (char*)"-s", (char*)"-v" };
    int argc = 3;

    // Use an asiolink IntervalTimer and callback to generate the
    // shutdown invocation. (Note IntervalTimer setup is in milliseconds).
    isc::asiolink::IntervalTimer timer(*getIOService());
    timer.setup(genFatalErrorCallback, 2 * 1000);

    // Record start time, and invoke launch().
    ptime start = microsec_clock::universal_time();
    int rcode = launch(argc, argv);

    // Record stop time.
    ptime stop = microsec_clock::universal_time();

228 229
    // Verify abnormal shutdown status.
    EXPECT_EQ(d2::RUN_ERROR, rcode);
230 231 232 233 234 235 236 237 238

    // Verify that duration of the run invocation is the same as the
    // timer duration.  This demonstrates that the shutdown was driven
    // by an io_service event and callback.
    time_duration elapsed = stop - start;
    EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 &&
                elapsed.total_milliseconds() <= 2100);
}

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
/// @brief Tests launch with a session establishment failure.
/// This test launches with a valid command line for integrated mode and no.
/// Attempting to connect to BIND10 should fail, even if BIND10 is running
/// UNLESS the test is run as root.  Launch exit code should be
/// d2::SESSION_START_ERROR.
TEST_F(DStubControllerTest, launchSessionFailure) {
    // Command line to run integrated
    char* argv[] = { (char*)"progName" };
    int argc = 1;

    // Launch the controller in integrated mode.
    int rcode = launch(argc, argv);

    // Verify session failure exit status.
    EXPECT_EQ(d2::SESSION_START_ERROR, rcode);
}

/// @brief Configuration update event testing.
257
/// This really tests just the ability of the handlers to invoke the necessary
258 259 260 261 262 263 264 265 266 267 268
/// chain of methods and handle error conditions. Configuration parsing and
/// retrieval should be tested as part of the d2 configuration management
/// implementation.  Note that this testing calls the configuration update event
/// callback, configHandler, directly.
/// This test verifies that:
/// 1. Configuration will be rejected in integrated mode when there is no
/// session established. (This is a very contrived situation).
/// 2. In stand-alone mode a configuration update results in successful
/// status return.
/// 3. That an application process error in configuration updating is handled
/// properly.
269 270 271 272
TEST_F(DStubControllerTest, configUpdateTests) {
    int rcode = -1;
    isc::data::ConstElementPtr answer;

273
    // Initialize the application process.
274 275 276
    ASSERT_NO_THROW(initProcess());
    EXPECT_TRUE(checkProcess());

277
    // Create a configuration set. Content is arbitrary, just needs to be
278 279 280 281 282
    // valid JSON.
    std::string config = "{ \"test-value\": 1000 } ";
    isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config);

    // We are not stand-alone, so configuration should be rejected as there is
283
    // no session.  This is a pretty contrived situation that shouldn't be
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
    // possible other than the handler being called directly (like this does).
    answer = DControllerBase::configHandler(config_set);
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(1, rcode);

    // Verify that in stand alone we get a successful update result.
    setStandAlone(true);
    answer = DControllerBase::configHandler(config_set);
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(0, rcode);

    // Verify that an error in process configure method is handled.
    SimFailure::set(SimFailure::ftProcessConfigure);
    answer = DControllerBase::configHandler(config_set);
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(1, rcode);
}

302 303 304 305 306 307 308 309 310 311 312 313 314 315
/// @brief Command execution tests.
/// This really tests just the ability of the handler to invoke the necessary
/// chain of methods and to handle error conditions. Note that this testing
/// calls the command callback, commandHandler, directly.
/// This test verifies that:
/// 1. That an unrecognized command is detected and returns a status of
/// d2::COMMAND_INVALID.
/// 2. Shutdown command is recognized and returns a d2::COMMAND_SUCCESS status.
/// 3. A valid, custom controller command is recognized a d2::COMMAND_SUCCESS
/// status.
/// 4. A valid, custom process command is recognized a d2::COMMAND_SUCCESS
/// status.
/// 5. That a valid controller command that fails returns a d2::COMMAND_ERROR.
/// 6. That a valid process command that fails returns a d2::COMMAND_ERROR.
316 317 318 319 320
TEST_F(DStubControllerTest, executeCommandTests) {
    int rcode = -1;
    isc::data::ConstElementPtr answer;
    isc::data::ElementPtr arg_set;

321
    // Initialize the application process.
322 323 324
    ASSERT_NO_THROW(initProcess());
    EXPECT_TRUE(checkProcess());

325 326 327 328 329 330 331
    // Verify that an unknown command returns an d2::COMMAND_INVALID response.
    std::string bogus_command("bogus");
    answer = DControllerBase::commandHandler(bogus_command, arg_set);
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(COMMAND_INVALID, rcode);

    // Verify that shutdown command returns d2::COMMAND_SUCCESS response.
332 333 334 335
    answer = DControllerBase::commandHandler(SHUT_DOWN_COMMAND, arg_set);
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(COMMAND_SUCCESS, rcode);

336 337
    // Verify that a valid custom controller command returns
    // d2::COMMAND_SUCCESS response.
338
    answer = DControllerBase::commandHandler(DStubController::
339
                                             stub_ctl_command_, arg_set);
340 341 342
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(COMMAND_SUCCESS, rcode);

343
    // Verify that a valid custom process command returns d2::COMMAND_SUCCESS
344 345
    // response.
    answer = DControllerBase::commandHandler(DStubProcess::
346
                                             stub_proc_command_, arg_set);
347 348
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(COMMAND_SUCCESS, rcode);
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364

    // Verify that a valid custom controller command that fails returns
    // a d2::COMMAND_ERROR.
    SimFailure::set(SimFailure::ftControllerCommand);
    answer = DControllerBase::commandHandler(DStubController::
                                             stub_ctl_command_, arg_set);
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(COMMAND_ERROR, rcode);

    // Verify that a valid custom process command that fails returns
    // a d2::COMMAND_ERROR.
    SimFailure::set(SimFailure::ftProcessCommand);
    answer = DControllerBase::commandHandler(DStubProcess::
                                             stub_proc_command_, arg_set);
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(COMMAND_ERROR, rcode);
365 366 367 368
}

}; // end of isc::d2 namespace
}; // end of isc namespace