d2_controller_unittests.cc 11 KB
Newer Older
1
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6

7 8
#include <config.h>

9
#include <cc/command_interpreter.h>
10
#include <d2/d2_controller.h>
11
#include <d2/d2_process.h>
12
#include <d2/tests/nc_test_utils.h>
13
#include <process/testutils/d_test_stubs.h>
14

15
#include <boost/pointer_cast.hpp>
16 17 18 19 20
#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>

#include <sstream>

21
using namespace isc::process;
22 23 24 25 26
using namespace boost::posix_time;

namespace isc {
namespace d2 {

27 28 29 30
/// @brief Test fixture class for testing D2Controller class.
///
/// This class derives from DControllerTest and wraps a D2Controller. Much of
/// the underlying functionality is in the DControllerBase class which has an
31 32 33 34 35 36 37 38
/// extensive set of unit tests that are independent of DHCP-DDNS.
/// @TODO Currently These tests are relatively light and duplicate some of
/// the testing done on the base class.  These tests are sufficient to ensure
/// that D2Controller properly derives from its base class and to test the
/// logic that is unique to D2Controller. These tests will be augmented and
/// or new tests added as additional functionality evolves.
/// Unlike the stub testing, there is no use of SimFailure to induce error
/// conditions as this is production code.
39 40 41
class D2ControllerTest : public DControllerTest {
public:
    /// @brief Constructor
42 43
    /// Note the constructor passes in the static D2Controller instance
    /// method.
44 45 46
    D2ControllerTest() : DControllerTest(D2Controller::instance) {
    }

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
    /// @brief Fetches the D2Controller's D2Process
    ///
    /// @return A pointer to the process which may be null if it has not yet
    /// been instantiated.
    D2ProcessPtr getD2Process() {
        return (boost::dynamic_pointer_cast<D2Process>(getProcess()));
    }

    /// @brief Fetches the D2Process's D2Configuration manager
    ///
    /// @return A pointer to the manager which may be null if it has not yet
    /// been instantiated.
    D2CfgMgrPtr getD2CfgMgr() {
        D2CfgMgrPtr p;
        if (getD2Process()) {
            p = getD2Process()->getD2CfgMgr();
        }

        return (p);
    }

