cfg_option_unittest.cc 25.4 KB
Newer Older
1
// Copyright (C) 2014-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

#include <config.h>
8
#include <dhcp/dhcp6.h>
9
#include <dhcp/option.h>
10 11
#include <dhcp/option_int.h>
#include <dhcp/option_space.h>
12
#include <dhcpsrv/cfg_option.h>
13
#include <testutils/test_to_element.h>
14
#include <boost/foreach.hpp>
15
#include <boost/pointer_cast.hpp>
16
#include <gtest/gtest.h>
17
#include <iterator>
18
#include <limits>
19
#include <list>
20
#include <sstream>
21 22 23 24 25 26

using namespace isc;
using namespace isc::dhcp;

namespace {

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 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 80 81 82 83 84 85 86 87 88 89 90 91
/// This class fixture for testing @c CfgOption class, holding option
/// configuration.
class CfgOptionTest : public ::testing::Test {
public:

    /// @brief Generates encapsulated options and adds them to CfgOption
    ///
    /// This method generates the following options:
    /// - 1000-1019 options: uint16 with value 1234, encapsulate "foo"
    ///   - 1-19 options: uint8 with value 1, encapsulate "foo-subs"
    ///     - 1-9 options: uint8 with value 3
    /// - 1020-1039 options: uint16 with value 2345, encapsulate "bar"
    ///   - 100-119 options: uint8 with value 2, encapsulate "bar-subs"
    ///     - 501-509 options: uint8 with value 4
    void generateEncapsulatedOptions(CfgOption& cfg) {
        // Create top-level options encapsulating "foo" option space.
        for (uint16_t code = 1000; code < 1020; ++code) {
            OptionUint16Ptr option = OptionUint16Ptr(new OptionUint16(Option::V6,
                                                                      code, 1234));
            option->setEncapsulatedSpace("foo");
            ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
        }

        // Create top level options encapsulating "bar" option space.
        for (uint16_t code = 1020; code < 1040; ++code) {
            OptionUint16Ptr option = OptionUint16Ptr(new OptionUint16(Option::V6,
                                                                      code, 2345));
            option->setEncapsulatedSpace("bar");
            ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
        }

        // Create sub-options belonging to "foo" option space and encapsulating
        // foo-subs option space.
        for (uint16_t code = 1; code < 20; ++code) {
            OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code,
                                                                   0x01));
            option->setEncapsulatedSpace("foo-subs");
            ASSERT_NO_THROW(cfg.add(option, false, "foo"));
        }

        // Create sub-options belonging to "bar" option space and encapsulating
        // bar-subs option space.
        for (uint16_t code = 100;  code < 119; ++code) {
            OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6,
                                                                   code, 0x02));
            option->setEncapsulatedSpace("bar-subs");
            ASSERT_NO_THROW(cfg.add(option, false, "bar"));
        }

        // Create sub-options belonging to "foo-subs" option space.
        for (uint16_t code = 1; code < 10; ++code) {
            OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code,
                                                                   0x03));
            ASSERT_NO_THROW(cfg.add(option, false, "foo-subs"));
        }

        // Create sub-options belonging to "bar-subs" option space.
        for (uint16_t code = 501;  code < 510; ++code) {
            OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6,
                                                                   code, 0x04));
            ASSERT_NO_THROW(cfg.add(option, false, "bar-subs"));
        }
    }
};

92
// This test verifies the empty predicate.
93
TEST_F(CfgOptionTest, empty) {
94 95 96 97 98 99 100 101 102
    CfgOption cfg1;
    CfgOption cfg2;

    // Initially the option configurations should be empty.
    ASSERT_TRUE(cfg1.empty());
    ASSERT_TRUE(cfg2.empty());

    // Add an option to each configuration
    OptionPtr option(new Option(Option::V6, 1));
103
    ASSERT_NO_THROW(cfg1.add(option, false, DHCP6_OPTION_SPACE));
104 105 106 107 108 109 110 111 112
    ASSERT_NO_THROW(cfg2.add(option, true, "isc"));

    // The first option configuration has an option
    ASSERT_FALSE(cfg1.empty());

    // The second option configuration has a vendor option
    ASSERT_FALSE(cfg2.empty());
}

