]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[219-allow-an-option-value-to-be-set-from-an-expression] Checkpoint: almost finished
authorFrancis Dupont <fdupont@isc.org>
Tue, 1 Oct 2019 23:22:09 +0000 (01:22 +0200)
committerFrancis Dupont <fdupont@isc.org>
Fri, 25 Oct 2019 08:57:53 +0000 (10:57 +0200)
configure.ac
src/hooks/dhcp/flex_option/Makefile.am
src/hooks/dhcp/flex_option/flex_option.cc
src/hooks/dhcp/flex_option/flex_option.h
src/hooks/dhcp/flex_option/libloadtests/.gitignore [new file with mode: 0644]
src/hooks/dhcp/flex_option/libloadtests/Makefile.am [new file with mode: 0644]
src/hooks/dhcp/flex_option/libloadtests/load_unload_unittests.cc [moved from src/hooks/dhcp/flex_option/tests/load_unload_unittests.cc with 95% similarity]
src/hooks/dhcp/flex_option/libloadtests/run_unittests.cc [new file with mode: 0644]
src/hooks/dhcp/flex_option/tests/Makefile.am
src/hooks/dhcp/flex_option/tests/callout_unittests.cc [deleted file]
src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc [new file with mode: 0644]

index ff3d33f705e4238795eb5da85d6262775ccaada3..b560dd6e8d457d4457c0b2317492b530b3075903 100755 (executable)
@@ -1695,6 +1695,7 @@ AC_CONFIG_FILES([Makefile
                  src/hooks/Makefile
                  src/hooks/dhcp/Makefile
                  src/hooks/dhcp/flex_option/Makefile
+                 src/hooks/dhcp/flex_option/libloadtests/Makefile
                  src/hooks/dhcp/flex_option/tests/Makefile
                  src/hooks/dhcp/high_availability/Makefile
                  src/hooks/dhcp/high_availability/tests/Makefile
@@ -1702,11 +1703,11 @@ AC_CONFIG_FILES([Makefile
                  src/hooks/dhcp/lease_cmds/tests/Makefile
                  src/hooks/dhcp/mysql_cb/Makefile
                  src/hooks/dhcp/mysql_cb/tests/Makefile
+                 src/hooks/dhcp/stat_cmds/Makefile
+                 src/hooks/dhcp/stat_cmds/tests/Makefile
                  src/hooks/dhcp/user_chk/Makefile
                  src/hooks/dhcp/user_chk/tests/Makefile
                  src/hooks/dhcp/user_chk/tests/test_data_files_config.h
-                 src/hooks/dhcp/stat_cmds/Makefile
-                 src/hooks/dhcp/stat_cmds/tests/Makefile
                  src/lib/Makefile
                  src/lib/asiodns/Makefile
                  src/lib/asiodns/tests/Makefile
index 8917366118ede1eacbad8c55221988de6d157b08..c5c8d45dc01ca3f204dba37da4f5c258dac7cb42 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . libloadtests tests
 
 AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
index d36cf48052f1101bb06d085eeec51aabf8477492..c1f4703da7f619b12d618ec9ab7d6b34221486aa 100644 (file)
@@ -24,7 +24,8 @@ using namespace std;
 namespace isc {
 namespace flex_option {
 
-FlexOptionImpl::OptionConfig::OptionConfig(uint16_t code) : code_(code) {
+FlexOptionImpl::OptionConfig::OptionConfig(uint16_t code)
+    : code_(code), action_(NONE) {
 }
 
 FlexOptionImpl::OptionConfig::~OptionConfig() {
@@ -217,6 +218,11 @@ FlexOptionImpl::parseOptionConfig(ConstElementPtr option) {
                       << remove << "] error: " << ex.what());
         }
     }
+
+    if (opt_cfg->getAction() == NONE) {
+        isc_throw(BadValue, "no action: " << option->str());
+    }
+    option_config_map_[code] = opt_cfg;
 }
 
 } // end of namespace flex_option
index ffb4a62fd09be3ceece2520c7dd2cb67e6cb5071..fb820d312b59dfbf07e16d361c1a901cd3a6d7b3 100644 (file)
@@ -194,6 +194,14 @@ public:
         }
     }
 
+protected:
+    /// @brief Get a mutable reference to the option config map
+    ///
+    /// @return The option config map.
+    OptionConfigMap& getMutableOptionConfigMap() {
+        return (option_config_map_);
+    }
+
 private:
     /// @brief The option config map (code and pointer to option config).
     OptionConfigMap option_config_map_;
diff --git a/src/hooks/dhcp/flex_option/libloadtests/.gitignore b/src/hooks/dhcp/flex_option/libloadtests/.gitignore
new file mode 100644 (file)
index 0000000..35b5e99
--- /dev/null
@@ -0,0 +1 @@
+/html
diff --git a/src/hooks/dhcp/flex_option/libloadtests/Makefile.am b/src/hooks/dhcp/flex_option/libloadtests/Makefile.am
new file mode 100644 (file)
index 0000000..6ce02d0
--- /dev/null
@@ -0,0 +1,54 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/flex_option -I$(top_srcdir)/src/hooks/dhcp/flex_option
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DFLEX_OPTION_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/flex_option/.libs/libdhcp_flex_option.so\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+# Unit test data files need to get installed.
+EXTRA_DIST =
+
+CLEANFILES = *.gcno *.gcda
+
+# TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+LOG_COMPILER = $(LIBTOOL)
+AM_LOG_FLAGS = --mode=execute
+
+TESTS =
+if HAVE_GTEST
+TESTS += flex_option_unittests
+
+flex_option_unittests_SOURCES = run_unittests.cc
+flex_option_unittests_SOURCES += load_unload_unittests.cc
+
+flex_option_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+
+flex_option_unittests_LDFLAGS  = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+
+flex_option_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+
+flex_option_unittests_LDADD  = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+flex_option_unittests_LDADD += $(LOG4CPLUS_LIBS)
+flex_option_unittests_LDADD += $(CRYPTO_LIBS)
+flex_option_unittests_LDADD += $(BOOST_LIBS)
+flex_option_unittests_LDADD += $(GTEST_LDADD)
+endif
+noinst_PROGRAMS = $(TESTS)
similarity index 95%
rename from src/hooks/dhcp/flex_option/tests/load_unload_unittests.cc
rename to src/hooks/dhcp/flex_option/libloadtests/load_unload_unittests.cc
index f0c339dcf15c7d53246c209a1f3051b2dcc53020..d9a386a9bdb36002d45d665ef1853b6f87b121c8 100644 (file)
 
 #include <config.h>
 
-#include <exceptions/exceptions.h>
+#include <flex_option.h>
 #include <hooks/hooks_manager.h>
+
 #include <gtest/gtest.h>
-#include <cc/data.h>
 #include <errno.h>
 
 using namespace std;
 using namespace isc;
-using namespace hooks;
+using namespace isc::hooks;
 using namespace isc::data;
+using namespace isc::dhcp;
 
 namespace {
 
-/// @brief Test fixture for testing loading and unloading the flex-id library
+/// @brief Test fixture for testing loading and unloading the flex-option library
 class LibLoadTest : public ::testing::Test {
 public:
     /// @brief Constructor
diff --git a/src/hooks/dhcp/flex_option/libloadtests/run_unittests.cc b/src/hooks/dhcp/flex_option/libloadtests/run_unittests.cc
new file mode 100644 (file)
index 0000000..5805b42
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright (C) 2019 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 <log/logger_support.h>
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    isc::log::initLogger();
+    int result = RUN_ALL_TESTS();
+
+    return (result);
+}
index 1623fe888d16a6d58192697d0f31e79154288975..dbeedf69ed808fed75799019fd82ba8dd9e4e5ec 100644 (file)
@@ -26,8 +26,7 @@ if HAVE_GTEST
 TESTS += flex_option_unittests
 
 flex_option_unittests_SOURCES = run_unittests.cc
-flex_option_unittests_SOURCES += callout_unittests.cc
-flex_option_unittests_SOURCES += load_unload_unittests.cc
+flex_option_unittests_SOURCES += flex_option_unittests.cc
 
 flex_option_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 
@@ -35,7 +34,8 @@ flex_option_unittests_LDFLAGS  = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS
 
 flex_option_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 
-flex_option_unittests_LDADD  = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
+flex_option_unittests_LDADD  = $(top_builddir)/src/hooks/dhcp/flex_option/libflex_option.la
+flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
 flex_option_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
 flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
 flex_option_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
diff --git a/src/hooks/dhcp/flex_option/tests/callout_unittests.cc b/src/hooks/dhcp/flex_option/tests/callout_unittests.cc
deleted file mode 100644 (file)
index cbb38f7..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2019 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/.
-
-/// @file This file contains tests which verify flexible option callouts.
-
-#include <config.h>
-#include <asiolink/asio_wrapper.h>
-#include <exceptions/exceptions.h>
-#include <dhcp/dhcp4.h>
-#include <dhcp/dhcp6.h>
-#include <dhcp/option.h>
-#include <dhcp/pkt4.h>
-#include <dhcp/pkt6.h>
-#include <dhcpsrv/host.h>
-#include <dhcp/option.h>
-#include <dhcp/option_string.h>
-#include <hooks/callout_manager.h>
-#include <hooks/hooks.h>
-#include <flex_option.h>
-#include <flex_option_log.h>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::dhcp;
-using namespace isc::hooks;
-using namespace isc::flex_option;
-
-extern "C" {
-extern int pkt4_send(CalloutHandle& handle);
-extern int pkt6_send(CalloutHandle& handle);
-}
-
-namespace {
-
-} // end of anonymous namespace
diff --git a/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc b/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc
new file mode 100644 (file)
index 0000000..de41cef
--- /dev/null
@@ -0,0 +1,885 @@
+// Copyright (C) 2019 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/.
+
+/// @file This file contains tests which verify flexible option.
+
+#include <config.h>
+#include <flex_option.h>
+#include <flex_option_log.h>
+#include <dhcp/option_string.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <eval/eval_context.h>
+#include <hooks/callout_manager.h>
+#include <hooks/hooks.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::eval;
+using namespace isc::hooks;
+using namespace isc::flex_option;
+
+extern "C" {
+extern int pkt4_send(CalloutHandle& handle);
+extern int pkt6_send(CalloutHandle& handle);
+}
+
+namespace {
+
+/// @brief Test class derived from FlexOptionImpl
+class TestFlexOptionImpl : public FlexOptionImpl {
+public:
+    using FlexOptionImpl::getMutableOptionConfigMap;
+};
+
+/// @brief The type of shared pointers to TestFlexOptionImpl
+typedef boost::shared_ptr<TestFlexOptionImpl> TestFlexOptionImplPtr;
+
+/// @brief Test fixture for testing the Flex Option library.
+class FlexOptionTest : public ::testing::Test {
+public:
+    /// @brief Constructor.
+    FlexOptionTest() {
+        impl_.reset(new TestFlexOptionImpl());
+        CfgMgr::instance().setFamily(AF_INET);
+    }
+
+    /// @brief Destructor.
+    virtual ~FlexOptionTest() {
+        CfgMgr::instance().setFamily(AF_INET);
+        impl_.reset();
+    }
+
+    /// @brief Flex Option implementation.
+    TestFlexOptionImplPtr impl_;
+};
+
+// Verify that the configuration must exist.
+TEST_F(FlexOptionTest, noConfig) {
+    ElementPtr options;
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the configuration must be a list.
+TEST_F(FlexOptionTest, configNotList) {
+    ElementPtr options = Element::createMap();
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the configuration can be the empty list.
+TEST_F(FlexOptionTest, configEmpty) {
+    ElementPtr options = Element::createList();
+    EXPECT_NO_THROW(impl_->configure(options));
+}
+
+// Verify that an option configuration must exist.
+TEST_F(FlexOptionTest, noOptionConfig) {
+    ElementPtr options = Element::createList();
+    ElementPtr option;
+    options->add(option);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that an option configuration must be a map.
+TEST_F(FlexOptionTest, optionConfigNotMap) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createList();
+    options->add(option);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that an option configuration must have code or name.
+TEST_F(FlexOptionTest, optionConfigNoCodeName) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the v4 option code must be in [1..254].
+TEST_F(FlexOptionTest, optionConfigBadCode4) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr add = Element::create(string("'ab'"));
+    option->set("add", add);
+    ElementPtr code = Element::create(-1);
+    option->set("code", code);
+    EXPECT_THROW(impl_->configure(options), OutOfRange);
+
+    code = Element::create(DHO_PAD);
+    option->set("code", code);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+
+    code = Element::create(DHO_END);
+    option->set("code", code);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+
+    code = Element::create(256);
+    option->set("code", code);
+    EXPECT_THROW(impl_->configure(options), OutOfRange);
+
+    code = Element::create(1);
+    option->set("code", code);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    code = Element::create(254);
+    option->set("code", code);
+    EXPECT_NO_THROW(impl_->configure(options));
+}
+
+// Verify that the v6 option code must be in [1..65535].
+TEST_F(FlexOptionTest, optionConfigBadCode6) {
+    CfgMgr::instance().setFamily(AF_INET6);
+
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr add = Element::create(string("'ab'"));
+    option->set("add", add);
+    ElementPtr code = Element::create(-1);
+    option->set("code", code);
+    EXPECT_THROW(impl_->configure(options), OutOfRange);
+
+    code = Element::create(0);
+    option->set("code", code);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+
+    code = Element::create(65536);
+    option->set("code", code);
+    EXPECT_THROW(impl_->configure(options), OutOfRange);
+
+    code = Element::create(1);
+    option->set("code", code);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    code = Element::create(65535);
+    option->set("code", code);
+    EXPECT_NO_THROW(impl_->configure(options));
+}
+
+// Verify that the name must be a string.
+TEST_F(FlexOptionTest, optionConfigBadName) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr add = Element::create(string("'ab'"));
+    option->set("add", add);
+    ElementPtr name = Element::create(true);
+    option->set("name", name);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the name must not be empty.
+TEST_F(FlexOptionTest, optionConfigEmptyName) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr add = Element::create(string("'ab'"));
+    option->set("add", add);
+    ElementPtr name = Element::create(string());
+    option->set("name",name);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the name must be a known option.
+TEST_F(FlexOptionTest, optionConfigUnknownName) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr add = Element::create(string("'ab'"));
+    option->set("add", add);
+    ElementPtr name = Element::create(string("foobar"));
+    option->set("name",name);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the name can be a standard option.
+TEST_F(FlexOptionTest, optionConfigStandardName) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr add = Element::create(string("'ab'"));
+    option->set("add", add);
+    ElementPtr name = Element::create(string("host-name"));
+    option->set("name", name);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    auto map = impl_->getOptionConfigMap();
+    EXPECT_EQ(1, map.count(DHO_HOST_NAME));
+}
+
+// Verify that the name can be an user defined option.
+TEST_F(FlexOptionTest, optionConfigDefinedName) {
+    OptionDefSpaceContainer defs;
+    OptionDefinitionPtr def(new OptionDefinition("my-option", 222, "string"));
+    defs.addItem(def, DHCP4_OPTION_SPACE);
+    EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr add = Element::create(string("'ab'"));
+    option->set("add", add);
+    ElementPtr name = Element::create(string("my-option"));
+    option->set("name", name);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    auto map = impl_->getOptionConfigMap();
+    EXPECT_EQ(1, map.count(222));
+}
+
+// Last resort is only option 43...
+
+// Verify that the name must match the code.
+TEST_F(FlexOptionTest, optionConfigCodeNameMismatch) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr add = Element::create(string("'ab'"));
+    option->set("add", add);
+    ElementPtr code = Element::create(DHO_HOST_NAME + 1);
+    option->set("code", code);
+    ElementPtr name = Element::create(string("host-name"));
+    option->set("name", name);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that an option can be configured only once.
+TEST_F(FlexOptionTest, optionConfigTwice) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr add = Element::create(string("'ab'"));
+    option->set("add", add);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+
+    options->add(option);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the add value must be a string.
+TEST_F(FlexOptionTest, optionConfigAddNotString) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr add = Element::create(true);
+    option->set("add", add);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the add value must not be empty.
+TEST_F(FlexOptionTest, optionConfigEmptyAdd) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr add = Element::create(string());
+    option->set("add", add);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the add value must parse.
+TEST_F(FlexOptionTest, optionConfigBadAdd) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr add = Element::create(string("if"));
+    option->set("add", add);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that a valid v4 add value is accepted.
+TEST_F(FlexOptionTest, optionConfigAdd4) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr add = Element::create(string("'abc'"));
+    option->set("add", add);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    auto map = impl_->getOptionConfigMap();
+    FlexOptionImpl::OptionConfigPtr opt_cfg;
+    ASSERT_NO_THROW(opt_cfg = map.at(DHO_HOST_NAME));
+    ASSERT_TRUE(opt_cfg);
+    EXPECT_EQ(DHO_HOST_NAME, opt_cfg->getCode());
+    EXPECT_EQ(FlexOptionImpl::ADD, opt_cfg->getAction());
+    EXPECT_EQ("'abc'", opt_cfg->getText());
+
+    ExpressionPtr expr = opt_cfg->getExpr();
+    ASSERT_TRUE(expr);
+    ASSERT_EQ(1, expr->size());
+    Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
+    ValueStack values;
+    EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt4, values));
+    ASSERT_EQ(1, values.size());
+    EXPECT_EQ("abc", values.top());
+}
+
+// Verify that a valid v6 add value is accepted.
+TEST_F(FlexOptionTest, optionConfigAdd6) {
+    CfgMgr::instance().setFamily(AF_INET6);
+
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(D6O_BOOTFILE_URL);
+    option->set("code", code);
+    ElementPtr add = Element::create(string("'abc'"));
+    option->set("add", add);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    auto map = impl_->getOptionConfigMap();
+    FlexOptionImpl::OptionConfigPtr opt_cfg;
+    ASSERT_NO_THROW(opt_cfg = map.at(D6O_BOOTFILE_URL));
+    ASSERT_TRUE(opt_cfg);
+    EXPECT_EQ(D6O_BOOTFILE_URL, opt_cfg->getCode());
+    EXPECT_EQ(FlexOptionImpl::ADD, opt_cfg->getAction());
+    EXPECT_EQ("'abc'", opt_cfg->getText());
+
+    ExpressionPtr expr = opt_cfg->getExpr();
+    ASSERT_TRUE(expr);
+    ASSERT_EQ(1, expr->size());
+    Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, 12345));
+    ValueStack values;
+    EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt6, values));
+    ASSERT_EQ(1, values.size());
+    EXPECT_EQ("abc", values.top());
+}
+
+// Verify that the supersede value must be a string.
+TEST_F(FlexOptionTest, optionConfigSupersedeNotString) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr supersede = Element::create(true);
+    option->set("supersede", supersede);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the supersede value must not be empty.
+TEST_F(FlexOptionTest, optionConfigEmptySupersede) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr supersede = Element::create(string());
+    option->set("supersede", supersede);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the supersede value must parse.
+TEST_F(FlexOptionTest, optionConfigBadSupersede) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr supersede = Element::create(string("if"));
+    option->set("supersede", supersede);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that a valid v4 supersede value is accepted.
+TEST_F(FlexOptionTest, optionConfigSupersede4) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr supersede = Element::create(string("'abc'"));
+    option->set("supersede", supersede);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    auto map = impl_->getOptionConfigMap();
+    FlexOptionImpl::OptionConfigPtr opt_cfg;
+    ASSERT_NO_THROW(opt_cfg = map.at(DHO_HOST_NAME));
+    ASSERT_TRUE(opt_cfg);
+    EXPECT_EQ(DHO_HOST_NAME, opt_cfg->getCode());
+    EXPECT_EQ(FlexOptionImpl::SUPERSEDE, opt_cfg->getAction());
+    EXPECT_EQ("'abc'", opt_cfg->getText());
+
+    ExpressionPtr expr = opt_cfg->getExpr();
+    ASSERT_TRUE(expr);
+    ASSERT_EQ(1, expr->size());
+    Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
+    ValueStack values;
+    EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt4, values));
+    ASSERT_EQ(1, values.size());
+    EXPECT_EQ("abc", values.top());
+}
+
+// Verify that a valid v6 supersede value is accepted.
+TEST_F(FlexOptionTest, optionConfigSupersede6) {
+    CfgMgr::instance().setFamily(AF_INET6);
+
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(D6O_BOOTFILE_URL);
+    option->set("code", code);
+    ElementPtr supersede = Element::create(string("'abc'"));
+    option->set("supersede", supersede);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    auto map = impl_->getOptionConfigMap();
+    FlexOptionImpl::OptionConfigPtr opt_cfg;
+    ASSERT_NO_THROW(opt_cfg = map.at(D6O_BOOTFILE_URL));
+    ASSERT_TRUE(opt_cfg);
+    EXPECT_EQ(D6O_BOOTFILE_URL, opt_cfg->getCode());
+    EXPECT_EQ(FlexOptionImpl::SUPERSEDE, opt_cfg->getAction());
+    EXPECT_EQ("'abc'", opt_cfg->getText());
+
+    ExpressionPtr expr = opt_cfg->getExpr();
+    ASSERT_TRUE(expr);
+    ASSERT_EQ(1, expr->size());
+    Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, 12345));
+    ValueStack values;
+    EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt6, values));
+    ASSERT_EQ(1, values.size());
+    EXPECT_EQ("abc", values.top());
+}
+
+// Verify that the remove value must be a string.
+TEST_F(FlexOptionTest, optionConfigRemoveNotString) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr remove = Element::create(true);
+    option->set("remove", remove);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the remove value must not be empty.
+TEST_F(FlexOptionTest, optionConfigEmptyRemove) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr remove = Element::create(string());
+    option->set("remove", remove);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that the remove value must parse.
+TEST_F(FlexOptionTest, optionConfigBadRemove) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr remove = Element::create(string("'abc'"));
+    option->set("remove", remove);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that a valid v4 remove value is accepted.
+TEST_F(FlexOptionTest, optionConfigRemove4) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+    option->set("remove", remove);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    auto map = impl_->getOptionConfigMap();
+    FlexOptionImpl::OptionConfigPtr opt_cfg;
+    ASSERT_NO_THROW(opt_cfg = map.at(DHO_HOST_NAME));
+    ASSERT_TRUE(opt_cfg);
+    EXPECT_EQ(DHO_HOST_NAME, opt_cfg->getCode());
+    EXPECT_EQ(FlexOptionImpl::REMOVE, opt_cfg->getAction());
+    EXPECT_EQ("'abc' == 'abc'", opt_cfg->getText());
+
+    ExpressionPtr expr = opt_cfg->getExpr();
+    ASSERT_TRUE(expr);
+    ASSERT_EQ(3, expr->size());
+    Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
+    ValueStack values;
+    EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt4, values));
+    ASSERT_EQ(1, values.size());
+    EXPECT_EQ("abc", values.top());
+    EXPECT_NO_THROW(expr->at(1)->evaluate(*pkt4, values));
+    ASSERT_EQ(2, values.size());
+    EXPECT_NO_THROW(expr->at(2)->evaluate(*pkt4, values));
+    ASSERT_EQ(1, values.size());
+    EXPECT_EQ("true", values.top());
+}
+
+// Verify that a valid v6 remove value is accepted.
+TEST_F(FlexOptionTest, optionConfigRemove6) {
+    CfgMgr::instance().setFamily(AF_INET6);
+
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(D6O_BOOTFILE_URL);
+    option->set("code", code);
+    ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+    option->set("remove", remove);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    auto map = impl_->getOptionConfigMap();
+    FlexOptionImpl::OptionConfigPtr opt_cfg;
+    ASSERT_NO_THROW(opt_cfg = map.at(D6O_BOOTFILE_URL));
+    ASSERT_TRUE(opt_cfg);
+    EXPECT_EQ(D6O_BOOTFILE_URL, opt_cfg->getCode());
+    EXPECT_EQ(FlexOptionImpl::REMOVE, opt_cfg->getAction());
+    EXPECT_EQ("'abc' == 'abc'", opt_cfg->getText());
+
+    ExpressionPtr expr = opt_cfg->getExpr();
+    ASSERT_TRUE(expr);
+    ASSERT_EQ(3, expr->size());
+    Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, 12345));
+    ValueStack values;
+    EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt6, values));
+    ASSERT_EQ(1, values.size());
+    EXPECT_EQ("abc", values.top());
+    EXPECT_NO_THROW(expr->at(1)->evaluate(*pkt6, values));
+    ASSERT_EQ(2, values.size());
+    EXPECT_NO_THROW(expr->at(2)->evaluate(*pkt6, values));
+    ASSERT_EQ(1, values.size());
+    EXPECT_EQ("true", values.top());
+}
+
+// Verify that multiple actions are not accepted.
+TEST_F(FlexOptionTest, optionConfigMultipleAction) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+
+    // add and supersede.
+    ElementPtr add = Element::create(string("'abc'"));
+    option->set("add", add);
+    ElementPtr supersede = Element::create(string("'abc'"));
+    option->set("supersede", supersede);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+
+    // supersede and remove.
+    option->remove("add");
+    ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+    option->set("remove", remove);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+
+    // add and remove.
+    option->remove("supersede");
+    option->set("add", add);
+    EXPECT_THROW(impl_->configure(options), BadValue);
+}
+
+// Verify that multiple options are accepted.
+TEST_F(FlexOptionTest, optionConfigList) {
+    ElementPtr options = Element::createList();
+
+    ElementPtr option1 = Element::createMap();
+    options->add(option1);
+    ElementPtr code1 = Element::create(DHO_HOST_NAME);
+    option1->set("code", code1);
+    ElementPtr add1 = Element::create(string("'abc'"));
+    option1->set("add", add1);
+
+    ElementPtr option2 = Element::createMap();
+    options->add(option2);
+    ElementPtr code2 = Element::create(DHO_ROOT_PATH);
+    option2->set("code", code2);
+    ElementPtr supersede2 = Element::create(string("'/'"));
+    option2->set("supersede", supersede2);
+
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    auto map = impl_->getOptionConfigMap();
+    EXPECT_EQ(2, map.size());
+
+    FlexOptionImpl::OptionConfigPtr opt1_cfg;
+    ASSERT_NO_THROW(opt1_cfg = map.at(DHO_HOST_NAME));
+    ASSERT_TRUE(opt1_cfg);
+    EXPECT_EQ(DHO_HOST_NAME, opt1_cfg->getCode());
+    EXPECT_EQ(FlexOptionImpl::ADD, opt1_cfg->getAction());
+    EXPECT_EQ("'abc'", opt1_cfg->getText());
+
+    FlexOptionImpl::OptionConfigPtr opt2_cfg;
+    ASSERT_NO_THROW(opt2_cfg = map.at(DHO_ROOT_PATH));
+    ASSERT_TRUE(opt2_cfg);
+    EXPECT_EQ(DHO_ROOT_PATH, opt2_cfg->getCode());
+    EXPECT_EQ(FlexOptionImpl::SUPERSEDE, opt2_cfg->getAction());
+    EXPECT_EQ("'/'", opt2_cfg->getText());
+}
+
+// Verify that empty option config list does nothing.
+TEST_F(FlexOptionTest, processEmpty) {
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+    Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+    string response_txt = response->toText();
+
+    EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+    EXPECT_EQ(response_txt, response->toText());
+}
+
+// Verify that NONE action really does nothing.
+TEST_F(FlexOptionTest, processNone) {
+    CfgMgr::instance().setFamily(AF_INET6);
+
+    FlexOptionImpl::OptionConfigPtr
+        opt_cfg(new FlexOptionImpl::OptionConfig(D6O_BOOTFILE_URL));
+    EXPECT_EQ(FlexOptionImpl::NONE, opt_cfg->getAction());
+    auto map = impl_->getMutableOptionConfigMap();
+    map[DHO_HOST_NAME] = opt_cfg;
+
+    Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+    Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+    string response_txt = response->toText();
+
+    EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+    EXPECT_EQ(response_txt, response->toText());
+}
+
+// Verify that ADD action adds the specified option.
+TEST_F(FlexOptionTest, processAdd) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr add = Element::create(string("'abc'"));
+    option->set("add", add);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+    Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+    EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+
+    EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+    OptionPtr opt = response->getOption(DHO_HOST_NAME);
+    ASSERT_TRUE(opt);
+    EXPECT_EQ(DHO_HOST_NAME, opt->getType());
+    const OptionBuffer& buffer = opt->getData();
+    ASSERT_EQ(3, buffer.size());
+    EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+}
+
+// Verify that ADD action does not add an already existing option.
+TEST_F(FlexOptionTest, processAddExisting) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(D6O_BOOTFILE_URL);
+    option->set("code", code);
+    ElementPtr add = Element::create(string("'abc'"));
+    option->set("add", add);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+    Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+    OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http"));
+    response->addOption(str);
+
+    EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+    OptionPtr opt = response->getOption(D6O_BOOTFILE_URL);
+    ASSERT_TRUE(opt);
+    EXPECT_EQ(D6O_BOOTFILE_URL, opt->getType());
+    EXPECT_EQ("http", opt->toString());
+}
+
+// Verify that ADD action does not add an empty value.
+TEST_F(FlexOptionTest, processAddEmpty) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr add = Element::create(string("''"));
+    option->set("add", add);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+    Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+    string response_txt = response->toText();
+    EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+
+    EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+    EXPECT_EQ(response_txt, response->toText());
+    EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+}
+
+// Verify that SUPERSEDE action supersedes the specified option.
+TEST_F(FlexOptionTest, processSupersede) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr supersede = Element::create(string("'abc'"));
+    option->set("supersede", supersede);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+    Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+    EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+
+    EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+    OptionPtr opt = response->getOption(DHO_HOST_NAME);
+    ASSERT_TRUE(opt);
+    EXPECT_EQ(DHO_HOST_NAME, opt->getType());
+    const OptionBuffer& buffer = opt->getData();
+    ASSERT_EQ(3, buffer.size());
+    EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+}
+
+// Verify that SUPERSEDE action supersedes an already existing option.
+TEST_F(FlexOptionTest, processSupersedeExisting) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(D6O_BOOTFILE_URL);
+    option->set("code", code);
+    ElementPtr supersede = Element::create(string("'abc'"));
+    option->set("supersede", supersede);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+    Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+    OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http"));
+    response->addOption(str);
+
+    EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+    OptionPtr opt = response->getOption(D6O_BOOTFILE_URL);
+    ASSERT_TRUE(opt);
+    EXPECT_EQ(D6O_BOOTFILE_URL, opt->getType());
+    const OptionBuffer& buffer = opt->getData();
+    ASSERT_EQ(3, buffer.size());
+    EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+}
+
+// Verify that SUPERSEDE action does not supersede an empty value.
+TEST_F(FlexOptionTest, processSupersedeEmpty) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr supersede = Element::create(string("''"));
+    option->set("supersede", supersede);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+    Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+    string response_txt = response->toText();
+    EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+
+    EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+    EXPECT_EQ(response_txt, response->toText());
+    EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+
+    // Empty value does not remove existing values.
+    OptionStringPtr str(new OptionString(Option::V4, DHO_HOST_NAME, "abc"));
+    response->addOption(str);
+
+    EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+    OptionPtr opt = response->getOption(DHO_HOST_NAME);
+    ASSERT_TRUE(opt);
+    EXPECT_EQ(DHO_HOST_NAME, opt->getType());
+    const OptionBuffer& buffer = opt->getData();
+    ASSERT_EQ(3, buffer.size());
+    EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+}
+
+// Verify that REMOVE action removes an already existing option.
+TEST_F(FlexOptionTest, processRemove) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(D6O_BOOTFILE_URL);
+    option->set("code", code);
+    ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+    option->set("remove", remove);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+    Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+    OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http"));
+    response->addOption(str);
+
+    EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+    EXPECT_FALSE(response->getOption(D6O_BOOTFILE_URL));
+}
+
+// Verify that REMOVE action does nothing if the option is not present.
+TEST_F(FlexOptionTest, processRemoveNoOption) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(DHO_HOST_NAME);
+    option->set("code", code);
+    ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+    option->set("remove", remove);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+    Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+    string response_txt = response->toText();
+    EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+
+    EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+    EXPECT_EQ(response_txt, response->toText());
+    EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+}
+
+// Verify that REMOVE action does nothing when the expression evaluates to false.
+TEST_F(FlexOptionTest, processRemoveFalse) {
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    options->add(option);
+    ElementPtr code = Element::create(D6O_BOOTFILE_URL);
+    option->set("code", code);
+    ElementPtr remove = Element::create(string("'abc' == 'xyz'"));
+    option->set("remove", remove);
+    EXPECT_NO_THROW(impl_->configure(options));
+
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+    Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+    OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http"));
+    response->addOption(str);
+    string response_txt = response->toText();
+
+    EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V6, query, response));
+
+    EXPECT_EQ(response_txt, response->toText());
+    EXPECT_TRUE(response->getOption(D6O_BOOTFILE_URL));
+}
+
+} // end of anonymous namespace