    /// @brief Fetches the D2Configuration manager's D2CfgContext
    ///
    /// @return A pointer to the context which may be null if it has not yet
    /// been instantiated.
    D2CfgContextPtr getD2CfgContext() {
        D2CfgContextPtr p;
        if (getD2CfgMgr()) {
            p = getD2CfgMgr()->getD2CfgContext();
        }

        return (p);
    }
80 81
};

82
/// @brief Basic Controller instantiation testing.
83
/// Verifies that the controller singleton gets created and that the
84
/// basic derivation from the base class is intact.
85
TEST_F(D2ControllerTest, basicInstanceTesting) {
86 87
    // Verify the singleton instance can be fetched and that
    // it has the correct type.
88 89
    DControllerBasePtr& controller = DControllerTest::getController();
    ASSERT_TRUE(controller);
90 91
    ASSERT_NO_THROW(boost::dynamic_pointer_cast<D2Controller>(controller));

92 93 94 95 96
    // Verify that controller's app name is correct.
    EXPECT_TRUE(checkAppName(D2Controller::d2_app_name_));

    // Verify that controller's bin name is correct.
    EXPECT_TRUE(checkBinName(D2Controller::d2_bin_name_));
97 98

    // Verify that controller's IOService exists.
99 100
    EXPECT_TRUE(checkIOService());

101
    // Verify that the Process does NOT exist.
102 103 104
    EXPECT_FALSE(checkProcess());
}

105
/// @brief Tests basic command line processing.
106
/// Verifies that:
107 108
/// 1. Standard command line options are supported.
/// 2. Invalid options are detected.
109
TEST_F(D2ControllerTest, commandLineArgs) {
110
    char* argv[] = { const_cast<char*>("progName"),
111 112
                     const_cast<char*>("-c"),
                     const_cast<char*>(DControllerTest::CFG_TEST_FILE),
113
                     const_cast<char*>("-d") };
114
    int argc = 4;
115

116
    // Verify that verbose flag is false initially.
117 118
    EXPECT_TRUE(checkVerbose(false));

119
    // Verify that standard options can be parsed without error.
120 121
    EXPECT_NO_THROW(parseArgs(argc, argv));

122
    // Verify that verbose flag is true.
123 124
    EXPECT_TRUE(checkVerbose(true));

125 126 127
    // Verify configuration file name is correct.
    EXPECT_TRUE(checkConfigFileName(DControllerTest::CFG_TEST_FILE));

128
    // Verify that an unknown option is detected.
129
    char* argv2[] = { const_cast<char*>("progName"),
130
                      const_cast<char*>("-x") };
131
    argc = 2;
132
    EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage);
133 134
}

135 136
/// @brief Tests application process creation and initialization.
/// Verifies that the process can be successfully created and initialized.
137 138 139 140 141
TEST_F(D2ControllerTest, initProcessTesting) {
    ASSERT_NO_THROW(initProcess());
    EXPECT_TRUE(checkProcess());
}

142 143 144 145
/// @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.
TEST_F(D2ControllerTest, launchNormalShutdown) {
146 147 148
    // Write valid_d2_config and then run launch() for 1000 ms.
    time_duration elapsed_time;
    runWithConfig(valid_d2_config, 1000, elapsed_time);
149

Andrei Pavel's avatar
Andrei Pavel committed
150
    // Give a generous margin to accommodate slower test environs.
151 152
    EXPECT_TRUE(elapsed_time.total_milliseconds() >= 800 &&
                elapsed_time.total_milliseconds() <= 1300);
153 154
}

155
/// @brief Configuration update event testing.
156
/// This really tests just the ability of the handlers to invoke the necessary
157 158
/// chain of methods and handle error conditions. Configuration parsing and
/// retrieval should be tested as part of the d2 configuration management
159
/// implementation.
160
/// This test verifies that:
161 162
/// 1. A valid configuration yields a successful parse result.
/// 2. That an application process error in configuration updating is handled
163
/// properly.
164 165 166 167
TEST_F(D2ControllerTest, configUpdateTests) {
    int rcode = -1;
    isc::data::ConstElementPtr answer;

168
    // Initialize the application process.
169 170 171
    ASSERT_NO_THROW(initProcess());
    EXPECT_TRUE(checkProcess());

172 173 174
    // Create a configuration set using a small, valid D2 configuration.
    isc::data::ElementPtr config_set =
                                isc::data::Element::fromJSON(valid_d2_config);
175

176
    // Verify that given a valid config we get a successful update result.
177
    answer = updateConfig(config_set);
178 179 180 181
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(0, rcode);

    // Verify that given a valid config we get a successful check result.
182
    answer = checkConfig(config_set);
183 184
    isc::config::parseAnswer(rcode, answer);
    EXPECT_EQ(0, rcode);
185 186

    // Use an invalid configuration to verify parsing error return.
187
    std::string config = "{ \"ip-address\": 1000 } ";
188
    config_set = isc::data::Element::fromJSON(config);
189
    answer = updateConfig(config_set);
190
    isc::config::parseAnswer(rcode, answer);
191
    EXPECT_EQ(2, rcode);
192 193

    // Use an invalid configuration to verify checking error return.
194
    answer = checkConfig(config_set);
195
    isc::config::parseAnswer(rcode, answer);
196
    EXPECT_EQ(2, rcode);
197 198
}

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
// Tests that the original configuration is retained after a SIGHUP triggered
// reconfiguration fails due to invalid config content.
TEST_F(D2ControllerTest, invalidConfigReload) {
    // Schedule to replace the configuration file after launch. This way the
    // file is updated after we have done the initial configuration.
    scheduleTimedWrite("{ \"string_test\": BOGUS JSON }", 100);

    // Setup to raise SIGHUP in 200 ms.
    TimedSignal sighup(*getIOService(), SIGHUP, 200);

    // Write valid_d2_config and then run launch() for a maximum of 500 ms.
    time_duration elapsed_time;
    runWithConfig(valid_d2_config, 500, elapsed_time);

    // Context is still available post launch.
    // Check to see that our configuration matches the original per
215
    // valid_d2_config (see src/lib/process/testutils/d_test_stubs.cc)
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    D2CfgMgrPtr d2_cfg_mgr = getD2CfgMgr();
    D2ParamsPtr d2_params = d2_cfg_mgr->getD2Params();
    ASSERT_TRUE(d2_params);

    EXPECT_EQ("127.0.0.1", d2_params->getIpAddress().toText());
    EXPECT_EQ(5031, d2_params->getPort());
    EXPECT_TRUE(d2_cfg_mgr->forwardUpdatesEnabled());
    EXPECT_TRUE(d2_cfg_mgr->reverseUpdatesEnabled());

    /// @todo add a way to trap log file and search it
}

// Tests that the original configuration is replaced after a SIGHUP triggered
// reconfiguration succeeds.
TEST_F(D2ControllerTest, validConfigReload) {
    // Define a replacement config.
    const char* second_cfg =
            "{"
234
            " \"ip-address\": \"192.168.77.1\" , "
235
            " \"port\": 777 , "
236 237 238
            "\"tsig-keys\": [], "
            "\"forward-ddns\" : {}, "
            "\"reverse-ddns\" : {} "
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
            "}";

    // Schedule to replace the configuration file after launch. This way the
    // file is updated after we have done the initial configuration.
    scheduleTimedWrite(second_cfg, 100);

    // Setup to raise SIGHUP in 200 ms.
    TimedSignal sighup(*getIOService(), SIGHUP, 200);

    // Write valid_d2_config and then run launch() for a maximum of 500ms.
    time_duration elapsed_time;
    runWithConfig(valid_d2_config, 500, elapsed_time);

    // Context is still available post launch.
    // Check to see that our configuration matches the replacement config.
    D2CfgMgrPtr d2_cfg_mgr = getD2CfgMgr();
    D2ParamsPtr d2_params = d2_cfg_mgr->getD2Params();
    ASSERT_TRUE(d2_params);

    EXPECT_EQ("192.168.77.1", d2_params->getIpAddress().toText());
    EXPECT_EQ(777, d2_params->getPort());
    EXPECT_FALSE(d2_cfg_mgr->forwardUpdatesEnabled());
    EXPECT_FALSE(d2_cfg_mgr->reverseUpdatesEnabled());

    /// @todo add a way to trap log file and search it
}

// Tests that the SIGINT triggers a normal shutdown.
TEST_F(D2ControllerTest, sigintShutdown) {
    // Setup to raise SIGHUP in 1 ms.
    TimedSignal sighup(*getIOService(), SIGINT, 1);

    // Write valid_d2_config and then run launch() for a maximum of 1000 ms.
    time_duration elapsed_time;
    runWithConfig(valid_d2_config, 1000, elapsed_time);

    // Signaled shutdown should make our elapsed time much smaller than
Andrei Pavel's avatar
Andrei Pavel committed
276
    // the maximum run time.  Give generous margin to accommodate slow
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
    // test environs.
    EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);

    /// @todo add a way to trap log file and search it
}

// Tests that the SIGTERM triggers a normal shutdown.
TEST_F(D2ControllerTest, sigtermShutdown) {
    // Setup to raise SIGHUP in 1 ms.
    TimedSignal sighup(*getIOService(), SIGTERM, 1);

    // Write valid_d2_config and then run launch() for a maximum of 1 s.
    time_duration elapsed_time;
    runWithConfig(valid_d2_config, 1000, elapsed_time);

    // Signaled shutdown should make our elapsed time much smaller than
Andrei Pavel's avatar
Andrei Pavel committed
293
    // the maximum run time.  Give generous margin to accommodate slow
294 295
    // test environs.
    EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);
296

297
    /// @todo add a way to trap log file and search it
298 299 300 301
}

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