113
// This test verifies that the option configurations can be compared.
114
TEST_F(CfgOptionTest, equals) {
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
    CfgOption cfg1;
    CfgOption cfg2;

    // Initially the configurations should be equal.
    ASSERT_TRUE(cfg1 == cfg2);
    ASSERT_FALSE(cfg1 != cfg2);

    // Add 9 options to two different option spaces. Each option have different
    // option code and content.
    for (uint16_t code = 1; code < 10; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, code)));
        ASSERT_NO_THROW(cfg1.add(option, false, "isc"));
        ASSERT_NO_THROW(cfg1.add(option, true, "vendor-123"));
    }

    // Configurations should now be different.
    ASSERT_FALSE(cfg1 == cfg2);
    ASSERT_TRUE(cfg1 != cfg2);

    // Add 8 options (excluding the option with code 1) to the same option
    // spaces.
    for (uint16_t code = 2; code < 10; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, code)));
        ASSERT_NO_THROW(cfg2.add(option, false, "isc"));
        ASSERT_NO_THROW(cfg2.add(option, true, "vendor-123"));
    }

    // Configurations should still be unequal.
    ASSERT_FALSE(cfg1 == cfg2);
    ASSERT_TRUE(cfg1 != cfg2);

    // Add missing option to the option space isc.
    ASSERT_NO_THROW(cfg2.add(OptionPtr(new Option(Option::V6, 1,
                                                  OptionBuffer(10, 0x01))),
                             false, "isc"));
    // Configurations should still be unequal because option with code 1
    // is missing in the option space vendor-123.
    ASSERT_FALSE(cfg1 == cfg2);
    ASSERT_TRUE(cfg1 != cfg2);

    // Add missing option.
    ASSERT_NO_THROW(cfg2.add(OptionPtr(new Option(Option::V6, 1,
                                                  OptionBuffer(10, 0x01))),
                             true, "vendor-123"));
    // Configurations should now be equal.
    ASSERT_TRUE(cfg1 == cfg2);
    ASSERT_FALSE(cfg1 != cfg2);

}

165 166
// This test verifies that multiple options can be added to the configuration
// and that they can be retrieved using the option space name.
167
TEST_F(CfgOptionTest, add) {
168 169 170 171 172
    CfgOption cfg;

    // Differentiate options by their codes (100-109)
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
173
        ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
174 175 176 177 178 179 180 181 182 183
    }

    // Add 7 options to another option space. The option codes partially overlap
    // with option codes that we have added to dhcp6 option space.
    for (uint16_t code = 105; code < 112; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        ASSERT_NO_THROW(cfg.add(option, false, "isc"));
    }

    // Get options from the Subnet and check if all 10 are there.
184
    OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
185 186 187 188 189 190 191
    ASSERT_TRUE(options);
    ASSERT_EQ(10, options->size());

    // Validate codes of options added to dhcp6 option space.
    uint16_t expected_code = 100;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
192 193
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
194 195 196 197 198 199 200 201 202 203 204
        ++expected_code;
    }

    options = cfg.getAll("isc");
    ASSERT_TRUE(options);
    ASSERT_EQ(7, options->size());

    // Validate codes of options added to isc option space.
    expected_code = 105;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
205 206
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
207 208 209 210 211 212 213 214 215
        ++expected_code;
    }

    // Try to get options from a non-existing option space.
    options = cfg.getAll("abcd");
    ASSERT_TRUE(options);
    EXPECT_TRUE(options->empty());
}

216
// This test verifies that two option configurations can be merged.
217
TEST_F(CfgOptionTest, merge) {
218 219 220 221 222 223 224
    CfgOption cfg_src;
    CfgOption cfg_dst;

    // Create collection of options in option space dhcp6, with option codes
    // from the range of 100 to 109 and holding one byte of data equal to 0xFF.
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0xFF)));
225
        ASSERT_NO_THROW(cfg_src.add(option, false, DHCP6_OPTION_SPACE));
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
    }

    // Create collection of options in vendor space 123, with option codes
    // from the range of 100 to 109 and holding one byte of data equal to 0xFF.
    for (uint16_t code = 100; code < 110; code += 2) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0xFF)));
        ASSERT_NO_THROW(cfg_src.add(option, false, "vendor-123"));
    }

    // Create destination configuration (configuration that we merge the
    // other configuration to).

    // Create collection of options having even option codes in the range of
    // 100 to 108.
    for (uint16_t code = 100; code < 110; code += 2) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0x01)));
