summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/tests/option_string_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcp/tests/option_string_unittest.cc')
-rw-r--r--src/lib/dhcp/tests/option_string_unittest.cc241
1 files changed, 241 insertions, 0 deletions
diff --git a/src/lib/dhcp/tests/option_string_unittest.cc b/src/lib/dhcp/tests/option_string_unittest.cc
new file mode 100644
index 0000000..38eca81
--- /dev/null
+++ b/src/lib/dhcp/tests/option_string_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright (C) 2013-2019,2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// 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/.
+
+#include <config.h>
+#include <dhcp/option_string.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+namespace {
+
+/// @brief OptionString test class.
+class OptionStringTest : public ::testing::Test {
+public:
+ /// @brief Constructor.
+ ///
+ /// Initializes the test buffer with some data.
+ OptionStringTest() {
+ std::string test_string("This is a test string");
+ buf_.assign(test_string.begin(), test_string.end());
+ }
+
+ OptionBuffer buf_;
+
+};
+
+// This test verifies that the constructor which creates an option instance
+// from a string value will create it properly.
+TEST_F(OptionStringTest, constructorFromString) {
+ const std::string optv4_value = "some option";
+ OptionString optv4(Option::V4, 123, optv4_value);
+ EXPECT_EQ(Option::V4, optv4.getUniverse());
+ EXPECT_EQ(123, optv4.getType());
+ EXPECT_EQ(optv4_value, optv4.getValue());
+ EXPECT_EQ(Option::OPTION4_HDR_LEN + optv4_value.size(), optv4.len());
+
+ // Do another test with the same constructor to make sure that
+ // different set of parameters would initialize the class members
+ // to different values.
+ const std::string optv6_value = "other option";
+ OptionString optv6(Option::V6, 234, optv6_value);
+ EXPECT_EQ(Option::V6, optv6.getUniverse());
+ EXPECT_EQ(234, optv6.getType());
+ EXPECT_EQ("other option", optv6.getValue());
+ EXPECT_EQ(Option::OPTION6_HDR_LEN + optv6_value.size(), optv6.len());
+
+ // Check that an attempt to use empty string in the constructor
+ // will result in an exception.
+ EXPECT_THROW(OptionString(Option::V6, 123, ""), isc::OutOfRange);
+
+ // Check that an attempt to use string containing only nulls
+ // in the constructor will result in an exception.
+ std::string nulls{"\0\0",2};
+ EXPECT_THROW(OptionString(Option::V6, 123, nulls), isc::OutOfRange);
+}
+
+// This test verifies that the constructor which creates an option instance
+// from a buffer, holding option payload, will create it properly.
+// This function calls unpack() internally thus test test is considered
+// to cover testing of unpack() functionality.
+TEST_F(OptionStringTest, constructorFromBuffer) {
+ // Attempt to create an option using empty buffer should result in
+ // an exception.
+ EXPECT_THROW(
+ OptionString(Option::V4, 234, buf_.begin(), buf_.begin()),
+ isc::dhcp::SkipThisOptionError
+ );
+
+ // NULLs should result in an exception.
+ std::vector<uint8_t>nulls = { 0, 0, 0 };
+ EXPECT_THROW(
+ OptionString(Option::V4, 234, nulls.begin(), nulls.begin()),
+ isc::dhcp::SkipThisOptionError
+ );
+
+ // Declare option as a scoped pointer here so as its scope is
+ // function wide. The initialization (constructor invocation)
+ // is pushed to the ASSERT_NO_THROW macro below, as it may
+ // throw exception if buffer is truncated.
+ boost::scoped_ptr<OptionString> optv4;
+ ASSERT_NO_THROW(
+ optv4.reset(new OptionString(Option::V4, 234, buf_.begin(), buf_.end()));
+ );
+ // Make sure that it has been initialized to non-NULL value.
+ ASSERT_TRUE(optv4);
+
+ // Test the instance of the created option.
+ const std::string optv4_value = "This is a test string";
+ EXPECT_EQ(Option::V4, optv4->getUniverse());
+ EXPECT_EQ(234, optv4->getType());
+ EXPECT_EQ(Option::OPTION4_HDR_LEN + buf_.size(), optv4->len());
+ EXPECT_EQ(optv4_value, optv4->getValue());
+
+ // Do the same test for V6 option.
+ boost::scoped_ptr<OptionString> optv6;
+ ASSERT_NO_THROW(
+ // Let's reduce the size of the buffer by one byte and see if our option
+ // will absorb this little change.
+ optv6.reset(new OptionString(Option::V6, 123, buf_.begin(), buf_.end() - 1));
+ );
+ // Make sure that it has been initialized to non-NULL value.
+ ASSERT_TRUE(optv6);
+
+ // Test the instance of the created option.
+ const std::string optv6_value = "This is a test strin";
+ EXPECT_EQ(Option::V6, optv6->getUniverse());
+ EXPECT_EQ(123, optv6->getType());
+ EXPECT_EQ(Option::OPTION6_HDR_LEN + buf_.size() - 1, optv6->len());
+ EXPECT_EQ(optv6_value, optv6->getValue());
+}
+
+// This test verifies that the current option value can be overridden
+// with new value, using setValue method.
+TEST_F(OptionStringTest, setValue) {
+ // Create an instance of the option and set some initial value.
+ OptionString optv4(Option::V4, 123, "some option");
+ EXPECT_EQ("some option", optv4.getValue());
+ // Replace the value with the new one, and make sure it has
+ // been successful.
+ EXPECT_NO_THROW(optv4.setValue("new option value"));
+ EXPECT_EQ("new option value", optv4.getValue());
+ // Try to set to an empty string. It should throw exception.
+ EXPECT_THROW(optv4.setValue(""), isc::OutOfRange);
+}
+
+// This test verifies that the pack function encodes the option in
+// a on-wire format properly.
+TEST_F(OptionStringTest, pack) {
+ // Create an instance of the option.
+ std::string option_value("sample option value");
+ OptionString optv4(Option::V4, 123, option_value);
+ // Encode the option in on-wire format.
+ OutputBuffer buf(Option::OPTION4_HDR_LEN);
+ EXPECT_NO_THROW(optv4.pack(buf));
+
+ // Sanity check the length of the buffer.
+ ASSERT_EQ(Option::OPTION4_HDR_LEN + option_value.length(),
+ buf.getLength());
+ // Copy the contents of the OutputBuffer to InputBuffer because
+ // the latter has API to read data from it.
+ InputBuffer test_buf(buf.getData(), buf.getLength());
+ // First byte holds option code.
+ EXPECT_EQ(123, test_buf.readUint8());
+ // Second byte holds option length.
+ EXPECT_EQ(option_value.size(), test_buf.readUint8());
+ // Read the option data.
+ std::vector<uint8_t> data;
+ test_buf.readVector(data, test_buf.getLength() - test_buf.getPosition());
+ // And create a string from it.
+ std::string test_string(data.begin(), data.end());
+ // This string should be equal to the string used to create
+ // option's instance.
+ EXPECT_TRUE(option_value == test_string);
+}
+
+// This test checks that the DHCP option holding a single string is
+// correctly returned in the textual format.
+TEST_F(OptionStringTest, toText) {
+ // V4 option
+ std::string option_value("lorem ipsum");
+ OptionString optv4(Option::V4, 122, option_value);
+ EXPECT_EQ("type=122, len=011: \"lorem ipsum\" (string)", optv4.toText());
+
+ // V6 option
+ option_value = "is a filler text";
+ OptionString optv6(Option::V6, 512, option_value);
+ EXPECT_EQ("type=00512, len=00016: \"is a filler text\" (string)", optv6.toText());
+}
+
+// This test checks proper handling of trailing and embedded NULLs in
+// data use to create or option value.
+TEST_F(OptionStringTest, setValueNullsHandling) {
+ OptionString optv4(Option::V4, 123, "123");
+
+ // Only nulls should throw.
+ ASSERT_THROW(optv4.setValue(std::string{"\0\0", 2}), isc::OutOfRange);
+
+ // One trailing null should trim off.
+ ASSERT_NO_THROW(optv4.setValue(std::string{"one\0", 4}));
+ EXPECT_EQ(3, optv4.getValue().length());
+ EXPECT_EQ(optv4.getValue(), std::string("one"));
+
+ // More than one trailing null should trim off.
+ ASSERT_NO_THROW(optv4.setValue(std::string{"three\0\0\0", 8}));
+ EXPECT_EQ(5, optv4.getValue().length());
+ EXPECT_EQ(optv4.getValue(), std::string("three"));
+
+ // Embedded null should be left in place.
+ ASSERT_NO_THROW(optv4.setValue(std::string{"em\0bed", 6}));
+ EXPECT_EQ(6, optv4.getValue().length());
+ EXPECT_EQ(optv4.getValue(), (std::string{"em\0bed", 6}));
+
+ // Leading null should be left in place.
+ ASSERT_NO_THROW(optv4.setValue(std::string{"\0leading", 8}));
+ EXPECT_EQ(8, optv4.getValue().length());
+ EXPECT_EQ(optv4.getValue(), (std::string{"\0leading", 8}));
+}
+
+// This test checks proper handling of trailing and embedded NULLs in
+// data use to create or option value.
+TEST_F(OptionStringTest, unpackNullsHandling) {
+ OptionString optv4(Option::V4, 123, "123");
+
+ // Only nulls should throw.
+ OptionBuffer buffer = { 0, 0 };
+ ASSERT_THROW(optv4.unpack(buffer.begin(), buffer.end()), isc::dhcp::SkipThisOptionError);
+
+ // One trailing null should trim off.
+ buffer = {'o', 'n', 'e', 0 };
+ ASSERT_NO_THROW(optv4.unpack(buffer.begin(), buffer.end()));
+ EXPECT_EQ(3, optv4.getValue().length());
+ EXPECT_EQ(optv4.getValue(), std::string("one"));
+
+ // More than one trailing null should trim off.
+ buffer = { 't', 'h', 'r', 'e', 'e', 0, 0, 0 };
+ ASSERT_NO_THROW(optv4.unpack(buffer.begin(), buffer.end()));
+ EXPECT_EQ(5, optv4.getValue().length());
+ EXPECT_EQ(optv4.getValue(), std::string("three"));
+
+ // Embedded null should be left in place.
+ buffer = { 'e', 'm', 0, 'b', 'e', 'd' };
+ ASSERT_NO_THROW(optv4.unpack(buffer.begin(), buffer.end()));
+ EXPECT_EQ(6, optv4.getValue().length());
+ EXPECT_EQ(optv4.getValue(), (std::string{"em\0bed", 6}));
+
+ // Leading null should be left in place.
+ buffer = { 0, 'l', 'e', 'a', 'd', 'i', 'n', 'g' };
+ ASSERT_NO_THROW(optv4.unpack(buffer.begin(), buffer.end()));
+ EXPECT_EQ(8, optv4.getValue().length());
+ EXPECT_EQ(optv4.getValue(), (std::string{"\0leading", 8}));
+}
+
+} // anonymous namespace