d_controller_unittests.cc 15.3 KB
Newer Older
1
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
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
//
// 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
    ASSERT_NO_THROW(boost::dynamic_pointer_cast<DStubController>(controller));

55 56 57 58 59
    // Verify that controller's app name is correct.
    EXPECT_TRUE(checkAppName(DStubController::stub_app_name_));

    // Verify that controller's bin name is correct.
    EXPECT_TRUE(checkBinName(DStubController::stub_bin_name_));
60 61

    // Verify that controller's spec file name is correct.
62
    EXPECT_TRUE(checkSpecFileName(D2_SPECFILE_LOCATION));
63 64

    // Verify that controller's IOService exists.
65 66
    EXPECT_TRUE(checkIOService());

67
    // Verify that the Process does NOT exist.
68 69 70
    EXPECT_FALSE(checkProcess());
}

71 72 73 74 75 76
/// @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.
77 78
TEST_F(DStubControllerTest, commandLineArgs) {

79
    // Verify that verbose flag is false initially.
80 81
    EXPECT_TRUE(checkVerbose(false));

82
    // Verify that standard options can be parsed without error.
83
    char* argv[] = { const_cast<char*>("progName"),
84 85
                     const_cast<char*>("-c"),
                     const_cast<char*>("cfgName"),
86
                     const_cast<char*>("-v") };
87
    int argc = 4;
88 89
    EXPECT_NO_THROW(parseArgs(argc, argv));

90
    // Verify that verbose is true.
91 92
    EXPECT_TRUE(checkVerbose(true));

93 94 95
    // Verify configuration file name is correct
    EXPECT_TRUE(checkConfigFileName("cfgName"));

96
    // Verify that the custom command line option is parsed without error.
97 98 99
    char xopt[3] = "- ";
    xopt[1] =  *DStubController::stub_option_x_;
    char* argv1[] = { const_cast<char*>("progName"), xopt};
100 101 102
    argc = 2;
    EXPECT_NO_THROW (parseArgs(argc, argv1));

103
    // Verify that an unknown option is detected.
104 105
    char* argv2[] = { const_cast<char*>("progName"),
                      const_cast<char*>("-bs") };
106 107
    argc = 2;
    EXPECT_THROW (parseArgs(argc, argv2), InvalidUsage);
108 109

    // Verify that extraneous information is detected.
110 111 112
    char* argv3[] = { const_cast<char*>("progName"),
                      const_cast<char*>("extra"),
                      const_cast<char*>("information") };
113 114
    argc = 3;
    EXPECT_THROW (parseArgs(argc, argv3), InvalidUsage);
115 116
}

117 118 119 120 121 122
/// @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.
123
TEST_F(DStubControllerTest, initProcessTesting) {
124
    // Verify that a failure during process creation is caught.
125 126 127 128
    SimFailure::set(SimFailure::ftCreateProcessException);
    EXPECT_THROW(initProcess(), DControllerBaseError);
    EXPECT_FALSE(checkProcess());

129
    // Verify that a NULL returned by process creation is handled.
130 131 132 133
    SimFailure::set(SimFailure::ftCreateProcessNull);
    EXPECT_THROW(initProcess(), DControllerBaseError);
    EXPECT_FALSE(checkProcess());

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

    // Verify that an error during process initialization is handled.
139 140 141
    SimFailure::set(SimFailure::ftProcessInit);
    EXPECT_THROW(initProcess(), DProcessBaseError);

142
    // Re-create controller, verify that we are starting clean
143 144 145
    resetController();
    EXPECT_FALSE(checkProcess());

146
    // Verify that the application process can created and initialized.
147 148 149 150
    ASSERT_NO_THROW(initProcess());
    EXPECT_TRUE(checkProcess());
}

151
/// @brief Tests launch handling of invalid command line.
152 153
/// This test launches with an invalid command line which should throw
/// an InvalidUsage.
154 155
TEST_F(DStubControllerTest, launchInvalidUsage) {
    // Command line to run integrated
156 157
    char* argv[] = { const_cast<char*>("progName"),
                     const_cast<char*>("-z") };
158 159 160
    int argc = 2;

    // Launch the controller in integrated mode.
161
    EXPECT_THROW(launch(argc, argv), InvalidUsage);
162 163 164 165
}

/// @brief Tests launch handling of failure in application process
/// initialization.  This test launches with a valid command line but with
166 167
/// SimFailure set to fail during process creation.  Launch should throw
/// ProcessInitError.
168 169
TEST_F(DStubControllerTest, launchProcessInitError) {
    // Command line to run integrated
170
    char* argv[] = { const_cast<char*>("progName"),
171 172
                     const_cast<char*>("-c"),
                     const_cast<char*>(DControllerTest::CFG_TEST_FILE),
173
                     const_cast<char*>("-v") };
174
    int argc = 4;
175 176 177

    // Launch the controller in stand alone mode.
    SimFailure::set(SimFailure::ftCreateProcessException);
178
    EXPECT_THROW(launch(argc, argv), ProcessInitError);
179 180
}