242
        ASSERT_NO_THROW(cfg_dst.add(option, false, DHCP6_OPTION_SPACE));
243 244 245 246 247 248 249 250 251 252 253 254
    }

    // Create collection of options having odd option codes in the range of
    // 101 to 109.
    for (uint16_t code = 101; code < 110; code += 2) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0x01)));
        ASSERT_NO_THROW(cfg_dst.add(option, false, "vendor-123"));
    }

    // Merge source configuration to the destination configuration. The options
    // in the destination should be preserved. The options from the source
    // configuration should be added.
255
    ASSERT_NO_THROW(cfg_src.mergeTo(cfg_dst));
256 257 258

    // Validate the options in the dhcp6 option space in the destination.
    for (uint16_t code = 100; code < 110; ++code) {
259
        OptionDescriptor desc = cfg_dst.get(DHCP6_OPTION_SPACE, code);
260 261
        ASSERT_TRUE(desc.option_);
        ASSERT_EQ(1, desc.option_->getData().size());
262 263 264 265 266 267
        // The options with even option codes should hold one byte of data
        // equal to 0x1. These are the ones that we have initially added to
        // the destination configuration. The other options should hold the
        // values of 0xFF which indicates that they have been merged from the
        // source configuration.
        if ((code % 2) == 0) {
268
            EXPECT_EQ(0x01, desc.option_->getData()[0]);
269
        } else {
270
            EXPECT_EQ(0xFF, desc.option_->getData()[0]);
271 272 273 274 275 276
        }
    }

    // Validate the options in the vendor space.
    for (uint16_t code = 100; code < 110; ++code) {
        OptionDescriptor desc = cfg_dst.get(123, code);
277 278
        ASSERT_TRUE(desc.option_);
        ASSERT_EQ(1, desc.option_->getData().size());
279 280 281 282
        // This time, the options with even option codes should hold a byte
        // of data equal to 0xFF. The other options should hold the byte of
        // data equal to 0x01.
        if ((code % 2) == 0) {
283
            EXPECT_EQ(0xFF, desc.option_->getData()[0]);
284
        } else {
285
            EXPECT_EQ(0x01, desc.option_->getData()[0]);
286 287 288 289
        }
    }
}

290 291
// This test verifies that the options configuration can be copied between
// objects.
292
TEST_F(CfgOptionTest, copy) {
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    CfgOption cfg_src;
    // Add 10 options to the custom option space in the source configuration.
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0x01)));
        ASSERT_NO_THROW(cfg_src.add(option, false, "foo"));
    }

    CfgOption cfg_dst;
    // Add 20 options to the custom option space in destination configuration.
    for (uint16_t code = 100; code < 120; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0xFF)));
        ASSERT_NO_THROW(cfg_dst.add(option, false, "isc"));
    }

    // Copy entire configuration to the destination. This should override any
    // existing data.
309
    ASSERT_NO_THROW(cfg_src.copyTo(cfg_dst));
310 311 312 313

    // Validate options in the destination configuration.
    for (uint16_t code = 100; code < 110; ++code) {
        OptionDescriptor desc = cfg_dst.get("foo", code);
314 315 316
        ASSERT_TRUE(desc.option_);
        ASSERT_EQ(1, desc.option_->getData().size());
        EXPECT_EQ(0x01, desc.option_->getData()[0]);
317 318 319 320 321 322 323 324 325 326 327 328 329
    }

    // Any existing options should be removed.
    OptionContainerPtr container = cfg_dst.getAll("isc");
    ASSERT_TRUE(container);
    EXPECT_TRUE(container->empty());

    // The option space "foo" should contain exactly 10 options.
    container = cfg_dst.getAll("foo");
    ASSERT_TRUE(container);
    EXPECT_EQ(10, container->size());
}

