summaryrefslogtreecommitdiffstats
path: root/src/lib/config/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/config/tests')
-rw-r--r--src/lib/config/tests/Makefile.am51
-rw-r--r--src/lib/config/tests/Makefile.in1086
-rw-r--r--src/lib/config/tests/client_connection_unittests.cc183
-rw-r--r--src/lib/config/tests/cmd_http_listener_unittests.cc980
-rw-r--r--src/lib/config/tests/cmd_response_creator_factory_unittests.cc71
-rw-r--r--src/lib/config/tests/cmd_response_creator_unittests.cc438
-rw-r--r--src/lib/config/tests/command_mgr_unittests.cc570
-rw-r--r--src/lib/config/tests/data_def_unittests_config.h.in7
-rw-r--r--src/lib/config/tests/run_unittests.cc18
-rw-r--r--src/lib/config/tests/testdata/Makefile.am59
-rw-r--r--src/lib/config/tests/testdata/Makefile.in536
-rw-r--r--src/lib/config/tests/testdata/data22_1.data9
-rw-r--r--src/lib/config/tests/testdata/data22_10.data11
-rw-r--r--src/lib/config/tests/testdata/data22_2.data8
-rw-r--r--src/lib/config/tests/testdata/data22_3.data8
-rw-r--r--src/lib/config/tests/testdata/data22_4.data8
-rw-r--r--src/lib/config/tests/testdata/data22_5.data8
-rw-r--r--src/lib/config/tests/testdata/data22_6.data10
-rw-r--r--src/lib/config/tests/testdata/data22_7.data10
-rw-r--r--src/lib/config/tests/testdata/data22_8.data10
-rw-r--r--src/lib/config/tests/testdata/data22_9.data11
-rw-r--r--src/lib/config/tests/testdata/data32_1.data3
-rw-r--r--src/lib/config/tests/testdata/data32_2.data3
-rw-r--r--src/lib/config/tests/testdata/data32_3.data3
-rw-r--r--src/lib/config/tests/testdata/data33_1.data7
-rw-r--r--src/lib/config/tests/testdata/data33_2.data7
-rw-r--r--src/lib/config/tests/testdata/data41_1.data12
-rw-r--r--src/lib/config/tests/testdata/data41_2.data16
-rw-r--r--src/lib/config/tests/testdata/spec1.spec6
-rw-r--r--src/lib/config/tests/testdata/spec10.spec13
-rw-r--r--src/lib/config/tests/testdata/spec11.spec13
-rw-r--r--src/lib/config/tests/testdata/spec12.spec13
-rw-r--r--src/lib/config/tests/testdata/spec13.spec13
-rw-r--r--src/lib/config/tests/testdata/spec14.spec13
-rw-r--r--src/lib/config/tests/testdata/spec15.spec13
-rw-r--r--src/lib/config/tests/testdata/spec16.spec7
-rw-r--r--src/lib/config/tests/testdata/spec17.spec17
-rw-r--r--src/lib/config/tests/testdata/spec18.spec12
-rw-r--r--src/lib/config/tests/testdata/spec19.spec13
-rw-r--r--src/lib/config/tests/testdata/spec2.spec83
-rw-r--r--src/lib/config/tests/testdata/spec20.spec18
-rw-r--r--src/lib/config/tests/testdata/spec21.spec7
-rw-r--r--src/lib/config/tests/testdata/spec22.spec114
-rw-r--r--src/lib/config/tests/testdata/spec23.spec18
-rw-r--r--src/lib/config/tests/testdata/spec24.spec18
-rw-r--r--src/lib/config/tests/testdata/spec25.spec7
-rw-r--r--src/lib/config/tests/testdata/spec26.spec6
-rw-r--r--src/lib/config/tests/testdata/spec27.spec121
-rw-r--r--src/lib/config/tests/testdata/spec28.spec7
-rw-r--r--src/lib/config/tests/testdata/spec29.spec35
-rw-r--r--src/lib/config/tests/testdata/spec3.spec13
-rw-r--r--src/lib/config/tests/testdata/spec30.spec45
-rw-r--r--src/lib/config/tests/testdata/spec31.spec63
-rw-r--r--src/lib/config/tests/testdata/spec32.spec72
-rw-r--r--src/lib/config/tests/testdata/spec33.spec50
-rw-r--r--src/lib/config/tests/testdata/spec34.spec14
-rw-r--r--src/lib/config/tests/testdata/spec35.spec15
-rw-r--r--src/lib/config/tests/testdata/spec36.spec17
-rw-r--r--src/lib/config/tests/testdata/spec37.spec7
-rw-r--r--src/lib/config/tests/testdata/spec38.spec17
-rw-r--r--src/lib/config/tests/testdata/spec39.spec21
-rw-r--r--src/lib/config/tests/testdata/spec4.spec12
-rw-r--r--src/lib/config/tests/testdata/spec40.spec21
-rw-r--r--src/lib/config/tests/testdata/spec41.spec35
-rw-r--r--src/lib/config/tests/testdata/spec42.spec17
-rw-r--r--src/lib/config/tests/testdata/spec5.spec12
-rw-r--r--src/lib/config/tests/testdata/spec6.spec12
-rw-r--r--src/lib/config/tests/testdata/spec7.spec5
-rw-r--r--src/lib/config/tests/testdata/spec8.spec3
-rw-r--r--src/lib/config/tests/testdata/spec9.spec13
70 files changed, 5174 insertions, 0 deletions
diff --git a/src/lib/config/tests/Makefile.am b/src/lib/config/tests/Makefile.am
new file mode 100644
index 0000000..b58dada
--- /dev/null
+++ b/src/lib/config/tests/Makefile.am
@@ -0,0 +1,51 @@
+SUBDIRS = testdata .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\"
+AM_CPPFLAGS += -DTEST_CA_DIR=\"$(srcdir)/../../asiolink/testutils/ca\"
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+DISTCLEANFILES = data_def_unittests_config.h
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = client_connection_unittests.cc
+run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES += command_mgr_unittests.cc
+run_unittests_SOURCES += cmd_http_listener_unittests.cc
+run_unittests_SOURCES += cmd_response_creator_unittests.cc
+run_unittests_SOURCES += cmd_response_creator_factory_unittests.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(top_builddir)/src/lib/asiolink/testutils/libasiolinktest.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+run_unittests_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS)
+run_unittests_LDADD += $(BOOST_LIBS) $(GTEST_LDADD)
+
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/config/tests/Makefile.in b/src/lib/config/tests/Makefile.in
new file mode 100644
index 0000000..4abd0cb
--- /dev/null
+++ b/src/lib/config/tests/Makefile.in
@@ -0,0 +1,1086 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+TESTS = $(am__EXEEXT_1)
+@HAVE_GTEST_TRUE@am__append_1 = run_unittests
+noinst_PROGRAMS = $(am__EXEEXT_2)
+subdir = src/lib/config/tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp11.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_sysrepo.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = data_def_unittests_config.h
+CONFIG_CLEAN_VPATH_FILES =
+@HAVE_GTEST_TRUE@am__EXEEXT_1 = run_unittests$(EXEEXT)
+am__EXEEXT_2 = $(am__EXEEXT_1)
+PROGRAMS = $(noinst_PROGRAMS)
+am__run_unittests_SOURCES_DIST = client_connection_unittests.cc \
+ run_unittests.cc command_mgr_unittests.cc \
+ cmd_http_listener_unittests.cc \
+ cmd_response_creator_unittests.cc \
+ cmd_response_creator_factory_unittests.cc
+@HAVE_GTEST_TRUE@am_run_unittests_OBJECTS = run_unittests-client_connection_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-run_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-command_mgr_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-cmd_http_listener_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-cmd_response_creator_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-cmd_response_creator_factory_unittests.$(OBJEXT)
+run_unittests_OBJECTS = $(am_run_unittests_OBJECTS)
+am__DEPENDENCIES_1 =
+@HAVE_GTEST_TRUE@run_unittests_DEPENDENCIES = $(top_builddir)/src/lib/asiolink/testutils/libasiolinktest.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+run_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(AM_CXXFLAGS) $(CXXFLAGS) $(run_unittests_LDFLAGS) $(LDFLAGS) \
+ -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = \
+ ./$(DEPDIR)/run_unittests-client_connection_unittests.Po \
+ ./$(DEPDIR)/run_unittests-cmd_http_listener_unittests.Po \
+ ./$(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Po \
+ ./$(DEPDIR)/run_unittests-cmd_response_creator_unittests.Po \
+ ./$(DEPDIR)/run_unittests-command_mgr_unittests.Po \
+ ./$(DEPDIR)/run_unittests-run_unittests.Po
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+SOURCES = $(run_unittests_SOURCES)
+DIST_SOURCES = $(am__run_unittests_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(srcdir)/data_def_unittests_config.h.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_SYSREPO = @HAVE_SYSREPO@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = testdata .
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \
+ $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) \
+ -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\" \
+ -DTEST_CA_DIR=\"$(srcdir)/../../asiolink/testutils/ca\"
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static
+CLEANFILES = *.gcno *.gcda
+DISTCLEANFILES = data_def_unittests_config.h
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+@HAVE_GTEST_TRUE@run_unittests_SOURCES = \
+@HAVE_GTEST_TRUE@ client_connection_unittests.cc \
+@HAVE_GTEST_TRUE@ run_unittests.cc command_mgr_unittests.cc \
+@HAVE_GTEST_TRUE@ cmd_http_listener_unittests.cc \
+@HAVE_GTEST_TRUE@ cmd_response_creator_unittests.cc \
+@HAVE_GTEST_TRUE@ cmd_response_creator_factory_unittests.cc
+@HAVE_GTEST_TRUE@run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+@HAVE_GTEST_TRUE@run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+@HAVE_GTEST_TRUE@run_unittests_LDADD = $(top_builddir)/src/lib/asiolink/testutils/libasiolinktest.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) \
+@HAVE_GTEST_TRUE@ $(BOOST_LIBS) $(GTEST_LDADD)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/config/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib/config/tests/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+data_def_unittests_config.h: $(top_builddir)/config.status $(srcdir)/data_def_unittests_config.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+run_unittests$(EXEEXT): $(run_unittests_OBJECTS) $(run_unittests_DEPENDENCIES) $(EXTRA_run_unittests_DEPENDENCIES)
+ @rm -f run_unittests$(EXEEXT)
+ $(AM_V_CXXLD)$(run_unittests_LINK) $(run_unittests_OBJECTS) $(run_unittests_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-client_connection_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-cmd_http_listener_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-cmd_response_creator_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-command_mgr_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-run_unittests.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+run_unittests-client_connection_unittests.o: client_connection_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-client_connection_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-client_connection_unittests.Tpo -c -o run_unittests-client_connection_unittests.o `test -f 'client_connection_unittests.cc' || echo '$(srcdir)/'`client_connection_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-client_connection_unittests.Tpo $(DEPDIR)/run_unittests-client_connection_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='client_connection_unittests.cc' object='run_unittests-client_connection_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-client_connection_unittests.o `test -f 'client_connection_unittests.cc' || echo '$(srcdir)/'`client_connection_unittests.cc
+
+run_unittests-client_connection_unittests.obj: client_connection_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-client_connection_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-client_connection_unittests.Tpo -c -o run_unittests-client_connection_unittests.obj `if test -f 'client_connection_unittests.cc'; then $(CYGPATH_W) 'client_connection_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/client_connection_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-client_connection_unittests.Tpo $(DEPDIR)/run_unittests-client_connection_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='client_connection_unittests.cc' object='run_unittests-client_connection_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-client_connection_unittests.obj `if test -f 'client_connection_unittests.cc'; then $(CYGPATH_W) 'client_connection_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/client_connection_unittests.cc'; fi`
+
+run_unittests-run_unittests.o: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+
+run_unittests-run_unittests.obj: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+
+run_unittests-command_mgr_unittests.o: command_mgr_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-command_mgr_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-command_mgr_unittests.Tpo -c -o run_unittests-command_mgr_unittests.o `test -f 'command_mgr_unittests.cc' || echo '$(srcdir)/'`command_mgr_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-command_mgr_unittests.Tpo $(DEPDIR)/run_unittests-command_mgr_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='command_mgr_unittests.cc' object='run_unittests-command_mgr_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-command_mgr_unittests.o `test -f 'command_mgr_unittests.cc' || echo '$(srcdir)/'`command_mgr_unittests.cc
+
+run_unittests-command_mgr_unittests.obj: command_mgr_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-command_mgr_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-command_mgr_unittests.Tpo -c -o run_unittests-command_mgr_unittests.obj `if test -f 'command_mgr_unittests.cc'; then $(CYGPATH_W) 'command_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/command_mgr_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-command_mgr_unittests.Tpo $(DEPDIR)/run_unittests-command_mgr_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='command_mgr_unittests.cc' object='run_unittests-command_mgr_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-command_mgr_unittests.obj `if test -f 'command_mgr_unittests.cc'; then $(CYGPATH_W) 'command_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/command_mgr_unittests.cc'; fi`
+
+run_unittests-cmd_http_listener_unittests.o: cmd_http_listener_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-cmd_http_listener_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-cmd_http_listener_unittests.Tpo -c -o run_unittests-cmd_http_listener_unittests.o `test -f 'cmd_http_listener_unittests.cc' || echo '$(srcdir)/'`cmd_http_listener_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-cmd_http_listener_unittests.Tpo $(DEPDIR)/run_unittests-cmd_http_listener_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cmd_http_listener_unittests.cc' object='run_unittests-cmd_http_listener_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-cmd_http_listener_unittests.o `test -f 'cmd_http_listener_unittests.cc' || echo '$(srcdir)/'`cmd_http_listener_unittests.cc
+
+run_unittests-cmd_http_listener_unittests.obj: cmd_http_listener_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-cmd_http_listener_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-cmd_http_listener_unittests.Tpo -c -o run_unittests-cmd_http_listener_unittests.obj `if test -f 'cmd_http_listener_unittests.cc'; then $(CYGPATH_W) 'cmd_http_listener_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/cmd_http_listener_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-cmd_http_listener_unittests.Tpo $(DEPDIR)/run_unittests-cmd_http_listener_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cmd_http_listener_unittests.cc' object='run_unittests-cmd_http_listener_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-cmd_http_listener_unittests.obj `if test -f 'cmd_http_listener_unittests.cc'; then $(CYGPATH_W) 'cmd_http_listener_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/cmd_http_listener_unittests.cc'; fi`
+
+run_unittests-cmd_response_creator_unittests.o: cmd_response_creator_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-cmd_response_creator_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-cmd_response_creator_unittests.Tpo -c -o run_unittests-cmd_response_creator_unittests.o `test -f 'cmd_response_creator_unittests.cc' || echo '$(srcdir)/'`cmd_response_creator_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-cmd_response_creator_unittests.Tpo $(DEPDIR)/run_unittests-cmd_response_creator_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cmd_response_creator_unittests.cc' object='run_unittests-cmd_response_creator_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-cmd_response_creator_unittests.o `test -f 'cmd_response_creator_unittests.cc' || echo '$(srcdir)/'`cmd_response_creator_unittests.cc
+
+run_unittests-cmd_response_creator_unittests.obj: cmd_response_creator_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-cmd_response_creator_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-cmd_response_creator_unittests.Tpo -c -o run_unittests-cmd_response_creator_unittests.obj `if test -f 'cmd_response_creator_unittests.cc'; then $(CYGPATH_W) 'cmd_response_creator_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/cmd_response_creator_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-cmd_response_creator_unittests.Tpo $(DEPDIR)/run_unittests-cmd_response_creator_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cmd_response_creator_unittests.cc' object='run_unittests-cmd_response_creator_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-cmd_response_creator_unittests.obj `if test -f 'cmd_response_creator_unittests.cc'; then $(CYGPATH_W) 'cmd_response_creator_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/cmd_response_creator_unittests.cc'; fi`
+
+run_unittests-cmd_response_creator_factory_unittests.o: cmd_response_creator_factory_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-cmd_response_creator_factory_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Tpo -c -o run_unittests-cmd_response_creator_factory_unittests.o `test -f 'cmd_response_creator_factory_unittests.cc' || echo '$(srcdir)/'`cmd_response_creator_factory_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Tpo $(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cmd_response_creator_factory_unittests.cc' object='run_unittests-cmd_response_creator_factory_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-cmd_response_creator_factory_unittests.o `test -f 'cmd_response_creator_factory_unittests.cc' || echo '$(srcdir)/'`cmd_response_creator_factory_unittests.cc
+
+run_unittests-cmd_response_creator_factory_unittests.obj: cmd_response_creator_factory_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-cmd_response_creator_factory_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Tpo -c -o run_unittests-cmd_response_creator_factory_unittests.obj `if test -f 'cmd_response_creator_factory_unittests.cc'; then $(CYGPATH_W) 'cmd_response_creator_factory_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/cmd_response_creator_factory_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Tpo $(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cmd_response_creator_factory_unittests.cc' object='run_unittests-cmd_response_creator_factory_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-cmd_response_creator_factory_unittests.obj `if test -f 'cmd_response_creator_factory_unittests.cc'; then $(CYGPATH_W) 'cmd_response_creator_factory_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/cmd_response_creator_factory_unittests.cc'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ fi; \
+ echo "$${col}$$dashes$${std}"; \
+ echo "$${col}$$banner$${std}"; \
+ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+ test -z "$$report" || echo "$${col}$$report$${std}"; \
+ echo "$${col}$$dashes$${std}"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/run_unittests-client_connection_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-cmd_http_listener_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-cmd_response_creator_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-command_mgr_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/run_unittests-client_connection_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-cmd_http_listener_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-cmd_response_creator_factory_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-cmd_response_creator_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-command_mgr_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-TESTS check-am clean clean-generic \
+ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/lib/config/tests/client_connection_unittests.cc b/src/lib/config/tests/client_connection_unittests.cc
new file mode 100644
index 0000000..3ecca82
--- /dev/null
+++ b/src/lib/config/tests/client_connection_unittests.cc
@@ -0,0 +1,183 @@
+// Copyright (C) 2017-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 <testutils/sandbox.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/testutils/test_server_unix_socket.h>
+#include <cc/json_feed.h>
+#include <config/client_connection.h>
+#include <gtest/gtest.h>
+#include <cstdlib>
+#include <sstream>
+#include <string>
+
+using namespace isc::asiolink;
+using namespace isc::config;
+
+namespace {
+
+/// @brief Test timeout in ms.
+const long TEST_TIMEOUT = 10000;
+
+/// Test fixture class for @ref ClientConnection.
+class ClientConnectionTest : public ::testing::Test {
+public:
+ isc::test::Sandbox sandbox;
+
+ /// @brief Constructor.
+ ///
+ /// Removes unix socket descriptor before the test.
+ ClientConnectionTest() :
+ io_service_(),
+ test_socket_(new test::TestServerUnixSocket(io_service_,
+ unixSocketFilePath())) {
+ removeUnixSocketFile();
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Removes unix socket descriptor after the test.
+ virtual ~ClientConnectionTest() {
+ removeUnixSocketFile();
+ }
+
+ /// @brief Returns socket file path.
+ ///
+ /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the
+ /// socket file is created in the location pointed to by this variable.
+ /// Otherwise, it is created in the build directory.
+ ///
+ /// The KEA_SOCKET_TEST_DIR is typically used to overcome the problem of
+ /// a system limit on the unix socket file path (usually 102 or 103 characters).
+ /// When Kea build is located in the nested directories with absolute path
+ /// exceeding this limit, the test system should be configured to set
+ /// the KEA_SOCKET_TEST_DIR environmental variable to point to an alternative
+ /// location, e.g. /tmp, with an absolute path length being within the
+ /// allowed range.
+ std::string unixSocketFilePath() {
+ std::string socket_path;
+ const char* env = getenv("KEA_SOCKET_TEST_DIR");
+ if (env) {
+ socket_path = std::string(env) + "/test-socket";
+ } else {
+ socket_path = sandbox.join("test-socket");
+ }
+ return (socket_path);
+ }
+
+ /// @brief Removes unix socket descriptor.
+ void removeUnixSocketFile() {
+ static_cast<void>(remove(unixSocketFilePath().c_str()));
+ }
+
+ /// @brief IO service used by the tests.
+ IOService io_service_;
+
+ /// @brief Server side unix socket used in these tests.
+ test::TestServerUnixSocketPtr test_socket_;
+};
+
+// Tests successful transaction: connect, send command and receive a
+// response.
+TEST_F(ClientConnectionTest, success) {
+ // Start timer protecting against test timeouts.
+ test_socket_->startTimer(TEST_TIMEOUT);
+
+ // Start the server.
+ test_socket_->bindServerSocket();
+ test_socket_->generateCustomResponse(2048);
+
+ // Create some valid command.
+ std::string command = "{ \"command\": \"list-commands\" }";
+
+ ClientConnection conn(io_service_);
+
+ // This boolean value will indicate when the callback function is invoked
+ // at the end of the transaction (whether it is successful or unsuccessful).
+ bool handler_invoked = false;
+ conn.start(ClientConnection::SocketPath(unixSocketFilePath()),
+ ClientConnection::ControlCommand(command),
+ [&handler_invoked](const boost::system::error_code& ec,
+ const ConstJSONFeedPtr& feed) {
+ // Indicate that the handler has been called to break from the
+ // while loop below.
+ handler_invoked = true;
+ // The ec should contain no error.
+ ASSERT_FALSE(ec);
+ // The JSONFeed should be present and it should contain a valid
+ // response.
+ ASSERT_TRUE(feed);
+ EXPECT_TRUE(feed->feedOk()) << feed->getErrorMessage();
+ });
+ // Run the connection.
+ while (!handler_invoked && !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+}
+
+// This test checks that a timeout is signalled when the communication
+// takes too long.
+TEST_F(ClientConnectionTest, timeout) {
+ // The server will return only partial JSON response (lacking closing
+ // brace). The client will wait for closing brace and eventually the
+ // connection should time out.
+ test_socket_.reset(new test::TestServerUnixSocket(io_service_,
+ unixSocketFilePath(),
+ "{ \"command\": \"foo\""));
+ test_socket_->startTimer(TEST_TIMEOUT);
+
+ // Start the server.
+ test_socket_->bindServerSocket();
+
+ // Command to be sent to the server.
+ std::string command = "{ \"command\": \"list-commands\" }";
+
+ ClientConnection conn(io_service_);
+
+ // This boolean value will be set to true when the callback is invoked.
+ bool handler_invoked = false;
+ conn.start(ClientConnection::SocketPath(unixSocketFilePath()),
+ ClientConnection::ControlCommand(command),
+ [&handler_invoked](const boost::system::error_code& ec,
+ const ConstJSONFeedPtr& /*feed*/) {
+ // Indicate that the callback has been invoked to break the loop
+ // below.
+ handler_invoked = true;
+ ASSERT_TRUE(ec);
+ EXPECT_TRUE(ec.value() == boost::asio::error::timed_out);
+ }, ClientConnection::Timeout(1000));
+
+ while (!handler_invoked && !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+}
+
+// This test checks that an error is returned when the client is unable
+// to connect to the server.
+TEST_F(ClientConnectionTest, connectionError) {
+ // Create the new connection but do not bind the server socket.
+ // The connection should be refused and an error returned.
+ ClientConnection conn(io_service_);
+
+ std::string command = "{ \"command\": \"list-commands\" }";
+
+ bool handler_invoked = false;
+ conn.start(ClientConnection::SocketPath(unixSocketFilePath()),
+ ClientConnection::ControlCommand(command),
+ [&handler_invoked](const boost::system::error_code& ec,
+ const ConstJSONFeedPtr& /*feed*/) {
+ handler_invoked = true;
+ ASSERT_TRUE(ec);
+ });
+
+ while (!handler_invoked && !test_socket_->isStopped()) {
+ io_service_.run_one();
+ }
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/config/tests/cmd_http_listener_unittests.cc b/src/lib/config/tests/cmd_http_listener_unittests.cc
new file mode 100644
index 0000000..854a471
--- /dev/null
+++ b/src/lib/config/tests/cmd_http_listener_unittests.cc
@@ -0,0 +1,980 @@
+// Copyright (C) 2021-2022 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 <asiolink/asio_wrapper.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/testutils/test_tls.h>
+#include <cc/command_interpreter.h>
+#include <config/cmd_http_listener.h>
+#include <config/command_mgr.h>
+#include <http/response.h>
+#include <http/response_parser.h>
+#include <http/tests/test_http_client.h>
+#include <util/multi_threading_mgr.h>
+#include <testutils/gtest_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <thread>
+#include <list>
+#include <sstream>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::asiolink::test;
+using namespace isc::config;
+using namespace isc::data;
+using namespace boost::asio::ip;
+using namespace isc::http;
+using namespace isc::util;
+namespace ph = std::placeholders;
+
+namespace {
+
+/// @brief IP address to which HTTP service is bound.
+const std::string SERVER_ADDRESS = "127.0.0.1";
+
+/// @brief Port number to which HTTP service is bound.
+const unsigned short SERVER_PORT = 18123;
+
+/// @brief Test timeout (ms).
+const long TEST_TIMEOUT = 10000;
+
+/// @brief Test fixture class for @ref CmdHttpListener.
+class CmdHttpListenerTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Starts test timer which detects timeouts, deregisters all commands
+ /// from CommandMgr, and enables multi-threading mode.
+ CmdHttpListenerTest()
+ : listener_(), io_service_(), test_timer_(io_service_),
+ run_io_service_timer_(io_service_), clients_(), num_threads_(),
+ num_clients_(), num_in_progress_(0), num_finished_(0), chunk_size_(0),
+ pause_cnt_(0) {
+ test_timer_.setup(std::bind(&CmdHttpListenerTest::timeoutHandler, this, true),
+ TEST_TIMEOUT, IntervalTimer::ONE_SHOT);
+
+ // Deregisters commands.
+ CommandMgr::instance().deregisterAll();
+
+ // Enable multi-threading.
+ MultiThreadingMgr::instance().setMode(true);
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Removes HTTP clients, unregisters commands, disables MT.
+ virtual ~CmdHttpListenerTest() {
+ // Wipe out the listener.
+ listener_.reset();
+
+ // Destroy all remaining clients.
+ for (auto const& client : clients_) {
+ client->close();
+ }
+
+ // Deregisters commands.
+ config::CommandMgr::instance().deregisterAll();
+
+ // Disable multi-threading.
+ MultiThreadingMgr::instance().setMode(false);
+ }
+
+ /// @brief Constructs a complete HTTP POST given a request body.
+ ///
+ /// @param request_body string containing the desired request body.
+ ///
+ /// @return string containing the constructed POST.
+ std::string buildPostStr(const std::string& request_body) {
+ // Create the command string.
+ std::stringstream ss;
+ ss << "POST /foo/bar HTTP/1.1\r\n"
+ "Content-Type: application/json\r\n"
+ "Content-Length: "
+ << request_body.size() << "\r\n\r\n"
+ << request_body;
+ return (ss.str());
+ }
+
+ /// @brief Initiates a command via a new HTTP client.
+ ///
+ /// This method creates a TestHttpClient instance, adds the
+ /// client to the list of clients, and starts a request based
+ /// on the given command. The client will run on the main
+ /// thread and be driven by the test's IOService instance.
+ ///
+ /// @param request_body JSON String containing the API command
+ /// to be sent.
+ void startRequest(const std::string& request_body = "{ }") {
+ std::string request_str = buildPostStr(request_body);
+
+ // Instantiate the client.
+ TestHttpClientPtr client(new TestHttpClient(io_service_, SERVER_ADDRESS,
+ SERVER_PORT));
+ // Add it to the list of clients.
+ clients_.push_back(client);
+
+ // Start the request. Note, nothing happens until the IOService runs.
+ client->startRequest(request_str);
+ }
+
+ /// @brief Initiates a "thread" command via a new HTTP client.
+ ///
+ /// This method creates a TestHttpClient instance, adds the
+ /// client to the list of clients, and starts a request based
+ /// on the given command. The client will run on the main
+ /// thread and be driven by the test's IOService instance.
+ ///
+ /// The command has a single argument, "client-ptr". The function creates
+ /// the value for this argument from the pointer address of client instance
+ /// it creates. This argument should be echoed back in the response, along
+ /// with the thread-id of the CmdHttpListener thread which handled the
+ /// command. The response body should look this:
+ ///
+ /// ```
+ /// [ { "arguments": { "client-ptr": "xxxxx", "thread-id": "zzzzz" }, "result": 0} ]
+ /// ```
+ void startThreadCommand() {
+ // Create a new client.
+ TestHttpClientPtr client(new TestHttpClient(io_service_, SERVER_ADDRESS,
+ SERVER_PORT));
+
+ // Construct the "thread" command post including the argument,
+ // "client-ptr", whose value is the stringified pointer to the
+ // newly created client.
+ std::stringstream request_body;
+ request_body << "{\"command\": \"thread\", \"arguments\": { \"client-ptr\": \""
+ << client << "\" } }";
+
+ std::string command = buildPostStr(request_body.str());
+
+ // Add it to the list of clients.
+ clients_.push_back(client);
+
+ // Start the request. Note, nothing happens until the IOService runs.
+ ASSERT_NO_THROW_LOG(client->startRequest(command));
+ }
+
+ /// @brief Callback function invoke upon test timeout.
+ ///
+ /// It stops the IO service and reports test timeout.
+ ///
+ /// @param fail_on_timeout Specifies if test failure should be reported.
+ void timeoutHandler(const bool fail_on_timeout) {
+ if (fail_on_timeout) {
+ ADD_FAILURE() << "Timeout occurred while running the test!";
+ }
+ io_service_.stop();
+ }
+
+ /// @brief Runs IO service with optional timeout.
+ ///
+ /// We iterate over calls to asio::io_service.run(), until
+ /// all the clients have completed their requests. We do it this way
+ /// because the test clients stop the io_service when they're
+ /// through with a request.
+ ///
+ /// @param request_limit Desired number of requests the function should wait
+ /// to be processed before returning.
+ void runIOService(size_t request_limit = 0) {
+ if (!request_limit) {
+ request_limit = clients_.size();
+ }
+
+ // Loop until the clients are done, an error occurs, or the time runs out.
+ size_t num_done = 0;
+ while (num_done != request_limit) {
+ // Always call restart() before we call run();
+ io_service_.restart();
+
+ // Run until a client stops the service.
+ io_service_.run();
+
+ // If all the clients are done receiving, the test is done.
+ num_done = 0;
+ for (auto const& client : clients_) {
+ if (client->receiveDone()) {
+ ++num_done;
+ }
+ }
+ }
+ }
+
+ /// @brief Create an HttpResponse from a response string.
+ ///
+ /// @param response_str a string containing the whole HTTP
+ /// response received.
+ ///
+ /// @return An HttpResponse constructed from by parsing the
+ /// response string.
+ HttpResponsePtr parseResponse(const std::string response_str) {
+ HttpResponsePtr hr(new HttpResponse());
+ HttpResponseParser parser(*hr);
+ parser.initModel();
+ parser.postBuffer(&response_str[0], response_str.size());
+ parser.poll();
+ if (!parser.httpParseOk()) {
+ isc_throw(Unexpected, "response_str: '" << response_str
+ << "' failed to parse: " << parser.getErrorMessage());
+ }
+
+ return (hr);
+ }
+
+ /// @brief Handler for the 'foo' command.
+ ///
+ /// The command needs no arguments and returns a response
+ /// with a body containing:
+ ///
+ /// "[ { \"arguments\": [ \"bar\" ], \"result\": 0 } ]")
+ ///
+ /// @param command_name Command name, i.e. 'foo'.
+ /// @param command_arguments Command arguments (empty).
+ ///
+ /// @return Returns response with a single string "bar".
+ ConstElementPtr fooCommandHandler(const std::string& /*command_name*/,
+ const ConstElementPtr& /*command_arguments*/) {
+ ElementPtr arguments = Element::createList();
+ arguments->add(Element::create("bar"));
+ return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
+ }
+
+ /// @brief Handler for the 'thread' command.
+ ///
+ /// @param command_name Command name, i.e. 'thread'.
+ /// @param command_arguments Command arguments should contain
+ /// one string element, "client-ptr", whose value is the stringified
+ /// pointer to the client that issued the command.
+ ///
+ /// @return Returns response with map of arguments containing
+ /// a string value 'thread-id': <thread id>
+ ConstElementPtr synchronizedCommandHandler(const std::string& /*command_name*/,
+ const ConstElementPtr& command_arguments) {
+ // If the number of in progress commands is less than the number
+ // of threads, then wait here until we're notified. Otherwise,
+ // notify everyone and finish. The idea is to force each thread
+ // to handle the same number of requests over the course of the
+ // test, making verification reliable.
+ {
+ std::unique_lock<std::mutex> lck(mutex_);
+ ++num_in_progress_;
+ if (num_in_progress_ == chunk_size_) {
+ num_finished_ = 0;
+ cv_.notify_all();
+ } else {
+ bool ret = cv_.wait_for(lck, std::chrono::seconds(10),
+ [&]() { return (num_in_progress_ == chunk_size_); });
+ if (!ret) {
+ ADD_FAILURE() << "clients failed to start work";
+ }
+ }
+ }
+
+ // Create the map of response arguments.
+ ElementPtr arguments = Element::createMap();
+ // First we echo the client-ptr command argument.
+ ConstElementPtr client_ptr = command_arguments->get("client-ptr");
+ if (!client_ptr) {
+ return (createAnswer(CONTROL_RESULT_ERROR, "missing client-ptr"));
+ }
+
+ arguments->set("client-ptr", client_ptr);
+
+ // Now we add the thread-id.
+ std::stringstream ss;
+ ss << std::this_thread::get_id();
+ arguments->set("thread-id", Element::create(ss.str()));
+
+ {
+ std::unique_lock<std::mutex> lck(mutex_);
+ num_finished_++;
+ if (num_finished_ == chunk_size_) {
+ // We're all done, notify the others and finish.
+ num_in_progress_ = 0;
+ cv_.notify_all();
+ } else {
+ // I'm done but others aren't wait here.
+ bool ret = cv_.wait_for(lck, std::chrono::seconds(10),
+ [&]() { return (num_finished_ == chunk_size_); });
+ if (!ret) {
+ ADD_FAILURE() << "clients failed to finish work";
+ }
+ }
+ }
+
+ EXPECT_THROW(listener_->start(), InvalidOperation);
+ EXPECT_THROW(listener_->pause(), MultiThreadingInvalidOperation);
+ EXPECT_THROW(listener_->resume(), MultiThreadingInvalidOperation);
+ EXPECT_THROW(listener_->stop(), MultiThreadingInvalidOperation);
+
+ // We're done, ship it!
+ return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
+ }
+
+ /// @brief Handler for the 'thread' command.
+ ///
+ /// @param command_name Command name, i.e. 'thread'.
+ /// @param command_arguments Command arguments should contain
+ /// one string element, "client-ptr", whose value is the stringified
+ /// pointer to the client that issued the command.
+ ///
+ /// @return Returns response with map of arguments containing
+ /// a string value 'thread-id': <thread id>
+ ConstElementPtr simpleCommandHandler(const std::string& /*command_name*/,
+ const ConstElementPtr& command_arguments) {
+ // Create the map of response arguments.
+ ElementPtr arguments = Element::createMap();
+ // First we echo the client-ptr command argument.
+ ConstElementPtr client_ptr = command_arguments->get("client-ptr");
+ if (!client_ptr) {
+ return (createAnswer(CONTROL_RESULT_ERROR, "missing client-ptr"));
+ }
+
+ arguments->set("client-ptr", client_ptr);
+
+ // Now we add the thread-id.
+ std::stringstream ss;
+ ss << std::this_thread::get_id();
+ arguments->set("thread-id", Element::create(ss.str()));
+
+ // We're done, ship it!
+ return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
+ }
+
+ /// @brief Submits one or more thread commands to a CmdHttpListener.
+ ///
+ /// This function command will create a CmdHttpListener
+ /// with the given number of threads, initiates the given
+ /// number of clients, each requesting the "thread" command,
+ /// and then iteratively runs the test's IOService until all
+ /// the clients have received their responses or an error occurs.
+ ///
+ /// It requires that the number of clients, when greater than the
+ /// number of threads, be a multiple of the number of threads. The
+ /// thread command handler is structured in such a way as to ensure
+ /// (we hope) that each thread handles the same number of commands.
+ ///
+ /// @param num_threads - the number of threads the CmdHttpListener
+ /// should use. Must be greater than 0.
+ /// @param num_clients - the number of clients that should issue the
+ /// thread command. Each client is used to carry out a single thread
+ /// command request. Must be greater than 0 and a multiple of num_threads
+ /// if it is greater than num_threads.
+ void threadListenAndRespond(size_t num_threads, size_t num_clients) {
+ // First we makes sure the parameter rules apply.
+ ASSERT_TRUE(num_threads);
+ ASSERT_TRUE(num_clients);
+ ASSERT_TRUE((num_clients < num_threads) || (num_clients % num_threads == 0));
+
+ num_threads_ = num_threads;
+ num_clients_ = num_clients;
+ chunk_size_ = num_threads_;
+ if (num_clients_ < chunk_size_) {
+ chunk_size_ = num_clients_;
+ }
+
+ // Register the thread command handler.
+ CommandMgr::instance().registerCommand("thread",
+ std::bind(&CmdHttpListenerTest
+ ::synchronizedCommandHandler,
+ this, ph::_1, ph::_2));
+
+ // Create a listener with prescribed number of threads.
+ ASSERT_NO_THROW_LOG(listener_.reset(new CmdHttpListener(IOAddress(SERVER_ADDRESS),
+ SERVER_PORT, num_threads)));
+ ASSERT_TRUE(listener_);
+
+ // Start it and verify it is running.
+ ASSERT_NO_THROW_LOG(listener_->start());
+ ASSERT_TRUE(listener_->isRunning());
+ EXPECT_EQ(listener_->getThreadCount(), num_threads);
+
+ // Maps the number of clients served by a given thread-id.
+ std::map<std::string, int> clients_per_thread;
+
+ // Initiate the prescribed number of command requests.
+ num_in_progress_ = 0;
+ for (auto i = 0; clients_.size() < num_clients; ++i) {
+ ASSERT_NO_THROW_LOG(startThreadCommand());
+ }
+
+ // Now we run the client-side IOService until all requests are done,
+ // errors occur or the test times out.
+ ASSERT_NO_FATAL_FAILURE(runIOService());
+
+ // Stop the listener and then verify it has stopped.
+ ASSERT_NO_THROW_LOG(listener_->stop());
+ ASSERT_TRUE(listener_->isStopped());
+ EXPECT_EQ(listener_->getThreadCount(), 0);
+
+ // Iterate over the clients, checking their outcomes.
+ size_t total_responses = 0;
+ for (auto const& client : clients_) {
+ // Client should have completed its receive successfully.
+ ASSERT_TRUE(client->receiveDone());
+
+ // Client response should not be empty.
+ HttpResponsePtr hr;
+ std::string response_str = client->getResponse();
+ ASSERT_FALSE(response_str.empty());
+
+ // Parse the response into an HttpResponse.
+ ASSERT_NO_THROW_LOG(hr = parseResponse(client->getResponse()));
+
+ // Now we walk the element tree to get the response data. It should look
+ // this:
+ //
+ // [ {
+ // "arguments": { "client-ptr": "xxxxx",
+ // "thread-id": "zzzzz" },
+ // "result": 0
+ // } ]
+ //
+ // First we turn it into an Element tree.
+ std::string body_str = hr->getBody();
+ ConstElementPtr body;
+ ASSERT_NO_THROW_LOG(body = Element::fromJSON(hr->getBody()));
+
+ // Outermost is a list, since we're emulating agent responses.
+ ASSERT_EQ(body->getType(), Element::list);
+ ASSERT_EQ(body->size(), 1);
+
+ // Answer should be a map containing "arguments" and "results".
+ ConstElementPtr answer = body->get(0);
+ ASSERT_EQ(answer->getType(), Element::map);
+
+ // "result" should be 0.
+ ConstElementPtr result = answer->get("result");
+ ASSERT_TRUE(result);
+ ASSERT_EQ(result->getType(), Element::integer);
+ ASSERT_EQ(result->intValue(), 0);
+
+ // "arguments" is a map containing "client-ptr" and "thread-id".
+ ConstElementPtr arguments = answer->get("arguments");
+ ASSERT_TRUE(arguments);
+ ASSERT_EQ(arguments->getType(), Element::map);
+
+ // "client-ptr" is a string.
+ ConstElementPtr client_ptr = arguments->get("client-ptr");
+ ASSERT_TRUE(client_ptr);
+ ASSERT_EQ(client_ptr->getType(), Element::string);
+
+ // "thread-id" is a string.
+ ConstElementPtr thread_id = arguments->get("thread-id");
+ ASSERT_TRUE(thread_id);
+ ASSERT_EQ(thread_id->getType(), Element::string);
+ std::string thread_id_str = thread_id->stringValue();
+
+ // Make sure the response received was for this client.
+ std::stringstream ss;
+ ss << client;
+ ASSERT_EQ(client_ptr->stringValue(), ss.str());
+
+ // Bump the client count for the given thread-id.
+ auto it = clients_per_thread.find(thread_id_str);
+ if (it != clients_per_thread.end()) {
+ clients_per_thread[thread_id_str] = it->second + 1;
+ } else {
+ clients_per_thread[thread_id_str] = 1;
+ }
+
+ ++total_responses;
+ }
+
+ // We should have responses for all our clients.
+ EXPECT_EQ(total_responses, num_clients);
+
+ // Verify we have the expected number of entries in our map.
+ size_t expected_thread_count = (num_clients < num_threads ?
+ num_clients : num_threads);
+
+ ASSERT_EQ(clients_per_thread.size(), expected_thread_count);
+
+ // Each thread-id ought to have handled the same number of clients.
+ for (auto const& it : clients_per_thread) {
+ EXPECT_EQ(it.second, num_clients / clients_per_thread.size())
+ << "thread-id: " << it.first
+ << ", clients: " << it.second << std::endl;
+ }
+ }
+
+ /// @brief Pauses and resumes a CmdHttpListener while it processes command
+ /// requests.
+ ///
+ /// This function command will create a CmdHttpListener
+ /// with the given number of threads, initiates the given
+ /// number of clients, each requesting the "thread" command,
+ /// and then iteratively runs the test's IOService until all
+ /// the clients have received their responses or an error occurs.
+ /// It will pause and resume the listener at intervals governed
+ /// by the given number of pauses.
+ ///
+ /// @param num_threads - the number of threads the CmdHttpListener
+ /// should use. Must be greater than 0.
+ /// @param num_clients - the number of clients that should issue the
+ /// thread command. Each client is used to carry out a single thread
+ /// command request. Must be greater than 0.
+ /// @param num_pauses Desired number of times the listener should be
+ /// paused during the test. Must be greater than 0.
+ void workPauseAndResume(size_t num_threads, size_t num_clients,
+ size_t num_pauses) {
+ // First we makes sure the parameter rules apply.
+ ASSERT_TRUE(num_threads);
+ ASSERT_TRUE(num_clients);
+ ASSERT_TRUE(num_pauses);
+ num_threads_ = num_threads;
+ num_clients_ = num_clients;
+
+ // Register the thread command handler.
+ CommandMgr::instance().registerCommand("thread",
+ std::bind(&CmdHttpListenerTest
+ ::simpleCommandHandler,
+ this, ph::_1, ph::_2));
+
+ // Create a listener with prescribed number of threads.
+ ASSERT_NO_THROW_LOG(listener_.reset(new CmdHttpListener(IOAddress(SERVER_ADDRESS),
+ SERVER_PORT, num_threads)));
+ ASSERT_TRUE(listener_);
+
+ // Start it and verify it is running.
+ ASSERT_NO_THROW_LOG(listener_->start());
+ ASSERT_TRUE(listener_->isRunning());
+ EXPECT_EQ(listener_->getThreadCount(), num_threads);
+
+ // Initiate the prescribed number of command requests.
+ num_in_progress_ = 0;
+ for (auto i = 0; clients_.size() < num_clients; ++i) {
+ ASSERT_NO_THROW_LOG(startThreadCommand());
+ }
+
+ // Now we run the client-side IOService until all requests are done,
+ // errors occur or the test times out. We'll pause and resume the
+ // number of times given by num_pauses.
+ size_t num_done = 0;
+ size_t total_requests = clients_.size();
+ while (num_done < total_requests) {
+ // Calculate how many more requests to process before we pause again.
+ // We divide the number of outstanding requests by the number of pauses
+ // and stop after we've done at least that many more requests.
+ size_t request_limit = (pause_cnt_ < num_pauses ?
+ (num_done + ((total_requests - num_done) / num_pauses))
+ : total_requests);
+
+ // Run test IOService until we hit the limit.
+ runIOService(request_limit);
+
+ // If we've done all our pauses we should be through.
+ if (pause_cnt_ == num_pauses) {
+ break;
+ }
+
+ // Pause the client.
+ ASSERT_NO_THROW(listener_->pause());
+ ASSERT_TRUE(listener_->isPaused());
+ ++pause_cnt_;
+
+ // Check our progress.
+ num_done = 0;
+ for (auto const& client : clients_) {
+ if (client->receiveDone()) {
+ ++num_done;
+ }
+ }
+
+ // We should completed at least as many as our
+ // target limit.
+ ASSERT_GE(num_done, request_limit);
+
+ // Resume the listener.
+ ASSERT_NO_THROW(listener_->resume());
+ ASSERT_TRUE(listener_->isRunning());
+ }
+
+ // Stop the listener and then verify it has stopped.
+ ASSERT_NO_THROW_LOG(listener_->stop());
+ ASSERT_TRUE(listener_->isStopped());
+ EXPECT_EQ(listener_->getThreadCount(), 0);
+
+ // Iterate over the clients, checking their outcomes.
+ size_t total_responses = 0;
+ for (auto const& client : clients_) {
+ // Client should have completed its receive successfully.
+ ASSERT_TRUE(client->receiveDone());
+
+ // Client response should not be empty.
+ HttpResponsePtr hr;
+ std::string response_str = client->getResponse();
+ ASSERT_FALSE(response_str.empty());
+
+ // Parse the response into an HttpResponse.
+ ASSERT_NO_THROW_LOG(hr = parseResponse(client->getResponse()));
+
+ // Now we walk the element tree to get the response data. It should look
+ // this:
+ //
+ // [ {
+ // "arguments": { "client-ptr": "xxxxx",
+ // "thread-id": "zzzzz" },
+ // "result": 0
+ // } ]
+ //
+ // First we turn it into an Element tree.
+ std::string body_str = hr->getBody();
+ ConstElementPtr body;
+ ASSERT_NO_THROW_LOG(body = Element::fromJSON(hr->getBody()));
+
+ // Outermost is a list, since we're emulating agent responses.
+ ASSERT_EQ(body->getType(), Element::list);
+ ASSERT_EQ(body->size(), 1);
+
+ // Answer should be a map containing "arguments" and "results".
+ ConstElementPtr answer = body->get(0);
+ ASSERT_EQ(answer->getType(), Element::map);
+
+ // "result" should be 0.
+ ConstElementPtr result = answer->get("result");
+ ASSERT_TRUE(result);
+ ASSERT_EQ(result->getType(), Element::integer);
+ ASSERT_EQ(result->intValue(), 0);
+
+ // "arguments" is a map containing "client-ptr" and "thread-id".
+ ConstElementPtr arguments = answer->get("arguments");
+ ASSERT_TRUE(arguments);
+ ASSERT_EQ(arguments->getType(), Element::map);
+
+ // "client-ptr" is a string.
+ ConstElementPtr client_ptr = arguments->get("client-ptr");
+ ASSERT_TRUE(client_ptr);
+ ASSERT_EQ(client_ptr->getType(), Element::string);
+
+ // "thread-id" is a string.
+ ConstElementPtr thread_id = arguments->get("thread-id");
+ ASSERT_TRUE(thread_id);
+ ASSERT_EQ(thread_id->getType(), Element::string);
+ std::string thread_id_str = thread_id->stringValue();
+
+ // Make sure the response received was for this client.
+ std::stringstream ss;
+ ss << client;
+ ASSERT_EQ(client_ptr->stringValue(), ss.str());
+
+ ++total_responses;
+ }
+
+ // We should have responses for all our clients.
+ EXPECT_EQ(total_responses, num_clients);
+
+ // We should have had the expected number of pauses.
+ if (!num_pauses) {
+ ASSERT_EQ(pause_cnt_, 0);
+ } else {
+ // We allow a range on pauses of +-1.
+ ASSERT_TRUE((num_pauses - 1) <= pause_cnt_ &&
+ (pause_cnt_ <= (num_pauses + 1)))
+ << " num_pauses: " << num_pauses
+ << ", pause_cnt_" << pause_cnt_;
+ }
+ }
+
+ /// @brief CmdHttpListener instance under test.
+ CmdHttpListenerPtr listener_;
+
+ /// @brief IO service used in drive the test and test clients.
+ IOService io_service_;
+
+ /// @brief Asynchronous timer service to detect timeouts.
+ IntervalTimer test_timer_;
+
+ /// @brief Asynchronous timer for running IO service for a specified amount
+ /// of time.
+ IntervalTimer run_io_service_timer_;
+
+ /// @brief List of client connections.
+ std::list<TestHttpClientPtr> clients_;
+
+ /// @brief Number of threads the listener should use for the test.
+ size_t num_threads_;
+
+ /// @brief Number of client requests to make during the test.
+ size_t num_clients_;
+
+ /// @brief Number of requests currently in progress.
+ size_t num_in_progress_;
+
+ /// @brief Number of requests that have finished.
+ size_t num_finished_;
+
+ /// @brief Chunk size of requests that need to be processed in parallel.
+ ///
+ /// This can either be the number of threads (if the number of requests is
+ /// greater than the number of threads) or the number of requests (if the
+ /// number of threads is greater than the number of requests).
+ size_t chunk_size_;
+
+ /// @brief Mutex used to lock during thread coordination.
+ std::mutex mutex_;
+
+ /// @brief Condition variable used to coordinate threads.
+ std::condition_variable cv_;
+
+ /// @brief Number of times client has been paused during the test.
+ size_t pause_cnt_;
+};
+
+/// Verifies the construction, starting, stopping, pausing, resuming,
+/// and destruction of CmdHttpListener.
+TEST_F(CmdHttpListenerTest, basics) {
+ // Make sure multi-threading is off.
+ MultiThreadingMgr::instance().setMode(false);
+ IOAddress address(SERVER_ADDRESS);
+ uint16_t port = SERVER_PORT;
+
+ // Make sure we can create one.
+ ASSERT_NO_THROW_LOG(listener_.reset(new CmdHttpListener(address, port)));
+ ASSERT_TRUE(listener_);
+
+ // Verify the getters do what we expect.
+ EXPECT_EQ(listener_->getAddress(), address);
+ EXPECT_EQ(listener_->getPort(), port);
+ EXPECT_EQ(listener_->getThreadPoolSize(), 1);
+ EXPECT_FALSE(listener_->getTlsContext());
+
+ // It should not have an IOService, should not be listening and
+ // should have no threads.
+ ASSERT_FALSE(listener_->getThreadIOService());
+ EXPECT_TRUE(listener_->isStopped());
+ EXPECT_EQ(listener_->getThreadCount(), 0);
+
+ // Verify that we cannot start it when multi-threading is disabled.
+ ASSERT_FALSE(MultiThreadingMgr::instance().getMode());
+ ASSERT_THROW_MSG(listener_->start(), InvalidOperation,
+ "CmdHttpListener cannot be started"
+ " when multi-threading is disabled");
+
+ // It should still not be listening and have no threads.
+ EXPECT_TRUE(listener_->isStopped());
+ EXPECT_EQ(listener_->getThreadCount(), 0);
+
+ // Enable multi-threading.
+ MultiThreadingMgr::instance().setMode(true);
+
+ // Make sure we can start it and it's listening with 1 thread.
+ ASSERT_NO_THROW_LOG(listener_->start());
+ ASSERT_TRUE(listener_->isRunning());
+ EXPECT_EQ(listener_->getThreadCount(), 1);
+ ASSERT_TRUE(listener_->getThreadIOService());
+ EXPECT_FALSE(listener_->getThreadIOService()->stopped());
+
+ // Trying to start it again should fail.
+ ASSERT_THROW_MSG(listener_->start(), InvalidOperation,
+ "CmdHttpListener already started!");
+
+ // Stop it and verify we're no longer listening.
+ ASSERT_NO_THROW_LOG(listener_->stop());
+ ASSERT_TRUE(listener_->isStopped());
+ EXPECT_EQ(listener_->getThreadCount(), 0);
+ ASSERT_FALSE(listener_->getThreadIOService());
+
+ // Make sure we can call stop again without problems.
+ ASSERT_NO_THROW_LOG(listener_->stop());
+
+ // We should be able to restart it.
+ ASSERT_NO_THROW_LOG(listener_->start());
+ ASSERT_TRUE(listener_->isRunning());
+ EXPECT_EQ(listener_->getThreadCount(), 1);
+ ASSERT_TRUE(listener_->getThreadIOService());
+ EXPECT_FALSE(listener_->getThreadIOService()->stopped());
+
+ // Destroying it should also stop it.
+ // If the test timeouts we know it didn't!
+ ASSERT_NO_THROW_LOG(listener_.reset());
+
+ // Verify we can construct with more than one thread.
+ ASSERT_NO_THROW_LOG(listener_.reset(new CmdHttpListener(address, port, 4)));
+ ASSERT_NO_THROW_LOG(listener_->start());
+ EXPECT_EQ(listener_->getAddress(), address);
+ EXPECT_EQ(listener_->getPort(), port);
+ EXPECT_EQ(listener_->getThreadCount(), 4);
+ EXPECT_EQ(listener_->getThreadPoolSize(), 4);
+ ASSERT_TRUE(listener_->isRunning());
+ ASSERT_TRUE(listener_->getThreadIOService());
+ EXPECT_FALSE(listener_->getThreadIOService()->stopped());
+
+ // Verify we can pause it. We should still be listening, threads intact,
+ // IOService stopped, state set to PAUSED.
+ ASSERT_NO_THROW_LOG(listener_->pause());
+ ASSERT_TRUE(listener_->isPaused());
+ EXPECT_EQ(listener_->getThreadCount(), 4);
+ EXPECT_EQ(listener_->getThreadPoolSize(), 4);
+ ASSERT_TRUE(listener_->getThreadIOService());
+ EXPECT_TRUE(listener_->getThreadIOService()->stopped());
+
+ // Verify we can resume it.
+ ASSERT_NO_THROW_LOG(listener_->resume());
+ ASSERT_TRUE(listener_->isRunning());
+ EXPECT_EQ(listener_->getThreadCount(), 4);
+ EXPECT_EQ(listener_->getThreadPoolSize(), 4);
+ ASSERT_TRUE(listener_->getThreadIOService());
+ EXPECT_FALSE(listener_->getThreadIOService()->stopped());
+
+ // Stop it and verify we're no longer listening.
+ ASSERT_NO_THROW_LOG(listener_->stop());
+ ASSERT_TRUE(listener_->isStopped());
+ EXPECT_EQ(listener_->getThreadCount(), 0);
+ EXPECT_EQ(listener_->getThreadPoolSize(), 4);
+ ASSERT_FALSE(listener_->getThreadIOService());
+ EXPECT_TRUE(listener_->isStopped());
+}
+
+// This test verifies that an HTTP connection can be established and used to
+// transmit an HTTP request and receive the response.
+TEST_F(CmdHttpListenerTest, basicListenAndRespond) {
+
+ // Create a listener with 1 thread.
+ ASSERT_NO_THROW_LOG(listener_.reset(new CmdHttpListener(IOAddress(SERVER_ADDRESS),
+ SERVER_PORT)));
+ ASSERT_TRUE(listener_);
+
+ // Start the listener and verify it's listening with 1 thread.
+ ASSERT_NO_THROW_LOG(listener_->start());
+ ASSERT_TRUE(listener_->isRunning());
+ EXPECT_EQ(listener_->getThreadCount(), 1);
+
+ // Now let's send a "foo" command. This should create a client, connect
+ // to our listener, post our request and retrieve our reply.
+ ASSERT_NO_THROW(startRequest("{\"command\": \"foo\"}"));
+ ++num_clients_;
+ ASSERT_EQ(num_clients_, clients_.size());
+ ASSERT_NO_THROW(runIOService());
+ TestHttpClientPtr client = clients_.front();
+ ASSERT_TRUE(client);
+
+ // Parse the response into an HttpResponse.
+ HttpResponsePtr hr;
+ ASSERT_NO_THROW_LOG(hr = parseResponse(client->getResponse()));
+
+ // Without a command handler loaded, we should get an unsupported command response.
+ EXPECT_EQ(hr->getBody(), "[ { \"result\": 2, \"text\": \"'foo' command not supported.\" } ]");
+
+ // Now let's register the foo command handler.
+ CommandMgr::instance().registerCommand("foo",
+ std::bind(&CmdHttpListenerTest::fooCommandHandler,
+ this, ph::_1, ph::_2));
+ // Try posting the foo command again.
+ ASSERT_NO_THROW(startRequest("{\"command\": \"foo\"}"));
+ ++num_clients_;
+ ASSERT_EQ(num_clients_, clients_.size());
+ ASSERT_NO_THROW(runIOService());
+ client = clients_.back();
+ ASSERT_TRUE(client);
+
+ // Parse the response.
+ ASSERT_NO_THROW_LOG(hr = parseResponse(client->getResponse()));
+
+ // We should have a response from our command handler.
+ EXPECT_EQ(hr->getBody(), "[ { \"arguments\": [ \"bar\" ], \"result\": 0 } ]");
+
+ // Make sure the listener is still listening.
+ ASSERT_TRUE(listener_->isRunning());
+ EXPECT_EQ(listener_->getThreadCount(), 1);
+
+ // Stop the listener then verify it has stopped.
+ ASSERT_NO_THROW_LOG(listener_->stop());
+ ASSERT_TRUE(listener_->isStopped());
+ EXPECT_EQ(listener_->getThreadCount(), 0);
+}
+
+// Now we'll run some permutations of the number of listener threads
+// and the number of client requests.
+
+// One thread, one client.
+TEST_F(CmdHttpListenerTest, oneByOne) {
+ size_t num_threads = 1;
+ size_t num_clients = 1;
+ threadListenAndRespond(num_threads, num_clients);
+}
+
+// One thread, four clients.
+TEST_F(CmdHttpListenerTest, oneByFour) {
+ size_t num_threads = 1;
+ size_t num_clients = 4;
+ threadListenAndRespond(num_threads, num_clients);
+}
+
+// Four threads, one clients.
+TEST_F(CmdHttpListenerTest, fourByOne) {
+ size_t num_threads = 4;
+ size_t num_clients = 1;
+ threadListenAndRespond(num_threads, num_clients);
+}
+
+// Four threads, four clients.
+TEST_F(CmdHttpListenerTest, fourByFour) {
+ size_t num_threads = 4;
+ size_t num_clients = 4;
+ threadListenAndRespond(num_threads, num_clients);
+}
+
+// Four threads, eight clients.
+TEST_F(CmdHttpListenerTest, fourByEight) {
+ size_t num_threads = 4;
+ size_t num_clients = 8;
+ threadListenAndRespond(num_threads, num_clients);
+}
+
+// Six threads, eighteen clients.
+TEST_F(CmdHttpListenerTest, sixByEighteen) {
+ size_t num_threads = 6;
+ size_t num_clients = 18;
+ threadListenAndRespond(num_threads, num_clients);
+}
+
+// Pauses and resumes the listener while it is processing
+// requests.
+TEST_F(CmdHttpListenerTest, pauseAndResume) {
+ size_t num_threads = 6;
+ size_t num_clients = 18;
+ size_t num_pauses = 3;
+ workPauseAndResume(num_threads, num_clients, num_pauses);
+}
+
+// Check if a TLS listener can be created.
+TEST_F(CmdHttpListenerTest, tls) {
+ IOAddress address(SERVER_ADDRESS);
+ uint16_t port = SERVER_PORT;
+ TlsContextPtr context;
+ configServer(context);
+
+ // Make sure we can create the listener.
+ ASSERT_NO_THROW_LOG(listener_.reset(new CmdHttpListener(address, port, 1, context)));
+ EXPECT_EQ(listener_->getAddress(), address);
+ EXPECT_EQ(listener_->getPort(), port);
+ EXPECT_EQ(listener_->getThreadPoolSize(), 1);
+ EXPECT_EQ(listener_->getTlsContext(), context);
+ EXPECT_TRUE(listener_->isStopped());
+ EXPECT_EQ(listener_->getThreadCount(), 0);
+
+ // Make sure we can start it and it's listening with 1 thread.
+ ASSERT_NO_THROW_LOG(listener_->start());
+ ASSERT_TRUE(listener_->isRunning());
+ EXPECT_EQ(listener_->getThreadCount(), 1);
+ ASSERT_TRUE(listener_->getThreadIOService());
+ EXPECT_FALSE(listener_->getThreadIOService()->stopped());
+
+ // Stop it.
+ ASSERT_NO_THROW_LOG(listener_->stop());
+ ASSERT_TRUE(listener_->isStopped());
+ EXPECT_EQ(listener_->getThreadCount(), 0);
+ EXPECT_EQ(listener_->getThreadPoolSize(), 1);
+ ASSERT_FALSE(listener_->getThreadIOService());
+ EXPECT_TRUE(listener_->isStopped());
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/config/tests/cmd_response_creator_factory_unittests.cc b/src/lib/config/tests/cmd_response_creator_factory_unittests.cc
new file mode 100644
index 0000000..50f2dd8
--- /dev/null
+++ b/src/lib/config/tests/cmd_response_creator_factory_unittests.cc
@@ -0,0 +1,71 @@
+// Copyright (C) 2021-2022 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 <config/cmd_response_creator_factory.h>
+#include <boost/pointer_cast.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace isc::config;
+
+namespace {
+
+// This test verifies the default factory constructor and
+// the create() method.
+TEST(CmdResponseCreatorFactory, createDefault) {
+ // Create the factory.
+ CmdResponseCreatorFactory factory;
+
+ // Create a response creator.
+ CmdResponseCreatorPtr response1;
+ ASSERT_NO_THROW(response1 = boost::dynamic_pointer_cast<
+ CmdResponseCreator>(factory.create()));
+ ASSERT_TRUE(response1);
+
+ // Agent response emulation should be enabled by default.
+ EXPECT_TRUE(response1->emulateAgentResponse());
+
+ // Authorization configuration should be an empty pointer.
+ EXPECT_FALSE(CmdResponseCreator::http_auth_config_);
+
+ // By default all commands are accepted.
+ EXPECT_TRUE(CmdResponseCreator::command_accept_list_.empty());
+
+ // Invoke create() again.
+ CmdResponseCreatorPtr response2;
+ ASSERT_NO_THROW(response2 = boost::dynamic_pointer_cast<
+ CmdResponseCreator>(factory.create()));
+ ASSERT_TRUE(response2);
+
+ // And it must always return the same object.
+ EXPECT_TRUE(response1 == response2);
+}
+
+// This test verifies that agent response emulation can
+// be turned off.
+TEST(CmdResponseCreatorFactory, createAgentEmulationDisabled) {
+ // Instantiate the factory with agent emulation disabled.
+ CmdResponseCreatorFactory factory(false);
+
+ // Create the response creator.
+ CmdResponseCreatorPtr response;
+ ASSERT_NO_THROW(response = boost::dynamic_pointer_cast<
+ CmdResponseCreator>(factory.create()));
+ ASSERT_TRUE(response);
+
+ // Agent response emulation should be disabled.
+ EXPECT_FALSE(response->emulateAgentResponse());
+
+ // Authorization configuration should be an empty pointer.
+ EXPECT_FALSE(CmdResponseCreator::http_auth_config_);
+
+ // By default all commands are accepted.
+ EXPECT_TRUE(CmdResponseCreator::command_accept_list_.empty());
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/config/tests/cmd_response_creator_unittests.cc b/src/lib/config/tests/cmd_response_creator_unittests.cc
new file mode 100644
index 0000000..7ab27c0
--- /dev/null
+++ b/src/lib/config/tests/cmd_response_creator_unittests.cc
@@ -0,0 +1,438 @@
+// Copyright (C) 2021-2022 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 <cc/command_interpreter.h>
+#include <config/command_mgr.h>
+#include <config/cmd_response_creator.h>
+#include <http/post_request.h>
+#include <http/post_request_json.h>
+#include <http/response_json.h>
+
+#include <gtest/gtest.h>
+#include <boost/pointer_cast.hpp>
+#include <functional>
+
+using namespace isc;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::http;
+using namespace std;
+namespace ph = std::placeholders;
+
+namespace {
+
+/// @brief Test fixture class for @ref CmdResponseCreator.
+class CmdResponseCreatorTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Creates instance of the response creator and uses this instance to
+ /// create "empty" request. It also removes registered commands from the
+ /// command manager.
+ CmdResponseCreatorTest() {
+ // Deregisters commands.
+ config::CommandMgr::instance().deregisterAll();
+ // Register our "foo" command.
+ config::CommandMgr::instance().
+ registerCommand("foo", std::bind(&CmdResponseCreatorTest::fooCommandHandler,
+ this, ph::_1, ph::_2));
+ // Clear class variables.
+ CmdResponseCreator::http_auth_config_.reset();
+ CmdResponseCreator::command_accept_list_.clear();
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Removes registered commands from the command manager.
+ virtual ~CmdResponseCreatorTest() {
+ config::CommandMgr::instance().deregisterAll();
+ CmdResponseCreator::http_auth_config_.reset();
+ CmdResponseCreator::command_accept_list_.clear();
+ }
+
+ /// @brief SetUp function that wraps call to initCreator.
+ ///
+ /// Creates a default CmdResponseCreator and new HttpRequest.
+ virtual void SetUp() {
+ initCreator();
+ }
+
+ /// @brief Creates a new CmdResponseCreator and new HttpRequest.
+ ///
+ /// @param emulate_agent_flag enables/disables agent response emulation
+ /// in the CmdResponsCreator.
+ void initCreator(bool emulate_agent_flag = true) {
+ response_creator_.reset(new CmdResponseCreator(emulate_agent_flag));
+ request_ = response_creator_->createNewHttpRequest();
+ ASSERT_TRUE(request_) << "initCreator failed to create request";
+ }
+
+ /// @brief Fills request context with required data to create new request.
+ ///
+ /// @param request Request which context should be configured.
+ void setBasicContext(const HttpRequestPtr& request) {
+ request->context()->method_ = "POST";
+ request->context()->http_version_major_ = 1;
+ request->context()->http_version_minor_ = 1;
+ request->context()->uri_ = "/foo";
+
+ // Content-Type
+ HttpHeaderContext content_type;
+ content_type.name_ = "Content-Type";
+ content_type.value_ = "application/json";
+ request->context()->headers_.push_back(content_type);
+
+ // Content-Length
+ HttpHeaderContext content_length;
+ content_length.name_ = "Content-Length";
+ content_length.value_ = "0";
+ request->context()->headers_.push_back(content_length);
+ }
+
+ /// @brief Test creation of stock response.
+ ///
+ /// @param status_code Status code to be included in the response.
+ /// @param must_contain Text that must be present in the textual
+ /// representation of the generated response.
+ void testStockResponse(const HttpStatusCode& status_code,
+ const string& must_contain) {
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(
+ response = response_creator_->createStockHttpResponse(request_,
+ status_code)
+ );
+ ASSERT_TRUE(response);
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+ // Make sure the response contains the string specified as argument.
+ EXPECT_TRUE(response_json->toString().find(must_contain) != string::npos);
+
+ }
+
+ /// @brief Handler for the 'foo' test command.
+ ///
+ /// @param command_name Command name, i.e. 'foo'.
+ /// @param command_arguments Command arguments (empty).
+ ///
+ /// @return Returns response with a single string "bar".
+ ConstElementPtr fooCommandHandler(const string& /*command_name*/,
+ const ConstElementPtr& /*command_arguments*/) {
+ ElementPtr arguments = Element::createList();
+ arguments->add(Element::create("bar"));
+ return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
+ }
+
+ /// @brief Instance of the response creator.
+ CmdResponseCreatorPtr response_creator_;
+
+ /// @brief Instance of the "empty" request.
+ ///
+ /// The context belonging to this request may be modified by the unit
+ /// tests to verify various scenarios of response creation.
+ HttpRequestPtr request_;
+};
+
+// This test verifies that the created "empty" request has valid type.
+TEST_F(CmdResponseCreatorTest, createNewHttpRequest) {
+ // The request must be of PostHttpRequestJson type.
+ PostHttpRequestJsonPtr request_json = boost::dynamic_pointer_cast<
+ PostHttpRequestJson>(request_);
+ ASSERT_TRUE(request_json);
+}
+
+// Test that HTTP version of stock response is set to 1.0 if the request
+// context doesn't specify any version.
+TEST_F(CmdResponseCreatorTest, createStockHttpResponseNoVersion) {
+ testStockResponse(HttpStatusCode::BAD_REQUEST, "HTTP/1.0 400 Bad Request");
+}
+
+// Test that HTTP version of stock response is set to 1.0 if the request
+// version is higher than 1.1.
+TEST_F(CmdResponseCreatorTest, createStockHttpResponseHighVersion) {
+ request_->context()->http_version_major_ = 2;
+ testStockResponse(HttpStatusCode::REQUEST_TIMEOUT,
+ "HTTP/1.0 408 Request Timeout");
+}
+
+// Test that the server responds with version 1.1 if request version is 1.1.
+TEST_F(CmdResponseCreatorTest, createStockHttpResponseCorrectVersion) {
+ request_->context()->http_version_major_ = 1;
+ request_->context()->http_version_minor_ = 1;
+ testStockResponse(HttpStatusCode::NO_CONTENT, "HTTP/1.1 204 No Content");
+}
+
+// Test successful server response when the client specifies valid command.
+TEST_F(CmdResponseCreatorTest, createDynamicHttpResponse) {
+ setBasicContext(request_);
+
+ // Body: "foo" command has been registered in the test fixture constructor.
+ request_->context()->body_ = "{ \"command\": \"foo\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Create response from the request.
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_->createHttpResponse(request_));
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response should be in a list by default.
+ ASSERT_TRUE(response_creator_->emulateAgentResponse());
+ ASSERT_TRUE(response_json->getBodyAsJson()->getType() == Element::list)
+ << "response is not a list: " << response_json->toString();
+
+ // Response must be successful.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 200 OK") !=
+ string::npos);
+
+ // Response must contain JSON body with "result" of 0.
+ EXPECT_TRUE(response_json->toString().find("\"result\": 0") !=
+ string::npos);
+}
+
+// Test successful server response without emulating agent response.
+TEST_F(CmdResponseCreatorTest, createDynamicHttpResponseNoEmulation) {
+ // Recreate the response creator setting emulate_agent_response to false;
+ initCreator(false);
+ setBasicContext(request_);
+
+ // Body: "foo" command has been registered in the test fixture constructor.
+ request_->context()->body_ = "{ \"command\": \"foo\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Create response from the request.
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_->createHttpResponse(request_));
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response should be a map that is not enclosed in a list.
+ ASSERT_FALSE(response_creator_->emulateAgentResponse());
+ ASSERT_TRUE(response_json->getBodyAsJson()->getType() == Element::map)
+ << "response is not a map: " << response_json->toString();
+
+ // Response must be successful.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 200 OK") !=
+ string::npos);
+
+ // Response must contain JSON body with "result" of 0.
+ EXPECT_TRUE(response_json->toString().find("\"result\": 0") !=
+ string::npos);
+}
+
+// This test verifies that Internal Server Error is returned when invalid C++
+// request type is used. This is considered an error in the server logic.
+TEST_F(CmdResponseCreatorTest, createDynamicHttpResponseInvalidType) {
+ PostHttpRequestPtr request(new PostHttpRequest());
+ setBasicContext(request);
+
+ // Body: "list-commands" is natively supported by the command manager.
+ request->context()->body_ = "{ \"command\": \"list-commands\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request->finalize());
+
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_->createHttpResponse(request));
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must contain Internal Server Error status code.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 500 Internal Server Error") !=
+ string::npos);
+}
+
+// This test verifies command filtering.
+TEST_F(CmdResponseCreatorTest, filterCommand) {
+ initCreator(false);
+ setBasicContext(request_);
+ // For the log message...
+ request_->setRemote("127.0.0.1");
+
+ HttpResponseJsonPtr response;
+ ConstElementPtr body;
+ unordered_set<string> accept;
+ ASSERT_NO_THROW(response = response_creator_->filterCommand(request_, body, accept));
+ EXPECT_FALSE(response);
+
+ accept.insert("foo");
+ ASSERT_NO_THROW(response = response_creator_->filterCommand(request_, body, accept));
+ EXPECT_FALSE(response);
+
+ body = Element::createList();
+ ASSERT_NO_THROW(response = response_creator_->filterCommand(request_, body, accept));
+ EXPECT_FALSE(response);
+
+ body = Element::createMap();
+ ASSERT_NO_THROW(response = response_creator_->filterCommand(request_, body, accept));
+ EXPECT_FALSE(response);
+
+ body = createCommand("foo", ConstElementPtr());
+ ASSERT_NO_THROW(response = response_creator_->filterCommand(request_, body, accept));
+ EXPECT_FALSE(response);
+
+ body = createCommand("bar", ConstElementPtr());
+ ASSERT_NO_THROW(response = response_creator_->filterCommand(request_, body, accept));
+ EXPECT_TRUE(response);
+ EXPECT_EQ("HTTP/1.1 403 Forbidden", response->toBriefString());
+
+ accept.clear();
+ ASSERT_NO_THROW(response = response_creator_->filterCommand(request_, body, accept));
+ EXPECT_FALSE(response);
+}
+
+// This test verifies basic HTTP authentication - reject case.
+// Empty case was handled in createDynamicHttpResponseNoEmulation.
+TEST_F(CmdResponseCreatorTest, basicAuthReject) {
+ initCreator(false);
+ setBasicContext(request_);
+
+ // Body: "foo" command has been registered in the test fixture constructor.
+ request_->context()->body_ = "{ \"command\": \"foo\" }";
+
+ // Add no basic HTTP authentication.
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Create basic HTTP authentication configuration.
+ CmdResponseCreator::http_auth_config_.reset(new BasicHttpAuthConfig());
+ BasicHttpAuthConfigPtr basic =
+ boost::dynamic_pointer_cast<BasicHttpAuthConfig>(
+ CmdResponseCreator::http_auth_config_);
+ ASSERT_TRUE(basic);
+ EXPECT_NO_THROW(basic->add("test", "", "123\xa3", ""));
+
+ // Create response from the request.
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_->createHttpResponse(request_));
+ ASSERT_TRUE(response);
+
+ // Response must not be successful.
+ EXPECT_EQ("HTTP/1.1 401 Unauthorized", response->toBriefString());
+}
+
+// This test verifies basic HTTP authentication - accept case.
+// Empty case was handled in createDynamicHttpResponseNoEmulation.
+TEST_F(CmdResponseCreatorTest, basicAuthAccept) {
+ initCreator(false);
+ setBasicContext(request_);
+
+ // Body: "foo" command has been registered in the test fixture constructor.
+ request_->context()->body_ = "{ \"command\": \"foo\" }";
+
+ // Add basic HTTP authentication.
+ HttpHeaderContext auth("Authorization", "Basic dGVzdDoxMjPCow==");
+ request_->context()->headers_.push_back(auth);
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Create basic HTTP authentication configuration.
+ CmdResponseCreator::http_auth_config_.reset(new BasicHttpAuthConfig());
+ BasicHttpAuthConfigPtr basic =
+ boost::dynamic_pointer_cast<BasicHttpAuthConfig>(
+ CmdResponseCreator::http_auth_config_);
+ ASSERT_TRUE(basic);
+ EXPECT_NO_THROW(basic->add("test", "", "123\xa3", ""));
+
+ // Create response from the request.
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_->createHttpResponse(request_));
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must be successful.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 200 OK") !=
+ string::npos);
+
+ // Response must contain JSON body with "result" of 0.
+ EXPECT_TRUE(response_json->toString().find("\"result\": 0") !=
+ string::npos);
+}
+
+// This test verifies command filtering at the HTTP level - reject case.
+TEST_F(CmdResponseCreatorTest, filterCommandReject) {
+ initCreator(false);
+ setBasicContext(request_);
+ // For the log message...
+ request_->setRemote("127.0.0.1");
+
+ // Body: "bar" command has been registered in the test fixture constructor.
+ request_->context()->body_ = "{ \"command\": \"bar\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Add foo in the access list.
+ CmdResponseCreator::command_accept_list_.insert("foo");
+
+ // Create response from the request.
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_->createHttpResponse(request_));
+ ASSERT_TRUE(response);
+
+ // Response must not be successful.
+ EXPECT_EQ("HTTP/1.1 403 Forbidden", response->toBriefString());
+}
+
+// This test verifies command filtering at the HTTP level - accept case.
+TEST_F(CmdResponseCreatorTest, filterCommandAccept) {
+ initCreator(false);
+ setBasicContext(request_);
+
+ // Body: "foo" command has been registered in the test fixture constructor.
+ request_->context()->body_ = "{ \"command\": \"foo\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Add foo in the access list.
+ CmdResponseCreator::command_accept_list_.insert("foo");
+
+ // Create response from the request.
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_->createHttpResponse(request_));
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must be successful.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 200 OK") !=
+ string::npos);
+
+ // Response must contain JSON body with "result" of 0.
+ EXPECT_TRUE(response_json->toString().find("\"result\": 0") !=
+ string::npos);
+}
+
+}
diff --git a/src/lib/config/tests/command_mgr_unittests.cc b/src/lib/config/tests/command_mgr_unittests.cc
new file mode 100644
index 0000000..b607d3e
--- /dev/null
+++ b/src/lib/config/tests/command_mgr_unittests.cc
@@ -0,0 +1,570 @@
+// Copyright (C) 2015-2020 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 <gtest/gtest.h>
+
+#include <testutils/sandbox.h>
+#include <asiolink/io_service.h>
+#include <config/base_command_mgr.h>
+#include <config/command_mgr.h>
+#include <config/hooked_command_mgr.h>
+#include <cc/command_interpreter.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
+#include <hooks/library_handle.h>
+#include <string>
+#include <vector>
+
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace std;
+
+// Test class for Command Manager
+class CommandMgrTest : public ::testing::Test {
+public:
+ isc::test::Sandbox sandbox;
+
+ /// Default constructor
+ CommandMgrTest()
+ : io_service_(new IOService()) {
+
+ CommandMgr::instance().setIOService(io_service_);
+
+ handler_name_ = "";
+ handler_params_ = ElementPtr();
+ handler_called_ = false;
+ callout_name_ = "";
+ callout_argument_names_.clear();
+ std::string processed_log_ = "";
+
+ CommandMgr::instance().deregisterAll();
+ CommandMgr::instance().closeCommandSocket();
+
+ resetCalloutIndicators();
+ }
+
+ /// Default destructor
+ virtual ~CommandMgrTest() {
+ CommandMgr::instance().deregisterAll();
+ CommandMgr::instance().closeCommandSocket();
+ resetCalloutIndicators();
+ }
+
+ /// @brief Returns socket path (using either hardcoded path or env variable)
+ /// @return path to the unix socket
+ std::string getSocketPath() {
+ std::string socket_path;
+ const char* env = getenv("KEA_SOCKET_TEST_DIR");
+ if (env) {
+ socket_path = std::string(env) + "/test-socket";
+ } else {
+ socket_path = sandbox.join("test-socket");
+ }
+ return (socket_path);
+ }
+
+ /// @brief Resets indicators related to callout invocation.
+ ///
+ /// It also removes any registered callouts.
+ static void resetCalloutIndicators() {
+ callout_name_ = "";
+ callout_argument_names_.clear();
+
+ // Iterate over existing hook points and for each of them remove
+ // callouts registered.
+ std::vector<std::string> hooks = ServerHooks::getServerHooksPtr()->getHookNames();
+ for (auto h = hooks.cbegin(); h != hooks.cend(); ++h) {
+ HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(*h);
+ }
+ }
+
+ /// @brief A simple command handler that always returns an eror
+ static ConstElementPtr my_handler(const std::string& name,
+ const ConstElementPtr& params) {
+
+ handler_name_ = name;
+ handler_params_ = params;
+ handler_called_ = true;
+
+ return (createAnswer(123, "test error message"));
+ }
+
+ /// @brief Test callback which stores callout name and passed arguments and
+ /// which handles the command.
+ ///
+ /// @param callout_handle Handle passed by the hooks framework.
+ /// @return Always 0.
+ static int
+ hook_lib_callout(CalloutHandle& callout_handle) {
+ callout_name_ = "hook_lib_callout";
+
+ ConstElementPtr command;
+ callout_handle.getArgument("command", command);
+
+ ConstElementPtr arg;
+ std::string command_name = parseCommand(arg, command);
+
+ callout_handle.setArgument("response",
+ createAnswer(234, "text generated by hook handler"));
+
+ callout_argument_names_ = callout_handle.getArgumentNames();
+ // Sort arguments alphabetically, so as we can access them on
+ // expected positions and verify.
+ std::sort(callout_argument_names_.begin(), callout_argument_names_.end());
+ return (0);
+ }
+
+ /// @brief Test callback which stores callout name and passed arguments and
+ /// which handles the command.
+ ///
+ /// @param callout_handle Handle passed by the hooks framework.
+ /// @return Always 0.
+ static int
+ command_processed_callout(CalloutHandle& callout_handle) {
+ callout_name_ = "command_processed_handler";
+
+ std::string name;
+ callout_handle.getArgument("name", name);
+
+ ConstElementPtr arguments;
+ callout_handle.getArgument("arguments", arguments);
+
+ ConstElementPtr response;
+ callout_handle.getArgument("response", response);
+ std::ostringstream os;
+ os << name << ":" << arguments->str() << ":" << response->str();
+ processed_log_ = os.str();
+
+ if (name == "change-response") {
+ callout_handle.setArgument("response",
+ createAnswer(777, "replaced response text"));
+ }
+
+ return (0);
+ }
+
+ /// @brief IO service used by these tests.
+ IOServicePtr io_service_;
+
+ /// @brief Name of the command (used in my_handler)
+ static std::string handler_name_;
+
+ /// @brief Parameters passed to the handler (used in my_handler)
+ static ConstElementPtr handler_params_;
+
+ /// @brief Indicates whether my_handler was called
+ static bool handler_called_;
+
+ /// @brief Holds invoked callout name.
+ static std::string callout_name_;
+
+ /// @brief Holds a list of arguments passed to the callout.
+ static std::vector<std::string> callout_argument_names_;
+
+ /// @brief Holds the generated command process log
+ static std::string processed_log_;
+};
+
+/// Name passed to the handler (used in my_handler)
+std::string CommandMgrTest::handler_name_("");
+
+/// Parameters passed to the handler (used in my_handler)
+ConstElementPtr CommandMgrTest::handler_params_;
+
+/// Indicates whether my_handler was called
+bool CommandMgrTest::handler_called_(false);
+
+/// Holds invoked callout name.
+std::string CommandMgrTest::callout_name_("");
+
+/// @brief Holds the generated command process log
+std::string CommandMgrTest::processed_log_;
+
+/// Holds a list of arguments passed to the callout.
+std::vector<std::string> CommandMgrTest::callout_argument_names_;
+
+// Test checks whether the internal command 'list-commands'
+// is working properly.
+TEST_F(CommandMgrTest, listCommandsEmpty) {
+
+ ConstElementPtr command = createCommand("list-commands");
+
+ ConstElementPtr answer;
+
+ EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
+
+ ASSERT_TRUE(answer);
+
+ EXPECT_EQ("{ \"arguments\": [ \"list-commands\" ], \"result\": 0 }",
+ answer->str());
+}
+
+// Test checks whether calling a bogus command is handled properly.
+TEST_F(CommandMgrTest, bogusCommand) {
+
+ ConstElementPtr command = createCommand("no-such-command");
+
+ ConstElementPtr answer;
+
+ EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
+
+ // Make sure the status code is non-zero
+ ASSERT_TRUE(answer);
+ int status_code;
+ parseAnswer(status_code, answer);
+ EXPECT_EQ(CONTROL_RESULT_COMMAND_UNSUPPORTED, status_code);
+}
+
+// Test checks whether handlers installation is sanitized. In particular,
+// whether NULL handler and attempt to install handlers for the same
+// command twice are rejected.
+TEST_F(CommandMgrTest, handlerInstall) {
+
+ // Check that it's not allowed to install NULL pointer instead of a real
+ // command.
+ EXPECT_THROW(CommandMgr::instance().registerCommand("my-command", 0),
+ InvalidCommandHandler);
+
+ // This registration should succeed.
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
+ my_handler));
+
+ // Check that it's not possible to install handlers for the same
+ // command twice.
+ EXPECT_THROW(CommandMgr::instance().registerCommand("my-command",
+ my_handler), InvalidCommandName);
+}
+
+// Test checks whether the internal list-commands command is working
+// correctly. Also, checks installation and deinstallation of other
+// command handlers.
+TEST_F(CommandMgrTest, listCommands) {
+
+ // Let's install two custom commands.
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("make-a-coffee",
+ my_handler));
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("do-the-dishes",
+ my_handler));
+
+ // And then run 'list-commands'
+ ConstElementPtr list_all = createCommand("list-commands");
+ ConstElementPtr answer;
+
+ // Now check that the command is returned by list-commands
+ EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_all));
+ ASSERT_TRUE(answer);
+ EXPECT_EQ("{ \"arguments\": [ \"do-the-dishes\", \"list-commands\", "
+ "\"make-a-coffee\" ], \"result\": 0 }", answer->str());
+
+ // Now unregister one command
+ EXPECT_NO_THROW(CommandMgr::instance().deregisterCommand("do-the-dishes"));
+
+ // Now check that the command is returned by list-commands
+ EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_all));
+ ASSERT_TRUE(answer);
+ EXPECT_EQ("{ \"arguments\": [ \"list-commands\", "
+ "\"make-a-coffee\" ], \"result\": 0 }", answer->str());
+
+ // Now test deregistration. It should work the first time.
+ EXPECT_NO_THROW(CommandMgr::instance().deregisterCommand("make-a-coffee"));
+
+ // Second time should throw an exception as the handler is no longer there.
+ EXPECT_THROW(CommandMgr::instance().deregisterCommand("make-a-coffee"),
+ InvalidCommandName);
+
+ // You can't uninstall list-commands as it's the internal handler.
+ // It always must be there.
+ EXPECT_THROW(CommandMgr::instance().deregisterCommand("list-commands"),
+ InvalidCommandName);
+
+ // Attempt to register a handler for existing command should fail.
+ EXPECT_THROW(CommandMgr::instance().registerCommand("list-commands",
+ my_handler), InvalidCommandName);
+}
+
+// Test checks whether deregisterAll method uninstalls all handlers,
+// except list-commands.
+TEST_F(CommandMgrTest, deregisterAll) {
+
+ // Install a couple handlers.
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command1",
+ my_handler));
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command2",
+ my_handler));
+
+ EXPECT_NO_THROW(CommandMgr::instance().deregisterAll());
+
+ ConstElementPtr answer;
+ EXPECT_NO_THROW(answer = CommandMgr::instance()
+ .processCommand(createCommand("list-commands")));
+ ASSERT_TRUE(answer);
+ EXPECT_EQ("{ \"arguments\": [ \"list-commands\" ], \"result\": 0 }",
+ answer->str());
+}
+
+// Test checks whether a command handler can be installed and then
+// runs through processCommand to check that it's indeed called.
+TEST_F(CommandMgrTest, processCommand) {
+ // Install my handler
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
+ my_handler));
+
+ // Now tell CommandMgr to process a command 'my-command' with the
+ // specified parameter.
+ ElementPtr my_params = Element::fromJSON("[ \"just\", \"some\", \"data\" ]");
+ ConstElementPtr command = createCommand("my-command", my_params);
+ ConstElementPtr answer;
+ EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
+
+ // There should be an answer.
+ ASSERT_TRUE(answer);
+
+ // my_handler remembers all passed parameters and returns status code of 123.
+ ConstElementPtr answer_arg;
+ int status_code;
+ // Check that the returned status code is correct.
+ EXPECT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
+ EXPECT_EQ(123, status_code);
+
+ // Check that the parameters passed are correct.
+ EXPECT_EQ(true, handler_called_);
+ EXPECT_EQ("my-command", handler_name_);
+ ASSERT_TRUE(handler_params_);
+ EXPECT_EQ("[ \"just\", \"some\", \"data\" ]", handler_params_->str());
+
+ // Command handlers not installed so expecting that callouts weren't
+ // called.
+ EXPECT_TRUE(callout_name_.empty());
+}
+
+// Verify that processing a command can be delegated to a hook library.
+TEST_F(CommandMgrTest, delegateProcessCommand) {
+ // Register callout so as we can check that it is called before
+ // processing the command by the manager.
+ HooksManager::preCalloutsLibraryHandle().registerCommandCallout(
+ "my-command", hook_lib_callout);
+
+ // Install local handler
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
+ my_handler));
+
+ // Now tell CommandMgr to process a command 'my-command' with the
+ // specified parameter.
+ ElementPtr my_params = Element::fromJSON("[ \"just\", \"some\", \"data\" ]");
+ ConstElementPtr command = createCommand("my-command", my_params);
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
+
+ // There should be an answer.
+ ASSERT_TRUE(answer);
+
+ // Local handler shouldn't be called because the command is handled by the
+ // hook library.
+ ASSERT_FALSE(handler_called_);
+
+ // Returned status should be unique for the hook library.
+ ConstElementPtr answer_arg;
+ int status_code;
+ ASSERT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
+ EXPECT_EQ(234, status_code);
+
+ EXPECT_EQ("hook_lib_callout", callout_name_);
+
+ // Check that the appropriate arguments have been set. Include the
+ // 'response' which should have been set by the callout.
+ ASSERT_EQ(2, callout_argument_names_.size());
+ EXPECT_EQ("command", callout_argument_names_[0]);
+ EXPECT_EQ("response", callout_argument_names_[1]);
+}
+
+// Verify that 'list-command' command returns combined list of supported
+// commands from hook library and from the Kea Command Manager.
+TEST_F(CommandMgrTest, delegateListCommands) {
+ // Register callout so as we can check that it is called before
+ // processing the command by the manager.
+ HooksManager::preCalloutsLibraryHandle().registerCommandCallout(
+ "my-command", hook_lib_callout);
+
+ // Create my-command-bis which is unique for the local Command Manager,
+ // i.e. not supported by the hook library. This command should also
+ // be returned as a result of processing 'list-commands'.
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command-bis",
+ my_handler));
+
+ // Process command. The command should be routed to the hook library
+ // and the hook library should return the commands it supports.
+ ConstElementPtr command = createCommand("list-commands");
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
+
+ // There should be an answer.
+ ASSERT_TRUE(answer);
+
+ ConstElementPtr answer_arg;
+ int status_code;
+ ASSERT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
+ EXPECT_EQ(0, status_code);
+
+ // The hook library supports: my-command and list-commands commands. The
+ // local Command Manager supports list-commands and my-command-bis. The
+ // combined list should include 3 unique commands.
+ const std::vector<ElementPtr>& commands_list = answer_arg->listValue();
+ ASSERT_EQ(3, commands_list.size());
+ std::vector<std::string> command_names_list;
+ for (auto cmd = commands_list.cbegin(); cmd != commands_list.cend();
+ ++cmd) {
+ command_names_list.push_back((*cmd)->stringValue());
+ }
+ std::sort(command_names_list.begin(), command_names_list.end());
+ EXPECT_EQ("list-commands", command_names_list[0]);
+ EXPECT_EQ("my-command", command_names_list[1]);
+ EXPECT_EQ("my-command-bis", command_names_list[2]);
+}
+
+// This test verifies that a Unix socket can be opened properly and that input
+// parameters (socket-type and socket-name) are verified.
+TEST_F(CommandMgrTest, unixCreate) {
+ // Null pointer is obviously a bad idea.
+ EXPECT_THROW(CommandMgr::instance().openCommandSocket(ConstElementPtr()),
+ isc::config::BadSocketInfo);
+
+ // So is passing no parameters.
+ ElementPtr socket_info = Element::createMap();
+ EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
+ isc::config::BadSocketInfo);
+
+ // We don't support ipx sockets
+ socket_info->set("socket-type", Element::create("ipx"));
+ EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
+ isc::config::BadSocketInfo);
+
+ socket_info->set("socket-type", Element::create("unix"));
+ EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
+ isc::config::BadSocketInfo);
+
+ socket_info->set("socket-name", Element::create(getSocketPath()));
+ EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info));
+ EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0);
+
+ // It should be possible to close the socket.
+ EXPECT_NO_THROW(CommandMgr::instance().closeCommandSocket());
+}
+
+// This test checks that when unix path is too long, the socket cannot be opened.
+TEST_F(CommandMgrTest, unixCreateTooLong) {
+ ElementPtr socket_info = Element::fromJSON("{ \"socket-type\": \"unix\","
+ "\"socket-name\": \"/tmp/toolongtoolongtoolongtoolongtoolongtoolong"
+ "toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong"
+ "\" }");
+
+ EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
+ SocketError);
+}
+
+// This test verifies that a registered callout for the command_processed
+// hookpoint is invoked and passed the correct information.
+TEST_F(CommandMgrTest, commandProcessedHook) {
+ // Register callout so as we can check that it is called before
+ // processing the command by the manager.
+ HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "command_processed", command_processed_callout);
+
+ // Install local handler
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
+ my_handler));
+
+ // Now tell CommandMgr to process a command 'my-command' with the
+ // specified parameter.
+ ElementPtr my_params = Element::fromJSON("[ \"just\", \"some\", \"data\" ]");
+ ConstElementPtr command = createCommand("my-command", my_params);
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
+
+ // There should be an answer.
+ ASSERT_TRUE(answer);
+
+ // Local handler should be called
+ ASSERT_TRUE(handler_called_);
+
+ // Verify that the response came through intact
+ EXPECT_EQ("{ \"result\": 123, \"text\": \"test error message\" }",
+ answer->str());
+
+ // Make sure invoked the command_processed callout
+ EXPECT_EQ("command_processed_handler", callout_name_);
+
+ // Verify the callout could extract all the context arguments
+ EXPECT_EQ("my-command:[ \"just\", \"some\", \"data\" ]:"
+ "{ \"result\": 123, \"text\": \"test error message\" }",
+ processed_log_);
+}
+
+// This test verifies that a registered callout for the command_processed
+// hookpoint is invoked and can replace the command response content.
+TEST_F(CommandMgrTest, commandProcessedHookReplaceResponse) {
+ // Register callout so as we can check that it is called before
+ // processing the command by the manager.
+ HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "command_processed", command_processed_callout);
+
+ // Install local handler
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
+ my_handler));
+
+ // Now tell CommandMgr to process a command 'my-command' with the
+ // specified parameter.
+ ElementPtr my_params = Element::fromJSON("[ \"just\", \"some\", \"data\" ]");
+ ConstElementPtr command = createCommand("change-response", my_params);
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
+
+ // There should be an answer.
+ ASSERT_TRUE(answer);
+
+ // Local handler should not have been called, command isn't recognized
+ ASSERT_FALSE(handler_called_);
+
+ // Verify that we overrode response came
+ EXPECT_EQ("{ \"result\": 777, \"text\": \"replaced response text\" }",
+ answer->str());
+
+ // Make sure invoked the command_processed callout
+ EXPECT_EQ("command_processed_handler", callout_name_);
+
+ // Verify the callout could extract all the context arguments
+ EXPECT_EQ("change-response:[ \"just\", \"some\", \"data\" ]:"
+ "{ \"result\": 2, \"text\": \"'change-response' command not supported.\" }",
+ processed_log_);
+}
+
+// Verifies that a socket cannot be concurrently opened more than once.
+TEST_F(CommandMgrTest, exclusiveOpen) {
+ // Pass in valid parameters.
+ ElementPtr socket_info = Element::createMap();
+ socket_info->set("socket-type", Element::create("unix"));
+ socket_info->set("socket-name", Element::create(getSocketPath()));
+
+ EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info));
+ EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0);
+
+ // Should not be able to open it twice.
+ EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
+ isc::config::SocketError);
+
+ // Now let's close it.
+ EXPECT_NO_THROW(CommandMgr::instance().closeCommandSocket());
+
+ // Should be able to re-open it now.
+ EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info));
+ EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0);
+
+ // Now let's close it.
+ EXPECT_NO_THROW(CommandMgr::instance().closeCommandSocket());
+}
diff --git a/src/lib/config/tests/data_def_unittests_config.h.in b/src/lib/config/tests/data_def_unittests_config.h.in
new file mode 100644
index 0000000..0837f84
--- /dev/null
+++ b/src/lib/config/tests/data_def_unittests_config.h.in
@@ -0,0 +1,7 @@
+// Copyright (C) 2009-2015 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/.
+
+#define TEST_DATA_PATH "@abs_srcdir@/testdata"
diff --git a/src/lib/config/tests/run_unittests.cc b/src/lib/config/tests/run_unittests.cc
new file mode 100644
index 0000000..fc385f9
--- /dev/null
+++ b/src/lib/config/tests/run_unittests.cc
@@ -0,0 +1,18 @@
+// Copyright (C) 2009-2015 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 <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/config/tests/testdata/Makefile.am b/src/lib/config/tests/testdata/Makefile.am
new file mode 100644
index 0000000..641a142
--- /dev/null
+++ b/src/lib/config/tests/testdata/Makefile.am
@@ -0,0 +1,59 @@
+EXTRA_DIST = data22_1.data
+EXTRA_DIST += data22_2.data
+EXTRA_DIST += data22_3.data
+EXTRA_DIST += data22_4.data
+EXTRA_DIST += data22_5.data
+EXTRA_DIST += data22_6.data
+EXTRA_DIST += data22_7.data
+EXTRA_DIST += data22_8.data
+EXTRA_DIST += data22_9.data
+EXTRA_DIST += data22_10.data
+EXTRA_DIST += data32_1.data
+EXTRA_DIST += data32_2.data
+EXTRA_DIST += data32_3.data
+EXTRA_DIST += data33_1.data
+EXTRA_DIST += data33_2.data
+EXTRA_DIST += data41_1.data
+EXTRA_DIST += data41_2.data
+EXTRA_DIST += spec1.spec
+EXTRA_DIST += spec2.spec
+EXTRA_DIST += spec3.spec
+EXTRA_DIST += spec4.spec
+EXTRA_DIST += spec5.spec
+EXTRA_DIST += spec6.spec
+EXTRA_DIST += spec7.spec
+EXTRA_DIST += spec8.spec
+EXTRA_DIST += spec9.spec
+EXTRA_DIST += spec10.spec
+EXTRA_DIST += spec11.spec
+EXTRA_DIST += spec12.spec
+EXTRA_DIST += spec13.spec
+EXTRA_DIST += spec14.spec
+EXTRA_DIST += spec15.spec
+EXTRA_DIST += spec16.spec
+EXTRA_DIST += spec17.spec
+EXTRA_DIST += spec18.spec
+EXTRA_DIST += spec19.spec
+EXTRA_DIST += spec20.spec
+EXTRA_DIST += spec21.spec
+EXTRA_DIST += spec22.spec
+EXTRA_DIST += spec23.spec
+EXTRA_DIST += spec24.spec
+EXTRA_DIST += spec25.spec
+EXTRA_DIST += spec26.spec
+EXTRA_DIST += spec27.spec
+EXTRA_DIST += spec28.spec
+EXTRA_DIST += spec29.spec
+EXTRA_DIST += spec30.spec
+EXTRA_DIST += spec31.spec
+EXTRA_DIST += spec32.spec
+EXTRA_DIST += spec33.spec
+EXTRA_DIST += spec34.spec
+EXTRA_DIST += spec35.spec
+EXTRA_DIST += spec36.spec
+EXTRA_DIST += spec37.spec
+EXTRA_DIST += spec38.spec
+EXTRA_DIST += spec39.spec
+EXTRA_DIST += spec40.spec
+EXTRA_DIST += spec41.spec
+EXTRA_DIST += spec42.spec
diff --git a/src/lib/config/tests/testdata/Makefile.in b/src/lib/config/tests/testdata/Makefile.in
new file mode 100644
index 0000000..4ab04ed
--- /dev/null
+++ b/src/lib/config/tests/testdata/Makefile.in
@@ -0,0 +1,536 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib/config/tests/testdata
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp11.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_sysrepo.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_SYSREPO = @HAVE_SYSREPO@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = data22_1.data data22_2.data data22_3.data data22_4.data \
+ data22_5.data data22_6.data data22_7.data data22_8.data \
+ data22_9.data data22_10.data data32_1.data data32_2.data \
+ data32_3.data data33_1.data data33_2.data data41_1.data \
+ data41_2.data spec1.spec spec2.spec spec3.spec spec4.spec \
+ spec5.spec spec6.spec spec7.spec spec8.spec spec9.spec \
+ spec10.spec spec11.spec spec12.spec spec13.spec spec14.spec \
+ spec15.spec spec16.spec spec17.spec spec18.spec spec19.spec \
+ spec20.spec spec21.spec spec22.spec spec23.spec spec24.spec \
+ spec25.spec spec26.spec spec27.spec spec28.spec spec29.spec \
+ spec30.spec spec31.spec spec32.spec spec33.spec spec34.spec \
+ spec35.spec spec36.spec spec37.spec spec38.spec spec39.spec \
+ spec40.spec spec41.spec spec42.spec
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/config/tests/testdata/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib/config/tests/testdata/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/lib/config/tests/testdata/data22_1.data b/src/lib/config/tests/testdata/data22_1.data
new file mode 100644
index 0000000..18732c7
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_1.data
@@ -0,0 +1,9 @@
+{
+ "value1": 1,
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": true },
+ "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
+}
diff --git a/src/lib/config/tests/testdata/data22_10.data b/src/lib/config/tests/testdata/data22_10.data
new file mode 100644
index 0000000..fed4001
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_10.data
@@ -0,0 +1,11 @@
+{
+ "version": 123,
+ "value1": 1,
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": true },
+ "value8": [ { "a": "d" }, { "a": "e" } ],
+ "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
+}
diff --git a/src/lib/config/tests/testdata/data22_2.data b/src/lib/config/tests/testdata/data22_2.data
new file mode 100644
index 0000000..c820706
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_2.data
@@ -0,0 +1,8 @@
+{
+ "value1": "asdf",
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": true }
+}
diff --git a/src/lib/config/tests/testdata/data22_3.data b/src/lib/config/tests/testdata/data22_3.data
new file mode 100644
index 0000000..7d57dfe
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_3.data
@@ -0,0 +1,8 @@
+{
+ "value1": 1,
+ "value2": false,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": true }
+}
diff --git a/src/lib/config/tests/testdata/data22_4.data b/src/lib/config/tests/testdata/data22_4.data
new file mode 100644
index 0000000..52862d5
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_4.data
@@ -0,0 +1,8 @@
+{
+ "value1": 1,
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, "a" ],
+ "value6": { "v61": "bar", "v62": true }
+}
diff --git a/src/lib/config/tests/testdata/data22_5.data b/src/lib/config/tests/testdata/data22_5.data
new file mode 100644
index 0000000..43937d6
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_5.data
@@ -0,0 +1,8 @@
+{
+ "value1": 1,
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": "Break" }
+}
diff --git a/src/lib/config/tests/testdata/data22_6.data b/src/lib/config/tests/testdata/data22_6.data
new file mode 100644
index 0000000..7b53490
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_6.data
@@ -0,0 +1,10 @@
+{
+ "value1": 1,
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": true },
+ "value7": [ 1, 2.2, "str", true ],
+ "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
+}
diff --git a/src/lib/config/tests/testdata/data22_7.data b/src/lib/config/tests/testdata/data22_7.data
new file mode 100644
index 0000000..98f537b
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_7.data
@@ -0,0 +1,10 @@
+{
+ "value1": 1,
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": true },
+ "value8": [ { "a": "d" }, { "a": "e" } ],
+ "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
+}
diff --git a/src/lib/config/tests/testdata/data22_8.data b/src/lib/config/tests/testdata/data22_8.data
new file mode 100644
index 0000000..8bc0aa9
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_8.data
@@ -0,0 +1,10 @@
+{
+ "value1": 1,
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": true },
+ "value8": [ { "a": "d" }, { "a": 1 } ],
+ "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
+}
diff --git a/src/lib/config/tests/testdata/data22_9.data b/src/lib/config/tests/testdata/data22_9.data
new file mode 100644
index 0000000..f115194
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_9.data
@@ -0,0 +1,11 @@
+{
+ "value1": 1,
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": true },
+ "value8": [ { "a": "d" }, { "a": "e" } ],
+ "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } },
+ "value_does_not_exist": 1
+}
diff --git a/src/lib/config/tests/testdata/data32_1.data b/src/lib/config/tests/testdata/data32_1.data
new file mode 100644
index 0000000..5695b52
--- /dev/null
+++ b/src/lib/config/tests/testdata/data32_1.data
@@ -0,0 +1,3 @@
+{
+ "named_set_item": { "foo": 1, "bar": 2 }
+}
diff --git a/src/lib/config/tests/testdata/data32_2.data b/src/lib/config/tests/testdata/data32_2.data
new file mode 100644
index 0000000..d5b9765
--- /dev/null
+++ b/src/lib/config/tests/testdata/data32_2.data
@@ -0,0 +1,3 @@
+{
+ "named_set_item": { "foo": "wrongtype", "bar": 2 }
+}
diff --git a/src/lib/config/tests/testdata/data32_3.data b/src/lib/config/tests/testdata/data32_3.data
new file mode 100644
index 0000000..85f32fe
--- /dev/null
+++ b/src/lib/config/tests/testdata/data32_3.data
@@ -0,0 +1,3 @@
+{
+ "named_set_item": []
+}
diff --git a/src/lib/config/tests/testdata/data33_1.data b/src/lib/config/tests/testdata/data33_1.data
new file mode 100644
index 0000000..429852c
--- /dev/null
+++ b/src/lib/config/tests/testdata/data33_1.data
@@ -0,0 +1,7 @@
+{
+ "dummy_str": "Dummy String",
+ "dummy_int": 118,
+ "dummy_datetime": "2011-05-27T19:42:57Z",
+ "dummy_date": "2011-05-27",
+ "dummy_time": "19:42:57"
+}
diff --git a/src/lib/config/tests/testdata/data33_2.data b/src/lib/config/tests/testdata/data33_2.data
new file mode 100644
index 0000000..eb0615c
--- /dev/null
+++ b/src/lib/config/tests/testdata/data33_2.data
@@ -0,0 +1,7 @@
+{
+ "dummy_str": "Dummy String",
+ "dummy_int": 118,
+ "dummy_datetime": "xxxx",
+ "dummy_date": "xxxx",
+ "dummy_time": "xxxx"
+}
diff --git a/src/lib/config/tests/testdata/data41_1.data b/src/lib/config/tests/testdata/data41_1.data
new file mode 100644
index 0000000..d309854
--- /dev/null
+++ b/src/lib/config/tests/testdata/data41_1.data
@@ -0,0 +1,12 @@
+{
+ "zones": {
+ "example.org": {
+ "queries.tcp": 100,
+ "queries.udp": 200
+ },
+ "example.net": {
+ "queries.tcp": 300,
+ "queries.udp": 400
+ }
+ }
+}
diff --git a/src/lib/config/tests/testdata/data41_2.data b/src/lib/config/tests/testdata/data41_2.data
new file mode 100644
index 0000000..c64e931
--- /dev/null
+++ b/src/lib/config/tests/testdata/data41_2.data
@@ -0,0 +1,16 @@
+{
+ "zones": [
+ {
+ "example.org": {
+ "queries.tcp": 100,
+ "queries.udp": 200
+ }
+ },
+ {
+ "example.net": {
+ "queries.tcp": 300,
+ "queries.udp": 400
+ }
+ }
+ ]
+}
diff --git a/src/lib/config/tests/testdata/spec1.spec b/src/lib/config/tests/testdata/spec1.spec
new file mode 100644
index 0000000..05a3794
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec1.spec
@@ -0,0 +1,6 @@
+{
+ "module_spec": {
+ "module_name": "Spec1"
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec10.spec b/src/lib/config/tests/testdata/spec10.spec
new file mode 100644
index 0000000..d2bac77
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec10.spec
@@ -0,0 +1,13 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "real",
+ "item_optional": false,
+ "item_default": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec11.spec b/src/lib/config/tests/testdata/spec11.spec
new file mode 100644
index 0000000..61cb2c3
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec11.spec
@@ -0,0 +1,13 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec12.spec b/src/lib/config/tests/testdata/spec12.spec
new file mode 100644
index 0000000..9083a66
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec12.spec
@@ -0,0 +1,13 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec13.spec b/src/lib/config/tests/testdata/spec13.spec
new file mode 100644
index 0000000..c13be46
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec13.spec
@@ -0,0 +1,13 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec14.spec b/src/lib/config/tests/testdata/spec14.spec
new file mode 100644
index 0000000..3ce1852
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec14.spec
@@ -0,0 +1,13 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec15.spec b/src/lib/config/tests/testdata/spec15.spec
new file mode 100644
index 0000000..a9ef085
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec15.spec
@@ -0,0 +1,13 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "badname",
+ "item_optional": false,
+ "item_default": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec16.spec b/src/lib/config/tests/testdata/spec16.spec
new file mode 100644
index 0000000..e78bc2e
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec16.spec
@@ -0,0 +1,7 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": 1
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec17.spec b/src/lib/config/tests/testdata/spec17.spec
new file mode 100644
index 0000000..74a1c25
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec17.spec
@@ -0,0 +1,17 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "commands": [
+ {
+ "command_description": "Print the given message to stdout",
+ "command_args": [ {
+ "item_name": "message",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ } ]
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec18.spec b/src/lib/config/tests/testdata/spec18.spec
new file mode 100644
index 0000000..e3854aa
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec18.spec
@@ -0,0 +1,12 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "commands": [
+ {
+ "command_name": "print_message",
+ "command_description": "Print the given message to stdout"
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec19.spec b/src/lib/config/tests/testdata/spec19.spec
new file mode 100644
index 0000000..1b3c703
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec19.spec
@@ -0,0 +1,13 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "commands": [
+ {
+ "command_name": "print_message",
+ "command_description": "Print the given message to stdout",
+ "command_args": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec2.spec b/src/lib/config/tests/testdata/spec2.spec
new file mode 100644
index 0000000..482c206
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec2.spec
@@ -0,0 +1,83 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 1
+ },
+ { "item_name": "item2",
+ "item_type": "real",
+ "item_optional": false,
+ "item_default": 1.1
+ },
+ { "item_name": "item3",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": true
+ },
+ { "item_name": "item4",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "test"
+ },
+ { "item_name": "item5",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [ "a", "b" ],
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ }
+ },
+ { "item_name": "item6",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "value1",
+ "item_type": "string",
+ "item_optional": true,
+ "item_default": "default"
+ },
+ { "item_name": "value2",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
+ }
+ ],
+ "commands": [
+ {
+ "command_name": "print_message",
+ "command_description": "Print the given message to stdout",
+ "command_args": [ {
+ "item_name": "message",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ } ]
+ },
+ {
+ "command_name": "shutdown",
+ "command_description": "Shut down Kea",
+ "command_args": []
+ }
+ ],
+ "statistics": [
+ {
+ "item_name": "dummy_time",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "1970-01-01T00:00:00Z",
+ "item_title": "Dummy Time",
+ "item_description": "A dummy date time",
+ "item_format": "date-time"
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec20.spec b/src/lib/config/tests/testdata/spec20.spec
new file mode 100644
index 0000000..c9d32a7
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec20.spec
@@ -0,0 +1,18 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "commands": [
+ {
+ "command_name": "print_message",
+ "command_description": "Print the given message to stdout",
+ "command_args": [ {
+ "item_name": "message",
+ "item_type": "somethingbad",
+ "item_optional": false,
+ "item_default": ""
+ } ]
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec21.spec b/src/lib/config/tests/testdata/spec21.spec
new file mode 100644
index 0000000..0af4302
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec21.spec
@@ -0,0 +1,7 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "commands": 1
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec22.spec b/src/lib/config/tests/testdata/spec22.spec
new file mode 100644
index 0000000..687db08
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec22.spec
@@ -0,0 +1,114 @@
+{
+ "module_spec": {
+ "module_name": "Spec22",
+ "config_data": [
+ { "item_name": "value1",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 9
+ },
+ { "item_name": "value2",
+ "item_type": "real",
+ "item_optional": false,
+ "item_default": 9.9
+ },
+ { "item_name": "value3",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ },
+ { "item_name": "value4",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "default_string"
+ },
+ { "item_name": "value5",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [ "a", "b" ],
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 8
+ }
+ },
+ { "item_name": "value6",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "v61",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "def"
+ },
+ { "item_name": "v62",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ }
+ ]
+ },
+ { "item_name": "value7",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [ ],
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "any",
+ "item_optional": true
+ }
+ },
+ { "item_name": "value8",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [ ],
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "map",
+ "item_optional": true,
+ "item_default": { "a": "b" },
+ "map_item_spec": [
+ { "item_name": "a",
+ "item_type": "string",
+ "item_optional": true,
+ "item_default": "empty"
+ }
+ ]
+ }
+ },
+ { "item_name": "value9",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": { "v91": "def", "v92": {} },
+ "map_item_spec": [
+ { "item_name": "v91",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "def"
+ },
+ { "item_name": "v92",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "v92a",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "Hello"
+ } ,
+ {
+ "item_name": "v92b",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 56176
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec23.spec b/src/lib/config/tests/testdata/spec23.spec
new file mode 100644
index 0000000..6791b6c
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec23.spec
@@ -0,0 +1,18 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "commands": [
+ {
+ "command_name": "print_message",
+ "command_description": "Print the given message to stdout",
+ "command_args": [ {
+ "item_name": "message",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ } ]
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec24.spec b/src/lib/config/tests/testdata/spec24.spec
new file mode 100644
index 0000000..bcb50ba
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec24.spec
@@ -0,0 +1,18 @@
+{
+ "module_spec": {
+ "module_name": "Spec24",
+ "config_data": [
+ { "item_name": "item",
+ "item_type": "list",
+ "item_optional": true,
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ }
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec25.spec b/src/lib/config/tests/testdata/spec25.spec
new file mode 100644
index 0000000..6a174d5
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec25.spec
@@ -0,0 +1,7 @@
+{
+ "module_spec": {
+ "module_name": "Spec25",
+ "module_description": "Just an empty module"
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec26.spec b/src/lib/config/tests/testdata/spec26.spec
new file mode 100644
index 0000000..27f3c5b
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec26.spec
@@ -0,0 +1,6 @@
+{
+ "module_spec": {
+ "module_name": "Spec26",
+ "module_description": 1
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec27.spec b/src/lib/config/tests/testdata/spec27.spec
new file mode 100644
index 0000000..2c73021
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec27.spec
@@ -0,0 +1,121 @@
+{
+ "module_spec": {
+ "module_name": "Spec27",
+ "commands": [
+ {
+ "command_name": "cmd1",
+ "command_description": "command_for_unittest",
+ "command_args": [
+ {
+ "item_name": "value1",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 9
+ },
+ { "item_name": "value2",
+ "item_type": "real",
+ "item_optional": false,
+ "item_default": 9.9
+ },
+ { "item_name": "value3",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ },
+ { "item_name": "value4",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "default_string"
+ },
+ { "item_name": "value5",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [ "a", "b" ],
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 8
+ }
+ },
+ { "item_name": "value6",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "v61",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "def"
+ },
+ { "item_name": "v62",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ }
+ ]
+ },
+ { "item_name": "value7",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [ ],
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "any",
+ "item_optional": true
+ }
+ },
+ { "item_name": "value8",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [ ],
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "map",
+ "item_optional": true,
+ "item_default": { "a": "b" },
+ "map_item_spec": [
+ { "item_name": "a",
+ "item_type": "string",
+ "item_optional": true,
+ "item_default": "empty"
+ }
+ ]
+ }
+ },
+ { "item_name": "value9",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "v91",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "def"
+ },
+ { "item_name": "v92",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "v92a",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "Hello"
+ } ,
+ {
+ "item_name": "v92b",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 56176
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec28.spec b/src/lib/config/tests/testdata/spec28.spec
new file mode 100644
index 0000000..cc83acb
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec28.spec
@@ -0,0 +1,7 @@
+{
+ "module_spec": {
+ "module_name": "Spec28",
+ 'commands': [ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec29.spec b/src/lib/config/tests/testdata/spec29.spec
new file mode 100644
index 0000000..c7ae5f8
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec29.spec
@@ -0,0 +1,35 @@
+{
+ "module_spec": {
+ "module_name": "Spec29",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 1
+ }
+ ],
+ "commands": [
+ {
+ "command_name": "good_command",
+ "command_description": "A good command",
+ "command_args": []
+ },
+ {
+ "command_name": "bad_command",
+ "command_description": "A bad command",
+ "command_args": []
+ },
+ {
+ "command_name": "command_with_arg",
+ "command_description": "A command with an (integer) argument",
+ "command_args": [ {
+ "item_name": "number",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 1
+ } ]
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec3.spec b/src/lib/config/tests/testdata/spec3.spec
new file mode 100644
index 0000000..b59d949
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec3.spec
@@ -0,0 +1,13 @@
+{
+ "module_spec": {
+ "module_name": "Spec3",
+ "config_data": [
+ {
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec30.spec b/src/lib/config/tests/testdata/spec30.spec
new file mode 100644
index 0000000..a9e00ad
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec30.spec
@@ -0,0 +1,45 @@
+{
+ "module_spec": {
+ "module_name": "lists",
+ "module_description": "Logging options",
+ "config_data": [
+ {
+ "item_name": "first_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "first_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "foo",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "foo"
+ },
+ { "item_name": "second_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "second_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "final_element",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "hello"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec31.spec b/src/lib/config/tests/testdata/spec31.spec
new file mode 100644
index 0000000..9eebfd1
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec31.spec
@@ -0,0 +1,63 @@
+{
+ "module_spec": {
+ "module_name": "lists",
+ "module_description": "Logging options",
+ "config_data": [
+ {
+ "item_name": "first_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "first_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "foo",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "foo"
+ },
+ { "item_name": "second_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "second_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "map_element",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "list1",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ { "item_name": "list2",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ { "item_name": "number",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 1
+ }
+ }
+ }]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec32.spec b/src/lib/config/tests/testdata/spec32.spec
new file mode 100644
index 0000000..2baf1c1
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec32.spec
@@ -0,0 +1,72 @@
+{
+ "module_spec": {
+ "module_name": "Spec32",
+ "config_data": [
+ { "item_name": "named_set_item",
+ "item_type": "named_set",
+ "item_optional": false,
+ "item_default": { "a": 1, "b": 2 },
+ "named_set_item_spec": {
+ "item_name": "named_set_element",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 3
+ }
+ },
+ { "item_name": "named_set_item2",
+ "item_type": "named_set",
+ "item_optional": true,
+ "item_default": { },
+ "named_set_item_spec": {
+ "item_name": "named_set_element",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "first",
+ "item_type": "integer",
+ "item_optional": true
+ },
+ { "item_name": "second",
+ "item_type": "string",
+ "item_optional": true
+ }
+ ]
+ }
+ },
+ { "item_name": "named_set_item3",
+ "item_type": "named_set",
+ "item_optional": true,
+ "item_default": { "values": [ 1, 2, 3 ] },
+ "named_set_item_spec": {
+ "item_name": "named_set_element",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ { "item_name": "list_value",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ }
+ },
+ { "item_name": "named_set_item4",
+ "item_type": "named_set",
+ "item_optional": true,
+ "item_default": {},
+ "named_set_item_spec": {
+ "item_name": "named_set_element",
+ "item_type": "named_set",
+ "item_optional": false,
+ "item_default": { "a": 1, "b": 2 },
+ "named_set_item_spec":
+ { "item_name": "named_set_element",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ }
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec33.spec b/src/lib/config/tests/testdata/spec33.spec
new file mode 100644
index 0000000..3002488
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec33.spec
@@ -0,0 +1,50 @@
+{
+ "module_spec": {
+ "module_name": "Spec33",
+ "statistics": [
+ {
+ "item_name": "dummy_str",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "Dummy",
+ "item_title": "Dummy String",
+ "item_description": "A dummy string"
+ },
+ {
+ "item_name": "dummy_int",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Dummy Integer",
+ "item_description": "A dummy integer"
+ },
+ {
+ "item_name": "dummy_datetime",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "1970-01-01T00:00:00Z",
+ "item_title": "Dummy DateTime",
+ "item_description": "A dummy datetime",
+ "item_format": "date-time"
+ },
+ {
+ "item_name": "dummy_date",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "1970-01-01",
+ "item_title": "Dummy Date",
+ "item_description": "A dummy date",
+ "item_format": "date"
+ },
+ {
+ "item_name": "dummy_time",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "00:00:00",
+ "item_title": "Dummy Time",
+ "item_description": "A dummy time",
+ "item_format": "time"
+ }
+ ]
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec34.spec b/src/lib/config/tests/testdata/spec34.spec
new file mode 100644
index 0000000..dd1f3ca
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec34.spec
@@ -0,0 +1,14 @@
+{
+ "module_spec": {
+ "module_name": "Spec34",
+ "statistics": [
+ {
+ "item_name": "dummy_str",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "Dummy",
+ "item_description": "A dummy string"
+ }
+ ]
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec35.spec b/src/lib/config/tests/testdata/spec35.spec
new file mode 100644
index 0000000..86aaf14
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec35.spec
@@ -0,0 +1,15 @@
+{
+ "module_spec": {
+ "module_name": "Spec35",
+ "statistics": [
+ {
+ "item_name": "dummy_str",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "Dummy",
+ "item_title": "Dummy String"
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec36.spec b/src/lib/config/tests/testdata/spec36.spec
new file mode 100644
index 0000000..fb9ce26
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec36.spec
@@ -0,0 +1,17 @@
+{
+ "module_spec": {
+ "module_name": "Spec36",
+ "statistics": [
+ {
+ "item_name": "dummy_str",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "Dummy",
+ "item_title": "Dummy String",
+ "item_description": "A dummy string",
+ "item_format": "dummy"
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec37.spec b/src/lib/config/tests/testdata/spec37.spec
new file mode 100644
index 0000000..bc444d1
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec37.spec
@@ -0,0 +1,7 @@
+{
+ "module_spec": {
+ "module_name": "Spec37",
+ "statistics": 8
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec38.spec b/src/lib/config/tests/testdata/spec38.spec
new file mode 100644
index 0000000..1892e88
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec38.spec
@@ -0,0 +1,17 @@
+{
+ "module_spec": {
+ "module_name": "Spec38",
+ "statistics": [
+ {
+ "item_name": "dummy_datetime",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "11",
+ "item_title": "Dummy DateTime",
+ "item_description": "A dummy datetime",
+ "item_format": "date-time"
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec39.spec b/src/lib/config/tests/testdata/spec39.spec
new file mode 100644
index 0000000..1f72319
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec39.spec
@@ -0,0 +1,21 @@
+{
+ "module_spec": {
+ "module_name": "Spec39",
+ "config_data": [
+ { "item_name": "list",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "list_item",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ }
+ }
+ ],
+ "commands": [],
+ "statistics": []
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec4.spec b/src/lib/config/tests/testdata/spec4.spec
new file mode 100644
index 0000000..80d9b22
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec4.spec
@@ -0,0 +1,12 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_optional": false,
+ "item_default": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec40.spec b/src/lib/config/tests/testdata/spec40.spec
new file mode 100644
index 0000000..6fbec10
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec40.spec
@@ -0,0 +1,21 @@
+{
+ "module_spec": {
+ "module_name": "Spec40",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "any",
+ "item_optional": false,
+ "item_default": "asdf"
+ },
+ { "item_name": "item2",
+ "item_type": "any",
+ "item_optional": true
+ },
+ { "item_name": "item3",
+ "item_type": "any",
+ "item_optional": true,
+ "item_default": null
+ }
+ ]
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec41.spec b/src/lib/config/tests/testdata/spec41.spec
new file mode 100644
index 0000000..1c57cfe
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec41.spec
@@ -0,0 +1,35 @@
+{
+ "module_spec": {
+ "module_name": "Spec40",
+ "statistics": [
+ {
+ "item_name": "zones",
+ "item_type": "named_set",
+ "item_optional": false,
+ "item_default": { },
+ "item_title": "Dummy name set",
+ "item_description": "A dummy name set",
+ "named_set_item_spec": {
+ "item_name": "zonename",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": { },
+ "map_item_spec": [
+ {
+ "item_name": "queries.tcp",
+ "item_optional": false,
+ "item_type": "integer",
+ "item_default": 0
+ },
+ {
+ "item_name": "queries.udp",
+ "item_optional": false,
+ "item_type": "integer",
+ "item_default": 0
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec42.spec b/src/lib/config/tests/testdata/spec42.spec
new file mode 100644
index 0000000..d822465
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec42.spec
@@ -0,0 +1,17 @@
+{
+ "module_spec": {
+ "module_name": "Spec42",
+ "config_data": [
+ { "item_name": "list_item",
+ "item_type": "list",
+ "item_optional": true,
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ }
+ }
+ ]
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec5.spec b/src/lib/config/tests/testdata/spec5.spec
new file mode 100644
index 0000000..515424a
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec5.spec
@@ -0,0 +1,12 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "integer",
+ "item_default": 1
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec6.spec b/src/lib/config/tests/testdata/spec6.spec
new file mode 100644
index 0000000..631d882
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec6.spec
@@ -0,0 +1,12 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "integer",
+ "item_optional": false
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec7.spec b/src/lib/config/tests/testdata/spec7.spec
new file mode 100644
index 0000000..42f8b7a
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec7.spec
@@ -0,0 +1,5 @@
+{
+ "module_spec": {
+ }
+}
+
diff --git a/src/lib/config/tests/testdata/spec8.spec b/src/lib/config/tests/testdata/spec8.spec
new file mode 100644
index 0000000..bfd870e
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec8.spec
@@ -0,0 +1,3 @@
+{
+}
+
diff --git a/src/lib/config/tests/testdata/spec9.spec b/src/lib/config/tests/testdata/spec9.spec
new file mode 100644
index 0000000..21018b8
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec9.spec
@@ -0,0 +1,13 @@
+{
+ "module_spec": {
+ "module_name": "Spec2",
+ "config_data": [
+ { "item_name": "item1",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": "asdf"
+ }
+ ]
+ }
+}
+