181 182
/// @brief Tests launch and normal shutdown (stand alone mode).
/// This creates an interval timer to generate a normal shutdown and then
183 184
/// launches with a valid, command line, with a valid configuration file
///  and no simulated errors.
185 186
TEST_F(DStubControllerTest, launchNormalShutdown) {
    // command line to run standalone
187
    char* argv[] = { const_cast<char*>("progName"),
188 189
                     const_cast<char*>("-c"),
                     const_cast<char*>(DControllerTest::CFG_TEST_FILE),
190
                     const_cast<char*>("-v") };
191 192 193 194 195
    int argc = 4;

    // Create a non-empty, config file.  writeFile will wrap the contents
    // with the module name for us.
    writeFile("{}");
196 197 198 199 200 201 202 203

    // 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();
204
    EXPECT_NO_THROW(launch(argc, argv));
205 206 207 208 209 210 211 212 213

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

    // 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 &&
214
                elapsed.total_milliseconds() <= 2200);
215 216
}

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
/// @brief Tests launch with an nonexistant configuration file.
TEST_F(DStubControllerTest, nonexistantConfigFile) {
    // command line to run standalone
    char* argv[] = { const_cast<char*>("progName"),
                     const_cast<char*>("-c"),
                     const_cast<char*>("bogus-file"),
                     const_cast<char*>("-v") };
    int argc = 4;

    // Record start time, and invoke launch().
    EXPECT_THROW(launch(argc, argv), ProcessInitError);
}

/// @brief Tests launch with configuration file argument but no file name
TEST_F(DStubControllerTest, missingConfigFileName) {
    // command line to run standalone
    char* argv[] = { const_cast<char*>("progName"),
                     const_cast<char*>("-c"),
                     const_cast<char*>("-v") };
    int argc = 3;

    // Record start time, and invoke launch().
    EXPECT_THROW(launch(argc, argv), ProcessInitError);
}

/// @brief Tests launch with no configuration file argument
TEST_F(DStubControllerTest, missingConfigFileArgument) {
    // command line to run standalone
    char* argv[] = { const_cast<char*>("progName"),
                     const_cast<char*>("-v") };
    int argc = 2;

    // Record start time, and invoke launch().
    EXPECT_THROW(launch(argc, argv), ProcessInitError);
}

253 254 255
/// @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
256
/// and no simulated errors.  Launch should throw ProcessRunError.
257
TEST_F(DStubControllerTest, launchRuntimeError) {
258
    // command line to run standalone
259
    char* argv[] = { const_cast<char*>("progName"),
260 261
                     const_cast<char*>("-c"),
                     const_cast<char*>(DControllerTest::CFG_TEST_FILE),
262
                     const_cast<char*>("-v") };
263 264 265 266 267
    int argc = 4;

    // Create a non-empty, config file.  writeFile will wrap the contents
    // with the module name for us.
    writeFile("{}");
268 269 270 271 272 273 274 275

    // 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();
276
    EXPECT_THROW(launch(argc, argv), ProcessRunError);
277 278 279 280 281 282 283 284 285

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

    // 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 &&
286
                elapsed.total_milliseconds() <= 2200);
287 288
}

289
/// @brief Configuration update event testing.
290
/// This really tests just the ability of the handlers to invoke the necessary
291 292 293 294 295
/// 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:
296 297
/// 1. That a valid configuration update results in successful status return.
/// 2. That an application process error in configuration updating is handled
298
/// properly.
299 300 301 302
TEST_F(DStubControllerTest, configUpdateTests) {
    int rcode = -1;
    isc::data::ConstElementPtr answer;

303
    // Initialize the application process.
304 305 306
    ASSERT_NO_THROW(initProcess());
    EXPECT_TRUE(checkProcess());

307
    // Create a configuration set. Content is arbitrary, just needs to be
308 309 310 311
    // valid JSON.
    std::string config = "{ \"test-value\": 1000 } ";
    isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config);

312
    // Verify that a valid config gets a successful update result.
313 314 315 316 317 318 319 320 321 322 323
    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);
}

324 325 326 327 328 329 330 331 332 333 334 335 336 337
/// @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.
338 339 340 341 342
TEST_F(DStubControllerTest, executeCommandTests) {
    int rcode = -1;
    isc::data::ConstElementPtr answer;
    isc::data::ElementPtr arg_set;

343
    // Initialize the application process.
344 345 346
    ASSERT_NO_THROW(initProcess());
    EXPECT_TRUE(checkProcess());

347 348 349 350 351 352 353
    // 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.
354 355 356 357
    answer = DControllerBase::commandHandler(SHUT_DOWN_COMMAND, arg_set);
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(COMMAND_SUCCESS, rcode);

358 359
    // Verify that a valid custom controller command returns
    // d2::COMMAND_SUCCESS response.
360
    answer = DControllerBase::commandHandler(DStubController::
361
                                             stub_ctl_command_, arg_set);
362 363 364
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(COMMAND_SUCCESS, rcode);

365
    // Verify that a valid custom process command returns d2::COMMAND_SUCCESS
366 367
    // response.
    answer = DControllerBase::commandHandler(DStubProcess::
368
                                             stub_proc_command_, arg_set);
369 370
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(COMMAND_SUCCESS, rcode);
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

    // 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);
387 388 389 390
}

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