330 331
// This test verifies that encapsulated options are added as sub-options
// to the top level options on request.
332
TEST_F(CfgOptionTest, encapsulate) {
333 334
    CfgOption cfg;

335
    generateEncapsulatedOptions(cfg);
336

337 338 339
    // Append options from "foo" and "bar" space as sub-options and options
    // from "foo-subs" and "bar-subs" as sub-options of "foo" and "bar"
    // options.
340 341
    ASSERT_NO_THROW(cfg.encapsulate());

342
    // Verify that we have 40 top-level options.
343
    OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
344
    ASSERT_EQ(40, options->size());
345

346
    // Iterate over top level options.
347
    for (uint16_t code = 1000; code < 1040; ++code) {
348

349
        OptionUint16Ptr option = boost::dynamic_pointer_cast<
350
            OptionUint16>(cfg.get(DHCP6_OPTION_SPACE, code).option_);
351
        ASSERT_TRUE(option) << "option with code " << code << " not found";
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371

        // First level sub options. There are 19 sub-options for each top
        // level option.
        const OptionCollection& first_level = option->getOptions();
        ASSERT_EQ(19, first_level.size());

        // Iterate over all first level sub-options.
        std::pair<unsigned int, OptionPtr> first_level_opt;
        BOOST_FOREACH(first_level_opt, first_level) {
            // Each option in this test comprises a single one byte field and
            // should cast to OptionUint8 type.
            OptionUint8Ptr first_level_uint8 = boost::dynamic_pointer_cast<
                OptionUint8>(first_level_opt.second);
            ASSERT_TRUE(first_level_uint8);

            const unsigned int value = static_cast<unsigned int>(first_level_uint8->getValue());
            // There are two sets of first level sub-options. Those that include
            // a value of 1 and those that include a value of 2.
            if (first_level_uint8->getType() < 20) {
                EXPECT_EQ(1, value);
372
            } else {
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
                EXPECT_EQ(2, value);
            }

            // Each first level sub-option should include 9 second level
            // sub options.
            const OptionCollection& second_level = first_level_uint8->getOptions();
            ASSERT_EQ(9, second_level.size());

            // Iterate over sub-options and make sure they include the expected
            // values.
            std::pair<unsigned int, OptionPtr> second_level_opt;
            BOOST_FOREACH(second_level_opt, second_level) {
                OptionUint8Ptr second_level_uint8 = boost::dynamic_pointer_cast<
                    OptionUint8>(second_level_opt.second);
                ASSERT_TRUE(second_level_uint8);
                const unsigned value = static_cast<
                    unsigned>(second_level_uint8->getValue());
                // Certain sub-options should have a value of 3, other the values
                // of 4.
                if (second_level_uint8->getType() < 20) {
                    EXPECT_EQ(3, value);
                } else {
                    EXPECT_EQ(4, value);
                }
397
            }
398 399 400 401
        }
    }
}

402 403
// This test verifies that single option can be retrieved from the configuration
// using option code and option space.
404
TEST_F(CfgOptionTest, get) {
405 406 407 408 409
    CfgOption cfg;

    // Add 10 options to a "dhcp6" option space in the subnet.
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
410
        ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
411 412 413 414 415 416 417 418 419
    }

    // Check that we can get each added option descriptor using
    // individually.
    for (uint16_t code = 100; code < 110; ++code) {
        std::ostringstream stream;
        // First, try the invalid option space name.
        OptionDescriptor desc = cfg.get("isc", code);
        // Returned descriptor should contain NULL option ptr.
420
        EXPECT_FALSE(desc.option_);
421
        // Now, try the valid option space.
422
        desc = cfg.get(DHCP6_OPTION_SPACE, code);
423
        // Test that the option code matches the expected code.
424 425
        ASSERT_TRUE(desc.option_);
        EXPECT_EQ(code, desc.option_->getType());
426 427 428 429 430
    }
}

// This test verifies that the same options can be added to the configuration
// under different option space.
431
TEST_F(CfgOptionTest, addNonUniqueOptions) {
432 433 434 435 436 437 438
    CfgOption cfg;

    // Create a set of options with non-unique codes.
    for (int i = 0;  i < 2; ++i) {
        // In the inner loop we create options with unique codes (100-109).
        for (uint16_t code = 100; code < 110; ++code) {
            OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
439
            ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
440 441 442 443
        }
    }

    // Sanity check that all options are there.
444
    OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
445 446 447 448 449 450 451
    ASSERT_EQ(20, options->size());

    // Use container index #1 to get the options by their codes.
    OptionContainerTypeIndex& idx = options->get<1>();
    // Look for the codes 100-109.
    for (uint16_t code = 100; code < 110; ++ code) {
        // For each code we should get two instances of options->
452
        OptionContainerTypeRange range = idx.equal_range(code);
453
        // Distance between iterators indicates how many options
Andrei Pavel's avatar
Andrei Pavel committed
454
        // have been returned for the particular code.
455 456 457 458
        ASSERT_EQ(2, distance(range.first, range.second));
        // Check that returned options actually have the expected option code.
        for (OptionContainerTypeIndex::const_iterator option_desc = range.first;
             option_desc != range.second; ++option_desc) {
459 460
            ASSERT_TRUE(option_desc->option_);
            EXPECT_EQ(code, option_desc->option_->getType());
461 462 463 464 465
        }
    }

    // Let's try to find some non-exiting option.
    const uint16_t non_existing_code = 150;
466
    OptionContainerTypeRange range = idx.equal_range(non_existing_code);
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
    // Empty set is expected.
    EXPECT_EQ(0, distance(range.first, range.second));
}

// This test verifies that the option with the persistency flag can be
// added to the configuration and that options with the persistency flags
// can be retrieved.
TEST(Subnet6Test, addPersistentOption) {
    CfgOption cfg;

    // Add 10 options to the subnet with option codes 100 - 109.
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        // We create 10 options and want some of them to be flagged
        // persistent and some non-persistent. Persistent options are
        // those that server sends to clients regardless if they ask
        // for them or not. We pick 3 out of 10 options and mark them
        // non-persistent and 7 other options persistent.
        // Code values: 102, 105 and 108 are divisible by 3
        // and options with these codes will be flagged non-persistent.
        // Options with other codes will be flagged persistent.
        bool persistent = (code % 3) ? true : false;
489
        ASSERT_NO_THROW(cfg.add(option, persistent, DHCP6_OPTION_SPACE));
490 491 492
    }

    // Get added options from the subnet.
493
    OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
494 495 496 497 498 499

    // options->get<2> returns reference to container index #2. This
    // index is used to access options by the 'persistent' flag.
    OptionContainerPersistIndex& idx = options->get<2>();

    // Get all persistent options->
500 501
    OptionContainerPersistRange range_persistent = idx.equal_range(true);
    // 7 out of 10 options have been flagged persistent.
502 503 504
    ASSERT_EQ(7, distance(range_persistent.first, range_persistent.second));

    // Get all non-persistent options->
505 506
    OptionContainerPersistRange range_non_persistent = idx.equal_range(false);
    // 3 out of 10 options have been flagged not persistent.
507 508 509 510
    ASSERT_EQ(3, distance(range_non_persistent.first, range_non_persistent.second));
}

// This test verifies that the vendor option can be added to the configuration.
511
TEST_F(CfgOptionTest, addVendorOptions) {
512 513 514 515 516 517 518 519
    CfgOption cfg;

    // Differentiate options by their codes (100-109)
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        ASSERT_NO_THROW(cfg.add(option, false, "vendor-12345678"));
    }

520 521 522 523 524
    // Second option space uses corner case value for vendor id = max uint8.
    uint32_t vendor_id = std::numeric_limits<uint32_t>::max();
    std::ostringstream option_space;
    option_space << "vendor-" << vendor_id;

525 526 527 528
    // Add 7 options to another option space. The option codes partially overlap
    // with option codes that we have added to dhcp6 option space.
    for (uint16_t code = 105; code < 112; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
529
        ASSERT_NO_THROW(cfg.add(option, false, option_space.str()));
530 531 532 533 534 535 536 537 538 539 540
    }

    // Get options from the Subnet and check if all 10 are there.
    OptionContainerPtr options = cfg.getAll(12345678);
    ASSERT_TRUE(options);
    ASSERT_EQ(10, options->size());

    // Validate codes of options added to dhcp6 option space.
    uint16_t expected_code = 100;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
541 542
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
543 544 545
        ++expected_code;
    }

546
    options = cfg.getAll(vendor_id);
547 548 549 550 551 552 553
    ASSERT_TRUE(options);
    ASSERT_EQ(7, options->size());

    // Validate codes of options added to isc option space.
    expected_code = 105;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
554 555
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
556 557 558 559 560 561 562 563 564
        ++expected_code;
    }

    // Try to get options from a non-existing option space.
    options = cfg.getAll(1111111);
    ASSERT_TRUE(options);
    EXPECT_TRUE(options->empty());
}

565 566
// This test verifies that option space names for the vendor options are
// correct.
567
TEST_F(CfgOptionTest, getVendorIdsSpaceNames) {
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
    CfgOption cfg;

    // Create 10 options, each goes under a different vendor id.
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        // Generate space name for a unique vendor id.
        std::ostringstream s;
        s << "vendor-" << code;
        ASSERT_NO_THROW(cfg.add(option, false, s.str()));
    }

    // We should now have 10 different vendor ids.
    std::list<std::string> space_names = cfg.getVendorIdsSpaceNames();
    ASSERT_EQ(10, space_names.size());

    // Check that the option space names for those vendor ids are correct.
    for (std::list<std::string>::iterator name = space_names.begin();
         name != space_names.end(); ++name) {
        uint16_t id = static_cast<uint16_t>(std::distance(space_names.begin(),
                                                          name));
        std::ostringstream s;
        s << "vendor-" << (100 + id);
        EXPECT_EQ(s.str(), *name);
    }
}

594 595 596 597 598 599 600 601 602 603 604 605 606
// This test verifies that the unparse function returns what is expected.
TEST_F(CfgOptionTest, unparse) {
    CfgOption cfg;

    // Add some options.
    OptionPtr opt1(new Option(Option::V6, 100, OptionBuffer(4, 0x12)));
    cfg.add(opt1, false, "dns");
    OptionPtr opt2(new Option(Option::V6, 101, OptionBuffer(4, 12)));
    OptionDescriptor desc2(opt2, false, "12, 12, 12, 12");
    cfg.add(desc2, "dns");
    OptionPtr opt3(new Option(Option::V6, D6O_STATUS_CODE, OptionBuffer(2, 0)));
    cfg.add(opt3, false, DHCP6_OPTION_SPACE);
    OptionPtr opt4(new Option(Option::V6, 100, OptionBuffer(4, 0x21)));
607
    cfg.add(opt4, true, "vendor-1234");
608 609 610 611 612 613 614

    // Unparse
    std::string expected = "[\n"
        "{\n"
        "    \"code\": 100,\n"
        "    \"space\": \"dns\",\n"
        "    \"csv-format\": false,\n"
615 616
        "    \"data\": \"12121212\",\n"
        "    \"persistent\": false\n"
617 618 619 620
        "},{\n"
        "    \"code\": 101,\n"
        "    \"space\": \"dns\",\n"
        "    \"csv-format\": true,\n"
621 622
        "    \"data\": \"12, 12, 12, 12\",\n"
        "    \"persistent\": false\n"
623 624 625 626 627
        "},{\n"
        "    \"code\": 13,\n"
        "    \"name\": \"status-code\",\n"
        "    \"space\": \"dhcp6\",\n"
        "    \"csv-format\": false,\n"
628 629
        "    \"data\": \"0000\",\n"
        "    \"persistent\": false\n"
630 631 632 633
        "},{\n"
        "    \"code\": 100,\n"
        "    \"space\": \"vendor-1234\",\n"
        "    \"csv-format\": false,\n"
634 635
        "    \"data\": \"21212121\",\n"
        "    \"persistent\": true\n"
636 637 638
        "}]\n";
    isc::test::runToElementTest<CfgOption>(expected, cfg);
}
639 640

} // end of anonymous namespace