summaryrefslogtreecommitdiffstats
path: root/src/lib/util/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/tests')
-rw-r--r--src/lib/util/tests/Makefile.am68
-rw-r--r--src/lib/util/tests/Makefile.in1649
-rw-r--r--src/lib/util/tests/bigint_unittest.cc160
-rw-r--r--src/lib/util/tests/boost_time_utils_unittest.cc99
-rw-r--r--src/lib/util/tests/buffer_unittest.cc312
-rw-r--r--src/lib/util/tests/chrono_time_utils_unittest.cc159
-rw-r--r--src/lib/util/tests/csv_file_unittest.cc700
-rw-r--r--src/lib/util/tests/dhcp_space_unittest.cc32
-rw-r--r--src/lib/util/tests/doubles_unittest.cc32
-rw-r--r--src/lib/util/tests/encode_unittest.cc395
-rw-r--r--src/lib/util/tests/fd_share_tests.cc72
-rw-r--r--src/lib/util/tests/fd_tests.cc71
-rw-r--r--src/lib/util/tests/filesystem_unittests.cc125
-rw-r--r--src/lib/util/tests/hash_unittest.cc34
-rw-r--r--src/lib/util/tests/io_unittests.cc236
-rw-r--r--src/lib/util/tests/labeled_value_unittest.cc101
-rw-r--r--src/lib/util/tests/memory_segment_common_unittest.cc100
-rw-r--r--src/lib/util/tests/memory_segment_common_unittest.h28
-rw-r--r--src/lib/util/tests/memory_segment_local_unittest.cc117
-rw-r--r--src/lib/util/tests/multi_threading_mgr_unittest.cc517
-rw-r--r--src/lib/util/tests/optional_unittest.cc162
-rw-r--r--src/lib/util/tests/pid_file_unittest.cc206
-rw-r--r--src/lib/util/tests/range_utilities_unittest.cc50
-rw-r--r--src/lib/util/tests/readwrite_mutex_unittest.cc470
-rw-r--r--src/lib/util/tests/run_unittests.cc18
-rw-r--r--src/lib/util/tests/staged_value_unittest.cc106
-rw-r--r--src/lib/util/tests/state_model_unittest.cc916
-rw-r--r--src/lib/util/tests/stopwatch_unittest.cc307
-rw-r--r--src/lib/util/tests/str_unittests.cc529
-rw-r--r--src/lib/util/tests/thread_pool_unittest.cc804
-rw-r--r--src/lib/util/tests/triplet_unittest.cc125
-rw-r--r--src/lib/util/tests/unlock_guard_unittests.cc236
-rw-r--r--src/lib/util/tests/utf8_unittest.cc50
-rw-r--r--src/lib/util/tests/versioned_csv_file_unittest.cc501
-rw-r--r--src/lib/util/tests/watch_socket_unittests.cc263
-rw-r--r--src/lib/util/tests/watched_thread_unittest.cc218
36 files changed, 9968 insertions, 0 deletions
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
new file mode 100644
index 0000000..430c53d
--- /dev/null
+++ b/src/lib/util/tests/Makefile.am
@@ -0,0 +1,68 @@
+SUBDIRS = .
+
+AM_CPPFLAGS =
+AM_CPPFLAGS += -DABS_SRCDIR=\"$(abs_srcdir)\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_builddir)\"
+AM_CPPFLAGS += -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+# CSV files are created by unit tests for CSVFile class.
+CLEANFILES += *.csv
+
+TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += bigint_unittest.cc
+run_unittests_SOURCES += boost_time_utils_unittest.cc
+run_unittests_SOURCES += buffer_unittest.cc
+run_unittests_SOURCES += chrono_time_utils_unittest.cc
+run_unittests_SOURCES += csv_file_unittest.cc
+run_unittests_SOURCES += dhcp_space_unittest.cc
+run_unittests_SOURCES += doubles_unittest.cc
+run_unittests_SOURCES += encode_unittest.cc
+run_unittests_SOURCES += fd_share_tests.cc
+run_unittests_SOURCES += fd_tests.cc
+run_unittests_SOURCES += filesystem_unittests.cc
+run_unittests_SOURCES += hash_unittest.cc
+run_unittests_SOURCES += io_unittests.cc
+run_unittests_SOURCES += labeled_value_unittest.cc
+run_unittests_SOURCES += memory_segment_common_unittest.cc
+run_unittests_SOURCES += memory_segment_common_unittest.h
+run_unittests_SOURCES += memory_segment_local_unittest.cc
+run_unittests_SOURCES += multi_threading_mgr_unittest.cc
+run_unittests_SOURCES += optional_unittest.cc
+run_unittests_SOURCES += pid_file_unittest.cc
+run_unittests_SOURCES += range_utilities_unittest.cc
+run_unittests_SOURCES += readwrite_mutex_unittest.cc
+run_unittests_SOURCES += staged_value_unittest.cc
+run_unittests_SOURCES += state_model_unittest.cc
+run_unittests_SOURCES += stopwatch_unittest.cc
+run_unittests_SOURCES += str_unittests.cc
+run_unittests_SOURCES += thread_pool_unittest.cc
+run_unittests_SOURCES += triplet_unittest.cc
+run_unittests_SOURCES += unlock_guard_unittests.cc
+run_unittests_SOURCES += utf8_unittest.cc
+run_unittests_SOURCES += versioned_csv_file_unittest.cc
+run_unittests_SOURCES += watch_socket_unittests.cc
+run_unittests_SOURCES += watched_thread_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libkea-util-io.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 += $(GTEST_LDADD)
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/util/tests/Makefile.in b/src/lib/util/tests/Makefile.in
new file mode 100644
index 0000000..8652f8c
--- /dev/null
+++ b/src/lib/util/tests/Makefile.in
@@ -0,0 +1,1649 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 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/util/tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp14.m4 \
+ $(top_srcdir)/m4macros/ax_cpp20.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_netconf.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 =
+@HAVE_GTEST_TRUE@am__EXEEXT_1 = run_unittests$(EXEEXT)
+am__EXEEXT_2 = $(am__EXEEXT_1)
+PROGRAMS = $(noinst_PROGRAMS)
+am__run_unittests_SOURCES_DIST = run_unittests.cc bigint_unittest.cc \
+ boost_time_utils_unittest.cc buffer_unittest.cc \
+ chrono_time_utils_unittest.cc csv_file_unittest.cc \
+ dhcp_space_unittest.cc doubles_unittest.cc encode_unittest.cc \
+ fd_share_tests.cc fd_tests.cc filesystem_unittests.cc \
+ hash_unittest.cc io_unittests.cc labeled_value_unittest.cc \
+ memory_segment_common_unittest.cc \
+ memory_segment_common_unittest.h \
+ memory_segment_local_unittest.cc \
+ multi_threading_mgr_unittest.cc optional_unittest.cc \
+ pid_file_unittest.cc range_utilities_unittest.cc \
+ readwrite_mutex_unittest.cc staged_value_unittest.cc \
+ state_model_unittest.cc stopwatch_unittest.cc str_unittests.cc \
+ thread_pool_unittest.cc triplet_unittest.cc \
+ unlock_guard_unittests.cc utf8_unittest.cc \
+ versioned_csv_file_unittest.cc watch_socket_unittests.cc \
+ watched_thread_unittest.cc
+@HAVE_GTEST_TRUE@am_run_unittests_OBJECTS = \
+@HAVE_GTEST_TRUE@ run_unittests-run_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-bigint_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-boost_time_utils_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-buffer_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-chrono_time_utils_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-csv_file_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-dhcp_space_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-doubles_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-encode_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-fd_share_tests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-fd_tests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-filesystem_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-hash_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-io_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-labeled_value_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-memory_segment_common_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-memory_segment_local_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-multi_threading_mgr_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-optional_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-pid_file_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-range_utilities_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-readwrite_mutex_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-staged_value_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-state_model_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-stopwatch_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-str_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-thread_pool_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-triplet_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-unlock_guard_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-utf8_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-versioned_csv_file_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-watch_socket_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-watched_thread_unittest.$(OBJEXT)
+run_unittests_OBJECTS = $(am_run_unittests_OBJECTS)
+am__DEPENDENCIES_1 =
+@HAVE_GTEST_TRUE@run_unittests_DEPENDENCIES = $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/io/libkea-util-io.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_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-bigint_unittest.Po \
+ ./$(DEPDIR)/run_unittests-boost_time_utils_unittest.Po \
+ ./$(DEPDIR)/run_unittests-buffer_unittest.Po \
+ ./$(DEPDIR)/run_unittests-chrono_time_utils_unittest.Po \
+ ./$(DEPDIR)/run_unittests-csv_file_unittest.Po \
+ ./$(DEPDIR)/run_unittests-dhcp_space_unittest.Po \
+ ./$(DEPDIR)/run_unittests-doubles_unittest.Po \
+ ./$(DEPDIR)/run_unittests-encode_unittest.Po \
+ ./$(DEPDIR)/run_unittests-fd_share_tests.Po \
+ ./$(DEPDIR)/run_unittests-fd_tests.Po \
+ ./$(DEPDIR)/run_unittests-filesystem_unittests.Po \
+ ./$(DEPDIR)/run_unittests-hash_unittest.Po \
+ ./$(DEPDIR)/run_unittests-io_unittests.Po \
+ ./$(DEPDIR)/run_unittests-labeled_value_unittest.Po \
+ ./$(DEPDIR)/run_unittests-memory_segment_common_unittest.Po \
+ ./$(DEPDIR)/run_unittests-memory_segment_local_unittest.Po \
+ ./$(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Po \
+ ./$(DEPDIR)/run_unittests-optional_unittest.Po \
+ ./$(DEPDIR)/run_unittests-pid_file_unittest.Po \
+ ./$(DEPDIR)/run_unittests-range_utilities_unittest.Po \
+ ./$(DEPDIR)/run_unittests-readwrite_mutex_unittest.Po \
+ ./$(DEPDIR)/run_unittests-run_unittests.Po \
+ ./$(DEPDIR)/run_unittests-staged_value_unittest.Po \
+ ./$(DEPDIR)/run_unittests-state_model_unittest.Po \
+ ./$(DEPDIR)/run_unittests-stopwatch_unittest.Po \
+ ./$(DEPDIR)/run_unittests-str_unittests.Po \
+ ./$(DEPDIR)/run_unittests-thread_pool_unittest.Po \
+ ./$(DEPDIR)/run_unittests-triplet_unittest.Po \
+ ./$(DEPDIR)/run_unittests-unlock_guard_unittests.Po \
+ ./$(DEPDIR)/run_unittests-utf8_unittest.Po \
+ ./$(DEPDIR)/run_unittests-versioned_csv_file_unittest.Po \
+ ./$(DEPDIR)/run_unittests-watch_socket_unittests.Po \
+ ./$(DEPDIR)/run_unittests-watched_thread_unittest.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 =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_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)`
+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 $(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@
+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@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+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_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@
+DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@
+DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DPKG = @DPKG@
+DPKGQUERY = @DPKGQUERY@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+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_NETCONF = @HAVE_NETCONF@
+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@
+LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@
+LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@
+LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@
+LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@
+LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@
+LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@
+LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@
+LIBYANG_LIBS = @LIBYANG_LIBS@
+LIBYANG_PREFIX = @LIBYANG_PREFIX@
+LIBYANG_VERSION = @LIBYANG_VERSION@
+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_PLUGINS_PATH = @SR_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@
+SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@
+SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@
+SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_PREFIX = @SYSREPO_PREFIX@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+XMLLINT = @XMLLINT@
+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 = .
+AM_CPPFLAGS = -DABS_SRCDIR=\"$(abs_srcdir)\" \
+ -DTEST_DATA_BUILDDIR=\"$(abs_builddir)\" \
+ -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \
+ $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static
+# CSV files are created by unit tests for CSVFile class.
+CLEANFILES = *.gcno *.gcda *.csv
+TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+@HAVE_GTEST_TRUE@run_unittests_SOURCES = run_unittests.cc \
+@HAVE_GTEST_TRUE@ bigint_unittest.cc \
+@HAVE_GTEST_TRUE@ boost_time_utils_unittest.cc \
+@HAVE_GTEST_TRUE@ buffer_unittest.cc \
+@HAVE_GTEST_TRUE@ chrono_time_utils_unittest.cc \
+@HAVE_GTEST_TRUE@ csv_file_unittest.cc dhcp_space_unittest.cc \
+@HAVE_GTEST_TRUE@ doubles_unittest.cc encode_unittest.cc \
+@HAVE_GTEST_TRUE@ fd_share_tests.cc fd_tests.cc \
+@HAVE_GTEST_TRUE@ filesystem_unittests.cc hash_unittest.cc \
+@HAVE_GTEST_TRUE@ io_unittests.cc labeled_value_unittest.cc \
+@HAVE_GTEST_TRUE@ memory_segment_common_unittest.cc \
+@HAVE_GTEST_TRUE@ memory_segment_common_unittest.h \
+@HAVE_GTEST_TRUE@ memory_segment_local_unittest.cc \
+@HAVE_GTEST_TRUE@ multi_threading_mgr_unittest.cc \
+@HAVE_GTEST_TRUE@ optional_unittest.cc pid_file_unittest.cc \
+@HAVE_GTEST_TRUE@ range_utilities_unittest.cc \
+@HAVE_GTEST_TRUE@ readwrite_mutex_unittest.cc \
+@HAVE_GTEST_TRUE@ staged_value_unittest.cc \
+@HAVE_GTEST_TRUE@ state_model_unittest.cc stopwatch_unittest.cc \
+@HAVE_GTEST_TRUE@ str_unittests.cc thread_pool_unittest.cc \
+@HAVE_GTEST_TRUE@ triplet_unittest.cc unlock_guard_unittests.cc \
+@HAVE_GTEST_TRUE@ utf8_unittest.cc \
+@HAVE_GTEST_TRUE@ versioned_csv_file_unittest.cc \
+@HAVE_GTEST_TRUE@ watch_socket_unittests.cc \
+@HAVE_GTEST_TRUE@ watched_thread_unittest.cc
+@HAVE_GTEST_TRUE@run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+@HAVE_GTEST_TRUE@run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+@HAVE_GTEST_TRUE@run_unittests_LDADD = $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/io/libkea-util-io.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@ $(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/util/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib/util/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):
+
+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-bigint_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-boost_time_utils_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-buffer_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-chrono_time_utils_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-csv_file_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-dhcp_space_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-doubles_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-encode_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-fd_share_tests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-fd_tests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-filesystem_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-hash_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-io_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-labeled_value_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-memory_segment_common_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-memory_segment_local_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-optional_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-pid_file_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-range_utilities_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-readwrite_mutex_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-run_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-staged_value_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-state_model_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-stopwatch_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-str_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-thread_pool_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-triplet_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-unlock_guard_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-utf8_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-versioned_csv_file_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-watch_socket_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-watched_thread_unittest.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-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-bigint_unittest.o: bigint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-bigint_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-bigint_unittest.Tpo -c -o run_unittests-bigint_unittest.o `test -f 'bigint_unittest.cc' || echo '$(srcdir)/'`bigint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-bigint_unittest.Tpo $(DEPDIR)/run_unittests-bigint_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='bigint_unittest.cc' object='run_unittests-bigint_unittest.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-bigint_unittest.o `test -f 'bigint_unittest.cc' || echo '$(srcdir)/'`bigint_unittest.cc
+
+run_unittests-bigint_unittest.obj: bigint_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-bigint_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-bigint_unittest.Tpo -c -o run_unittests-bigint_unittest.obj `if test -f 'bigint_unittest.cc'; then $(CYGPATH_W) 'bigint_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/bigint_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-bigint_unittest.Tpo $(DEPDIR)/run_unittests-bigint_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='bigint_unittest.cc' object='run_unittests-bigint_unittest.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-bigint_unittest.obj `if test -f 'bigint_unittest.cc'; then $(CYGPATH_W) 'bigint_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/bigint_unittest.cc'; fi`
+
+run_unittests-boost_time_utils_unittest.o: boost_time_utils_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-boost_time_utils_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-boost_time_utils_unittest.Tpo -c -o run_unittests-boost_time_utils_unittest.o `test -f 'boost_time_utils_unittest.cc' || echo '$(srcdir)/'`boost_time_utils_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-boost_time_utils_unittest.Tpo $(DEPDIR)/run_unittests-boost_time_utils_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='boost_time_utils_unittest.cc' object='run_unittests-boost_time_utils_unittest.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-boost_time_utils_unittest.o `test -f 'boost_time_utils_unittest.cc' || echo '$(srcdir)/'`boost_time_utils_unittest.cc
+
+run_unittests-boost_time_utils_unittest.obj: boost_time_utils_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-boost_time_utils_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-boost_time_utils_unittest.Tpo -c -o run_unittests-boost_time_utils_unittest.obj `if test -f 'boost_time_utils_unittest.cc'; then $(CYGPATH_W) 'boost_time_utils_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/boost_time_utils_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-boost_time_utils_unittest.Tpo $(DEPDIR)/run_unittests-boost_time_utils_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='boost_time_utils_unittest.cc' object='run_unittests-boost_time_utils_unittest.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-boost_time_utils_unittest.obj `if test -f 'boost_time_utils_unittest.cc'; then $(CYGPATH_W) 'boost_time_utils_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/boost_time_utils_unittest.cc'; fi`
+
+run_unittests-buffer_unittest.o: buffer_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-buffer_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-buffer_unittest.Tpo -c -o run_unittests-buffer_unittest.o `test -f 'buffer_unittest.cc' || echo '$(srcdir)/'`buffer_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-buffer_unittest.Tpo $(DEPDIR)/run_unittests-buffer_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_unittest.cc' object='run_unittests-buffer_unittest.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-buffer_unittest.o `test -f 'buffer_unittest.cc' || echo '$(srcdir)/'`buffer_unittest.cc
+
+run_unittests-buffer_unittest.obj: buffer_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-buffer_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-buffer_unittest.Tpo -c -o run_unittests-buffer_unittest.obj `if test -f 'buffer_unittest.cc'; then $(CYGPATH_W) 'buffer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-buffer_unittest.Tpo $(DEPDIR)/run_unittests-buffer_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_unittest.cc' object='run_unittests-buffer_unittest.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-buffer_unittest.obj `if test -f 'buffer_unittest.cc'; then $(CYGPATH_W) 'buffer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_unittest.cc'; fi`
+
+run_unittests-chrono_time_utils_unittest.o: chrono_time_utils_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-chrono_time_utils_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-chrono_time_utils_unittest.Tpo -c -o run_unittests-chrono_time_utils_unittest.o `test -f 'chrono_time_utils_unittest.cc' || echo '$(srcdir)/'`chrono_time_utils_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-chrono_time_utils_unittest.Tpo $(DEPDIR)/run_unittests-chrono_time_utils_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='chrono_time_utils_unittest.cc' object='run_unittests-chrono_time_utils_unittest.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-chrono_time_utils_unittest.o `test -f 'chrono_time_utils_unittest.cc' || echo '$(srcdir)/'`chrono_time_utils_unittest.cc
+
+run_unittests-chrono_time_utils_unittest.obj: chrono_time_utils_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-chrono_time_utils_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-chrono_time_utils_unittest.Tpo -c -o run_unittests-chrono_time_utils_unittest.obj `if test -f 'chrono_time_utils_unittest.cc'; then $(CYGPATH_W) 'chrono_time_utils_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/chrono_time_utils_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-chrono_time_utils_unittest.Tpo $(DEPDIR)/run_unittests-chrono_time_utils_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='chrono_time_utils_unittest.cc' object='run_unittests-chrono_time_utils_unittest.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-chrono_time_utils_unittest.obj `if test -f 'chrono_time_utils_unittest.cc'; then $(CYGPATH_W) 'chrono_time_utils_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/chrono_time_utils_unittest.cc'; fi`
+
+run_unittests-csv_file_unittest.o: csv_file_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-csv_file_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-csv_file_unittest.Tpo -c -o run_unittests-csv_file_unittest.o `test -f 'csv_file_unittest.cc' || echo '$(srcdir)/'`csv_file_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-csv_file_unittest.Tpo $(DEPDIR)/run_unittests-csv_file_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='csv_file_unittest.cc' object='run_unittests-csv_file_unittest.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-csv_file_unittest.o `test -f 'csv_file_unittest.cc' || echo '$(srcdir)/'`csv_file_unittest.cc
+
+run_unittests-csv_file_unittest.obj: csv_file_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-csv_file_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-csv_file_unittest.Tpo -c -o run_unittests-csv_file_unittest.obj `if test -f 'csv_file_unittest.cc'; then $(CYGPATH_W) 'csv_file_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/csv_file_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-csv_file_unittest.Tpo $(DEPDIR)/run_unittests-csv_file_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='csv_file_unittest.cc' object='run_unittests-csv_file_unittest.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-csv_file_unittest.obj `if test -f 'csv_file_unittest.cc'; then $(CYGPATH_W) 'csv_file_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/csv_file_unittest.cc'; fi`
+
+run_unittests-dhcp_space_unittest.o: dhcp_space_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-dhcp_space_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-dhcp_space_unittest.Tpo -c -o run_unittests-dhcp_space_unittest.o `test -f 'dhcp_space_unittest.cc' || echo '$(srcdir)/'`dhcp_space_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-dhcp_space_unittest.Tpo $(DEPDIR)/run_unittests-dhcp_space_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dhcp_space_unittest.cc' object='run_unittests-dhcp_space_unittest.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-dhcp_space_unittest.o `test -f 'dhcp_space_unittest.cc' || echo '$(srcdir)/'`dhcp_space_unittest.cc
+
+run_unittests-dhcp_space_unittest.obj: dhcp_space_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-dhcp_space_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-dhcp_space_unittest.Tpo -c -o run_unittests-dhcp_space_unittest.obj `if test -f 'dhcp_space_unittest.cc'; then $(CYGPATH_W) 'dhcp_space_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/dhcp_space_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-dhcp_space_unittest.Tpo $(DEPDIR)/run_unittests-dhcp_space_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dhcp_space_unittest.cc' object='run_unittests-dhcp_space_unittest.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-dhcp_space_unittest.obj `if test -f 'dhcp_space_unittest.cc'; then $(CYGPATH_W) 'dhcp_space_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/dhcp_space_unittest.cc'; fi`
+
+run_unittests-doubles_unittest.o: doubles_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-doubles_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-doubles_unittest.Tpo -c -o run_unittests-doubles_unittest.o `test -f 'doubles_unittest.cc' || echo '$(srcdir)/'`doubles_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-doubles_unittest.Tpo $(DEPDIR)/run_unittests-doubles_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='doubles_unittest.cc' object='run_unittests-doubles_unittest.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-doubles_unittest.o `test -f 'doubles_unittest.cc' || echo '$(srcdir)/'`doubles_unittest.cc
+
+run_unittests-doubles_unittest.obj: doubles_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-doubles_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-doubles_unittest.Tpo -c -o run_unittests-doubles_unittest.obj `if test -f 'doubles_unittest.cc'; then $(CYGPATH_W) 'doubles_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/doubles_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-doubles_unittest.Tpo $(DEPDIR)/run_unittests-doubles_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='doubles_unittest.cc' object='run_unittests-doubles_unittest.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-doubles_unittest.obj `if test -f 'doubles_unittest.cc'; then $(CYGPATH_W) 'doubles_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/doubles_unittest.cc'; fi`
+
+run_unittests-encode_unittest.o: encode_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-encode_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-encode_unittest.Tpo -c -o run_unittests-encode_unittest.o `test -f 'encode_unittest.cc' || echo '$(srcdir)/'`encode_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-encode_unittest.Tpo $(DEPDIR)/run_unittests-encode_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='encode_unittest.cc' object='run_unittests-encode_unittest.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-encode_unittest.o `test -f 'encode_unittest.cc' || echo '$(srcdir)/'`encode_unittest.cc
+
+run_unittests-encode_unittest.obj: encode_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-encode_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-encode_unittest.Tpo -c -o run_unittests-encode_unittest.obj `if test -f 'encode_unittest.cc'; then $(CYGPATH_W) 'encode_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/encode_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-encode_unittest.Tpo $(DEPDIR)/run_unittests-encode_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='encode_unittest.cc' object='run_unittests-encode_unittest.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-encode_unittest.obj `if test -f 'encode_unittest.cc'; then $(CYGPATH_W) 'encode_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/encode_unittest.cc'; fi`
+
+run_unittests-fd_share_tests.o: fd_share_tests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-fd_share_tests.o -MD -MP -MF $(DEPDIR)/run_unittests-fd_share_tests.Tpo -c -o run_unittests-fd_share_tests.o `test -f 'fd_share_tests.cc' || echo '$(srcdir)/'`fd_share_tests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-fd_share_tests.Tpo $(DEPDIR)/run_unittests-fd_share_tests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='fd_share_tests.cc' object='run_unittests-fd_share_tests.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-fd_share_tests.o `test -f 'fd_share_tests.cc' || echo '$(srcdir)/'`fd_share_tests.cc
+
+run_unittests-fd_share_tests.obj: fd_share_tests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-fd_share_tests.obj -MD -MP -MF $(DEPDIR)/run_unittests-fd_share_tests.Tpo -c -o run_unittests-fd_share_tests.obj `if test -f 'fd_share_tests.cc'; then $(CYGPATH_W) 'fd_share_tests.cc'; else $(CYGPATH_W) '$(srcdir)/fd_share_tests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-fd_share_tests.Tpo $(DEPDIR)/run_unittests-fd_share_tests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='fd_share_tests.cc' object='run_unittests-fd_share_tests.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-fd_share_tests.obj `if test -f 'fd_share_tests.cc'; then $(CYGPATH_W) 'fd_share_tests.cc'; else $(CYGPATH_W) '$(srcdir)/fd_share_tests.cc'; fi`
+
+run_unittests-fd_tests.o: fd_tests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-fd_tests.o -MD -MP -MF $(DEPDIR)/run_unittests-fd_tests.Tpo -c -o run_unittests-fd_tests.o `test -f 'fd_tests.cc' || echo '$(srcdir)/'`fd_tests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-fd_tests.Tpo $(DEPDIR)/run_unittests-fd_tests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='fd_tests.cc' object='run_unittests-fd_tests.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-fd_tests.o `test -f 'fd_tests.cc' || echo '$(srcdir)/'`fd_tests.cc
+
+run_unittests-fd_tests.obj: fd_tests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-fd_tests.obj -MD -MP -MF $(DEPDIR)/run_unittests-fd_tests.Tpo -c -o run_unittests-fd_tests.obj `if test -f 'fd_tests.cc'; then $(CYGPATH_W) 'fd_tests.cc'; else $(CYGPATH_W) '$(srcdir)/fd_tests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-fd_tests.Tpo $(DEPDIR)/run_unittests-fd_tests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='fd_tests.cc' object='run_unittests-fd_tests.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-fd_tests.obj `if test -f 'fd_tests.cc'; then $(CYGPATH_W) 'fd_tests.cc'; else $(CYGPATH_W) '$(srcdir)/fd_tests.cc'; fi`
+
+run_unittests-filesystem_unittests.o: filesystem_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-filesystem_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-filesystem_unittests.Tpo -c -o run_unittests-filesystem_unittests.o `test -f 'filesystem_unittests.cc' || echo '$(srcdir)/'`filesystem_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-filesystem_unittests.Tpo $(DEPDIR)/run_unittests-filesystem_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='filesystem_unittests.cc' object='run_unittests-filesystem_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-filesystem_unittests.o `test -f 'filesystem_unittests.cc' || echo '$(srcdir)/'`filesystem_unittests.cc
+
+run_unittests-filesystem_unittests.obj: filesystem_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-filesystem_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-filesystem_unittests.Tpo -c -o run_unittests-filesystem_unittests.obj `if test -f 'filesystem_unittests.cc'; then $(CYGPATH_W) 'filesystem_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/filesystem_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-filesystem_unittests.Tpo $(DEPDIR)/run_unittests-filesystem_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='filesystem_unittests.cc' object='run_unittests-filesystem_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-filesystem_unittests.obj `if test -f 'filesystem_unittests.cc'; then $(CYGPATH_W) 'filesystem_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/filesystem_unittests.cc'; fi`
+
+run_unittests-hash_unittest.o: hash_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-hash_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-hash_unittest.Tpo -c -o run_unittests-hash_unittest.o `test -f 'hash_unittest.cc' || echo '$(srcdir)/'`hash_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-hash_unittest.Tpo $(DEPDIR)/run_unittests-hash_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hash_unittest.cc' object='run_unittests-hash_unittest.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-hash_unittest.o `test -f 'hash_unittest.cc' || echo '$(srcdir)/'`hash_unittest.cc
+
+run_unittests-hash_unittest.obj: hash_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-hash_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-hash_unittest.Tpo -c -o run_unittests-hash_unittest.obj `if test -f 'hash_unittest.cc'; then $(CYGPATH_W) 'hash_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/hash_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-hash_unittest.Tpo $(DEPDIR)/run_unittests-hash_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hash_unittest.cc' object='run_unittests-hash_unittest.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-hash_unittest.obj `if test -f 'hash_unittest.cc'; then $(CYGPATH_W) 'hash_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/hash_unittest.cc'; fi`
+
+run_unittests-io_unittests.o: io_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-io_unittests.Tpo -c -o run_unittests-io_unittests.o `test -f 'io_unittests.cc' || echo '$(srcdir)/'`io_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_unittests.Tpo $(DEPDIR)/run_unittests-io_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_unittests.cc' object='run_unittests-io_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-io_unittests.o `test -f 'io_unittests.cc' || echo '$(srcdir)/'`io_unittests.cc
+
+run_unittests-io_unittests.obj: io_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-io_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-io_unittests.Tpo -c -o run_unittests-io_unittests.obj `if test -f 'io_unittests.cc'; then $(CYGPATH_W) 'io_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/io_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-io_unittests.Tpo $(DEPDIR)/run_unittests-io_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='io_unittests.cc' object='run_unittests-io_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-io_unittests.obj `if test -f 'io_unittests.cc'; then $(CYGPATH_W) 'io_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/io_unittests.cc'; fi`
+
+run_unittests-labeled_value_unittest.o: labeled_value_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-labeled_value_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-labeled_value_unittest.Tpo -c -o run_unittests-labeled_value_unittest.o `test -f 'labeled_value_unittest.cc' || echo '$(srcdir)/'`labeled_value_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-labeled_value_unittest.Tpo $(DEPDIR)/run_unittests-labeled_value_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='labeled_value_unittest.cc' object='run_unittests-labeled_value_unittest.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-labeled_value_unittest.o `test -f 'labeled_value_unittest.cc' || echo '$(srcdir)/'`labeled_value_unittest.cc
+
+run_unittests-labeled_value_unittest.obj: labeled_value_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-labeled_value_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-labeled_value_unittest.Tpo -c -o run_unittests-labeled_value_unittest.obj `if test -f 'labeled_value_unittest.cc'; then $(CYGPATH_W) 'labeled_value_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/labeled_value_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-labeled_value_unittest.Tpo $(DEPDIR)/run_unittests-labeled_value_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='labeled_value_unittest.cc' object='run_unittests-labeled_value_unittest.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-labeled_value_unittest.obj `if test -f 'labeled_value_unittest.cc'; then $(CYGPATH_W) 'labeled_value_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/labeled_value_unittest.cc'; fi`
+
+run_unittests-memory_segment_common_unittest.o: memory_segment_common_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-memory_segment_common_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-memory_segment_common_unittest.Tpo -c -o run_unittests-memory_segment_common_unittest.o `test -f 'memory_segment_common_unittest.cc' || echo '$(srcdir)/'`memory_segment_common_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-memory_segment_common_unittest.Tpo $(DEPDIR)/run_unittests-memory_segment_common_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memory_segment_common_unittest.cc' object='run_unittests-memory_segment_common_unittest.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-memory_segment_common_unittest.o `test -f 'memory_segment_common_unittest.cc' || echo '$(srcdir)/'`memory_segment_common_unittest.cc
+
+run_unittests-memory_segment_common_unittest.obj: memory_segment_common_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-memory_segment_common_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-memory_segment_common_unittest.Tpo -c -o run_unittests-memory_segment_common_unittest.obj `if test -f 'memory_segment_common_unittest.cc'; then $(CYGPATH_W) 'memory_segment_common_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/memory_segment_common_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-memory_segment_common_unittest.Tpo $(DEPDIR)/run_unittests-memory_segment_common_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memory_segment_common_unittest.cc' object='run_unittests-memory_segment_common_unittest.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-memory_segment_common_unittest.obj `if test -f 'memory_segment_common_unittest.cc'; then $(CYGPATH_W) 'memory_segment_common_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/memory_segment_common_unittest.cc'; fi`
+
+run_unittests-memory_segment_local_unittest.o: memory_segment_local_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-memory_segment_local_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-memory_segment_local_unittest.Tpo -c -o run_unittests-memory_segment_local_unittest.o `test -f 'memory_segment_local_unittest.cc' || echo '$(srcdir)/'`memory_segment_local_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-memory_segment_local_unittest.Tpo $(DEPDIR)/run_unittests-memory_segment_local_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memory_segment_local_unittest.cc' object='run_unittests-memory_segment_local_unittest.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-memory_segment_local_unittest.o `test -f 'memory_segment_local_unittest.cc' || echo '$(srcdir)/'`memory_segment_local_unittest.cc
+
+run_unittests-memory_segment_local_unittest.obj: memory_segment_local_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-memory_segment_local_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-memory_segment_local_unittest.Tpo -c -o run_unittests-memory_segment_local_unittest.obj `if test -f 'memory_segment_local_unittest.cc'; then $(CYGPATH_W) 'memory_segment_local_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/memory_segment_local_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-memory_segment_local_unittest.Tpo $(DEPDIR)/run_unittests-memory_segment_local_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memory_segment_local_unittest.cc' object='run_unittests-memory_segment_local_unittest.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-memory_segment_local_unittest.obj `if test -f 'memory_segment_local_unittest.cc'; then $(CYGPATH_W) 'memory_segment_local_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/memory_segment_local_unittest.cc'; fi`
+
+run_unittests-multi_threading_mgr_unittest.o: multi_threading_mgr_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-multi_threading_mgr_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Tpo -c -o run_unittests-multi_threading_mgr_unittest.o `test -f 'multi_threading_mgr_unittest.cc' || echo '$(srcdir)/'`multi_threading_mgr_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Tpo $(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='multi_threading_mgr_unittest.cc' object='run_unittests-multi_threading_mgr_unittest.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-multi_threading_mgr_unittest.o `test -f 'multi_threading_mgr_unittest.cc' || echo '$(srcdir)/'`multi_threading_mgr_unittest.cc
+
+run_unittests-multi_threading_mgr_unittest.obj: multi_threading_mgr_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-multi_threading_mgr_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Tpo -c -o run_unittests-multi_threading_mgr_unittest.obj `if test -f 'multi_threading_mgr_unittest.cc'; then $(CYGPATH_W) 'multi_threading_mgr_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/multi_threading_mgr_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Tpo $(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='multi_threading_mgr_unittest.cc' object='run_unittests-multi_threading_mgr_unittest.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-multi_threading_mgr_unittest.obj `if test -f 'multi_threading_mgr_unittest.cc'; then $(CYGPATH_W) 'multi_threading_mgr_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/multi_threading_mgr_unittest.cc'; fi`
+
+run_unittests-optional_unittest.o: optional_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-optional_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-optional_unittest.Tpo -c -o run_unittests-optional_unittest.o `test -f 'optional_unittest.cc' || echo '$(srcdir)/'`optional_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-optional_unittest.Tpo $(DEPDIR)/run_unittests-optional_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='optional_unittest.cc' object='run_unittests-optional_unittest.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-optional_unittest.o `test -f 'optional_unittest.cc' || echo '$(srcdir)/'`optional_unittest.cc
+
+run_unittests-optional_unittest.obj: optional_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-optional_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-optional_unittest.Tpo -c -o run_unittests-optional_unittest.obj `if test -f 'optional_unittest.cc'; then $(CYGPATH_W) 'optional_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/optional_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-optional_unittest.Tpo $(DEPDIR)/run_unittests-optional_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='optional_unittest.cc' object='run_unittests-optional_unittest.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-optional_unittest.obj `if test -f 'optional_unittest.cc'; then $(CYGPATH_W) 'optional_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/optional_unittest.cc'; fi`
+
+run_unittests-pid_file_unittest.o: pid_file_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-pid_file_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-pid_file_unittest.Tpo -c -o run_unittests-pid_file_unittest.o `test -f 'pid_file_unittest.cc' || echo '$(srcdir)/'`pid_file_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-pid_file_unittest.Tpo $(DEPDIR)/run_unittests-pid_file_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='pid_file_unittest.cc' object='run_unittests-pid_file_unittest.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-pid_file_unittest.o `test -f 'pid_file_unittest.cc' || echo '$(srcdir)/'`pid_file_unittest.cc
+
+run_unittests-pid_file_unittest.obj: pid_file_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-pid_file_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-pid_file_unittest.Tpo -c -o run_unittests-pid_file_unittest.obj `if test -f 'pid_file_unittest.cc'; then $(CYGPATH_W) 'pid_file_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/pid_file_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-pid_file_unittest.Tpo $(DEPDIR)/run_unittests-pid_file_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='pid_file_unittest.cc' object='run_unittests-pid_file_unittest.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-pid_file_unittest.obj `if test -f 'pid_file_unittest.cc'; then $(CYGPATH_W) 'pid_file_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/pid_file_unittest.cc'; fi`
+
+run_unittests-range_utilities_unittest.o: range_utilities_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-range_utilities_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-range_utilities_unittest.Tpo -c -o run_unittests-range_utilities_unittest.o `test -f 'range_utilities_unittest.cc' || echo '$(srcdir)/'`range_utilities_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-range_utilities_unittest.Tpo $(DEPDIR)/run_unittests-range_utilities_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='range_utilities_unittest.cc' object='run_unittests-range_utilities_unittest.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-range_utilities_unittest.o `test -f 'range_utilities_unittest.cc' || echo '$(srcdir)/'`range_utilities_unittest.cc
+
+run_unittests-range_utilities_unittest.obj: range_utilities_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-range_utilities_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-range_utilities_unittest.Tpo -c -o run_unittests-range_utilities_unittest.obj `if test -f 'range_utilities_unittest.cc'; then $(CYGPATH_W) 'range_utilities_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/range_utilities_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-range_utilities_unittest.Tpo $(DEPDIR)/run_unittests-range_utilities_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='range_utilities_unittest.cc' object='run_unittests-range_utilities_unittest.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-range_utilities_unittest.obj `if test -f 'range_utilities_unittest.cc'; then $(CYGPATH_W) 'range_utilities_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/range_utilities_unittest.cc'; fi`
+
+run_unittests-readwrite_mutex_unittest.o: readwrite_mutex_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-readwrite_mutex_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-readwrite_mutex_unittest.Tpo -c -o run_unittests-readwrite_mutex_unittest.o `test -f 'readwrite_mutex_unittest.cc' || echo '$(srcdir)/'`readwrite_mutex_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-readwrite_mutex_unittest.Tpo $(DEPDIR)/run_unittests-readwrite_mutex_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='readwrite_mutex_unittest.cc' object='run_unittests-readwrite_mutex_unittest.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-readwrite_mutex_unittest.o `test -f 'readwrite_mutex_unittest.cc' || echo '$(srcdir)/'`readwrite_mutex_unittest.cc
+
+run_unittests-readwrite_mutex_unittest.obj: readwrite_mutex_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-readwrite_mutex_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-readwrite_mutex_unittest.Tpo -c -o run_unittests-readwrite_mutex_unittest.obj `if test -f 'readwrite_mutex_unittest.cc'; then $(CYGPATH_W) 'readwrite_mutex_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/readwrite_mutex_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-readwrite_mutex_unittest.Tpo $(DEPDIR)/run_unittests-readwrite_mutex_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='readwrite_mutex_unittest.cc' object='run_unittests-readwrite_mutex_unittest.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-readwrite_mutex_unittest.obj `if test -f 'readwrite_mutex_unittest.cc'; then $(CYGPATH_W) 'readwrite_mutex_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/readwrite_mutex_unittest.cc'; fi`
+
+run_unittests-staged_value_unittest.o: staged_value_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-staged_value_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-staged_value_unittest.Tpo -c -o run_unittests-staged_value_unittest.o `test -f 'staged_value_unittest.cc' || echo '$(srcdir)/'`staged_value_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-staged_value_unittest.Tpo $(DEPDIR)/run_unittests-staged_value_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='staged_value_unittest.cc' object='run_unittests-staged_value_unittest.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-staged_value_unittest.o `test -f 'staged_value_unittest.cc' || echo '$(srcdir)/'`staged_value_unittest.cc
+
+run_unittests-staged_value_unittest.obj: staged_value_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-staged_value_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-staged_value_unittest.Tpo -c -o run_unittests-staged_value_unittest.obj `if test -f 'staged_value_unittest.cc'; then $(CYGPATH_W) 'staged_value_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/staged_value_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-staged_value_unittest.Tpo $(DEPDIR)/run_unittests-staged_value_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='staged_value_unittest.cc' object='run_unittests-staged_value_unittest.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-staged_value_unittest.obj `if test -f 'staged_value_unittest.cc'; then $(CYGPATH_W) 'staged_value_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/staged_value_unittest.cc'; fi`
+
+run_unittests-state_model_unittest.o: state_model_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-state_model_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-state_model_unittest.Tpo -c -o run_unittests-state_model_unittest.o `test -f 'state_model_unittest.cc' || echo '$(srcdir)/'`state_model_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-state_model_unittest.Tpo $(DEPDIR)/run_unittests-state_model_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='state_model_unittest.cc' object='run_unittests-state_model_unittest.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-state_model_unittest.o `test -f 'state_model_unittest.cc' || echo '$(srcdir)/'`state_model_unittest.cc
+
+run_unittests-state_model_unittest.obj: state_model_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-state_model_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-state_model_unittest.Tpo -c -o run_unittests-state_model_unittest.obj `if test -f 'state_model_unittest.cc'; then $(CYGPATH_W) 'state_model_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/state_model_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-state_model_unittest.Tpo $(DEPDIR)/run_unittests-state_model_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='state_model_unittest.cc' object='run_unittests-state_model_unittest.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-state_model_unittest.obj `if test -f 'state_model_unittest.cc'; then $(CYGPATH_W) 'state_model_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/state_model_unittest.cc'; fi`
+
+run_unittests-stopwatch_unittest.o: stopwatch_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-stopwatch_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-stopwatch_unittest.Tpo -c -o run_unittests-stopwatch_unittest.o `test -f 'stopwatch_unittest.cc' || echo '$(srcdir)/'`stopwatch_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-stopwatch_unittest.Tpo $(DEPDIR)/run_unittests-stopwatch_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='stopwatch_unittest.cc' object='run_unittests-stopwatch_unittest.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-stopwatch_unittest.o `test -f 'stopwatch_unittest.cc' || echo '$(srcdir)/'`stopwatch_unittest.cc
+
+run_unittests-stopwatch_unittest.obj: stopwatch_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-stopwatch_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-stopwatch_unittest.Tpo -c -o run_unittests-stopwatch_unittest.obj `if test -f 'stopwatch_unittest.cc'; then $(CYGPATH_W) 'stopwatch_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/stopwatch_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-stopwatch_unittest.Tpo $(DEPDIR)/run_unittests-stopwatch_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='stopwatch_unittest.cc' object='run_unittests-stopwatch_unittest.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-stopwatch_unittest.obj `if test -f 'stopwatch_unittest.cc'; then $(CYGPATH_W) 'stopwatch_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/stopwatch_unittest.cc'; fi`
+
+run_unittests-str_unittests.o: str_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-str_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-str_unittests.Tpo -c -o run_unittests-str_unittests.o `test -f 'str_unittests.cc' || echo '$(srcdir)/'`str_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-str_unittests.Tpo $(DEPDIR)/run_unittests-str_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='str_unittests.cc' object='run_unittests-str_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-str_unittests.o `test -f 'str_unittests.cc' || echo '$(srcdir)/'`str_unittests.cc
+
+run_unittests-str_unittests.obj: str_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-str_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-str_unittests.Tpo -c -o run_unittests-str_unittests.obj `if test -f 'str_unittests.cc'; then $(CYGPATH_W) 'str_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/str_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-str_unittests.Tpo $(DEPDIR)/run_unittests-str_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='str_unittests.cc' object='run_unittests-str_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-str_unittests.obj `if test -f 'str_unittests.cc'; then $(CYGPATH_W) 'str_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/str_unittests.cc'; fi`
+
+run_unittests-thread_pool_unittest.o: thread_pool_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-thread_pool_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-thread_pool_unittest.Tpo -c -o run_unittests-thread_pool_unittest.o `test -f 'thread_pool_unittest.cc' || echo '$(srcdir)/'`thread_pool_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-thread_pool_unittest.Tpo $(DEPDIR)/run_unittests-thread_pool_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='thread_pool_unittest.cc' object='run_unittests-thread_pool_unittest.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-thread_pool_unittest.o `test -f 'thread_pool_unittest.cc' || echo '$(srcdir)/'`thread_pool_unittest.cc
+
+run_unittests-thread_pool_unittest.obj: thread_pool_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-thread_pool_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-thread_pool_unittest.Tpo -c -o run_unittests-thread_pool_unittest.obj `if test -f 'thread_pool_unittest.cc'; then $(CYGPATH_W) 'thread_pool_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/thread_pool_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-thread_pool_unittest.Tpo $(DEPDIR)/run_unittests-thread_pool_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='thread_pool_unittest.cc' object='run_unittests-thread_pool_unittest.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-thread_pool_unittest.obj `if test -f 'thread_pool_unittest.cc'; then $(CYGPATH_W) 'thread_pool_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/thread_pool_unittest.cc'; fi`
+
+run_unittests-triplet_unittest.o: triplet_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-triplet_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-triplet_unittest.Tpo -c -o run_unittests-triplet_unittest.o `test -f 'triplet_unittest.cc' || echo '$(srcdir)/'`triplet_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-triplet_unittest.Tpo $(DEPDIR)/run_unittests-triplet_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='triplet_unittest.cc' object='run_unittests-triplet_unittest.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-triplet_unittest.o `test -f 'triplet_unittest.cc' || echo '$(srcdir)/'`triplet_unittest.cc
+
+run_unittests-triplet_unittest.obj: triplet_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-triplet_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-triplet_unittest.Tpo -c -o run_unittests-triplet_unittest.obj `if test -f 'triplet_unittest.cc'; then $(CYGPATH_W) 'triplet_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/triplet_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-triplet_unittest.Tpo $(DEPDIR)/run_unittests-triplet_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='triplet_unittest.cc' object='run_unittests-triplet_unittest.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-triplet_unittest.obj `if test -f 'triplet_unittest.cc'; then $(CYGPATH_W) 'triplet_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/triplet_unittest.cc'; fi`
+
+run_unittests-unlock_guard_unittests.o: unlock_guard_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-unlock_guard_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-unlock_guard_unittests.Tpo -c -o run_unittests-unlock_guard_unittests.o `test -f 'unlock_guard_unittests.cc' || echo '$(srcdir)/'`unlock_guard_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-unlock_guard_unittests.Tpo $(DEPDIR)/run_unittests-unlock_guard_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='unlock_guard_unittests.cc' object='run_unittests-unlock_guard_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-unlock_guard_unittests.o `test -f 'unlock_guard_unittests.cc' || echo '$(srcdir)/'`unlock_guard_unittests.cc
+
+run_unittests-unlock_guard_unittests.obj: unlock_guard_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-unlock_guard_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-unlock_guard_unittests.Tpo -c -o run_unittests-unlock_guard_unittests.obj `if test -f 'unlock_guard_unittests.cc'; then $(CYGPATH_W) 'unlock_guard_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/unlock_guard_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-unlock_guard_unittests.Tpo $(DEPDIR)/run_unittests-unlock_guard_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='unlock_guard_unittests.cc' object='run_unittests-unlock_guard_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-unlock_guard_unittests.obj `if test -f 'unlock_guard_unittests.cc'; then $(CYGPATH_W) 'unlock_guard_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/unlock_guard_unittests.cc'; fi`
+
+run_unittests-utf8_unittest.o: utf8_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-utf8_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-utf8_unittest.Tpo -c -o run_unittests-utf8_unittest.o `test -f 'utf8_unittest.cc' || echo '$(srcdir)/'`utf8_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-utf8_unittest.Tpo $(DEPDIR)/run_unittests-utf8_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='utf8_unittest.cc' object='run_unittests-utf8_unittest.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-utf8_unittest.o `test -f 'utf8_unittest.cc' || echo '$(srcdir)/'`utf8_unittest.cc
+
+run_unittests-utf8_unittest.obj: utf8_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-utf8_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-utf8_unittest.Tpo -c -o run_unittests-utf8_unittest.obj `if test -f 'utf8_unittest.cc'; then $(CYGPATH_W) 'utf8_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/utf8_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-utf8_unittest.Tpo $(DEPDIR)/run_unittests-utf8_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='utf8_unittest.cc' object='run_unittests-utf8_unittest.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-utf8_unittest.obj `if test -f 'utf8_unittest.cc'; then $(CYGPATH_W) 'utf8_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/utf8_unittest.cc'; fi`
+
+run_unittests-versioned_csv_file_unittest.o: versioned_csv_file_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-versioned_csv_file_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-versioned_csv_file_unittest.Tpo -c -o run_unittests-versioned_csv_file_unittest.o `test -f 'versioned_csv_file_unittest.cc' || echo '$(srcdir)/'`versioned_csv_file_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-versioned_csv_file_unittest.Tpo $(DEPDIR)/run_unittests-versioned_csv_file_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='versioned_csv_file_unittest.cc' object='run_unittests-versioned_csv_file_unittest.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-versioned_csv_file_unittest.o `test -f 'versioned_csv_file_unittest.cc' || echo '$(srcdir)/'`versioned_csv_file_unittest.cc
+
+run_unittests-versioned_csv_file_unittest.obj: versioned_csv_file_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-versioned_csv_file_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-versioned_csv_file_unittest.Tpo -c -o run_unittests-versioned_csv_file_unittest.obj `if test -f 'versioned_csv_file_unittest.cc'; then $(CYGPATH_W) 'versioned_csv_file_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/versioned_csv_file_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-versioned_csv_file_unittest.Tpo $(DEPDIR)/run_unittests-versioned_csv_file_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='versioned_csv_file_unittest.cc' object='run_unittests-versioned_csv_file_unittest.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-versioned_csv_file_unittest.obj `if test -f 'versioned_csv_file_unittest.cc'; then $(CYGPATH_W) 'versioned_csv_file_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/versioned_csv_file_unittest.cc'; fi`
+
+run_unittests-watch_socket_unittests.o: watch_socket_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-watch_socket_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-watch_socket_unittests.Tpo -c -o run_unittests-watch_socket_unittests.o `test -f 'watch_socket_unittests.cc' || echo '$(srcdir)/'`watch_socket_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-watch_socket_unittests.Tpo $(DEPDIR)/run_unittests-watch_socket_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='watch_socket_unittests.cc' object='run_unittests-watch_socket_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-watch_socket_unittests.o `test -f 'watch_socket_unittests.cc' || echo '$(srcdir)/'`watch_socket_unittests.cc
+
+run_unittests-watch_socket_unittests.obj: watch_socket_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-watch_socket_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-watch_socket_unittests.Tpo -c -o run_unittests-watch_socket_unittests.obj `if test -f 'watch_socket_unittests.cc'; then $(CYGPATH_W) 'watch_socket_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/watch_socket_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-watch_socket_unittests.Tpo $(DEPDIR)/run_unittests-watch_socket_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='watch_socket_unittests.cc' object='run_unittests-watch_socket_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-watch_socket_unittests.obj `if test -f 'watch_socket_unittests.cc'; then $(CYGPATH_W) 'watch_socket_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/watch_socket_unittests.cc'; fi`
+
+run_unittests-watched_thread_unittest.o: watched_thread_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-watched_thread_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-watched_thread_unittest.Tpo -c -o run_unittests-watched_thread_unittest.o `test -f 'watched_thread_unittest.cc' || echo '$(srcdir)/'`watched_thread_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-watched_thread_unittest.Tpo $(DEPDIR)/run_unittests-watched_thread_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='watched_thread_unittest.cc' object='run_unittests-watched_thread_unittest.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-watched_thread_unittest.o `test -f 'watched_thread_unittest.cc' || echo '$(srcdir)/'`watched_thread_unittest.cc
+
+run_unittests-watched_thread_unittest.obj: watched_thread_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-watched_thread_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-watched_thread_unittest.Tpo -c -o run_unittests-watched_thread_unittest.obj `if test -f 'watched_thread_unittest.cc'; then $(CYGPATH_W) 'watched_thread_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/watched_thread_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-watched_thread_unittest.Tpo $(DEPDIR)/run_unittests-watched_thread_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='watched_thread_unittest.cc' object='run_unittests-watched_thread_unittest.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-watched_thread_unittest.obj `if test -f 'watched_thread_unittest.cc'; then $(CYGPATH_W) 'watched_thread_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/watched_thread_unittest.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)
+
+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-bigint_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-boost_time_utils_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-buffer_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-chrono_time_utils_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-csv_file_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-dhcp_space_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-doubles_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-encode_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-fd_share_tests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-fd_tests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-filesystem_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-hash_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-labeled_value_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-memory_segment_common_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-memory_segment_local_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-optional_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-pid_file_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-range_utilities_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-readwrite_mutex_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-staged_value_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-state_model_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-stopwatch_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-str_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-thread_pool_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-triplet_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-unlock_guard_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-utf8_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-versioned_csv_file_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-watch_socket_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-watched_thread_unittest.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-bigint_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-boost_time_utils_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-buffer_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-chrono_time_utils_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-csv_file_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-dhcp_space_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-doubles_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-encode_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-fd_share_tests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-fd_tests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-filesystem_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-hash_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-io_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-labeled_value_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-memory_segment_common_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-memory_segment_local_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-multi_threading_mgr_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-optional_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-pid_file_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-range_utilities_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-readwrite_mutex_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-staged_value_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-state_model_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-stopwatch_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-str_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-thread_pool_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-triplet_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-unlock_guard_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-utf8_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-versioned_csv_file_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-watch_socket_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-watched_thread_unittest.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/util/tests/bigint_unittest.cc b/src/lib/util/tests/bigint_unittest.cc
new file mode 100644
index 0000000..bbd2c29
--- /dev/null
+++ b/src/lib/util/tests/bigint_unittest.cc
@@ -0,0 +1,160 @@
+// Copyright (C) 2023 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/gtest_utils.h>
+#include <util/bigints.h>
+
+#include <gtest/gtest.h>
+
+#include <limits>
+
+using namespace isc::util;
+
+using namespace std;
+
+namespace {
+
+// C++ doesn't allow very big integer literals, so that's why some tests on big
+// numbers might appear like they're unnecessarily circumventing a more obvious
+// test choice.
+
+// Checks that int128_t behaves like a signed integer should.
+TEST(BigintTest, int128) {
+ // Check addition with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(24, int128_t(16) + int128_t(8));
+ });
+
+ // Check subtraction with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(48, int128_t(64) - int128_t(16));
+ });
+
+ // Check multiplication with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(8, int128_t(2) * int128_t(4));
+ });
+
+ // Check division with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(16, int128_t(64) / int128_t(4));
+ });
+
+ // Check rounded division with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(16, int128_t(65) / int128_t(4));
+ });
+
+ // Check that INT128_MIN < INT64_MIN.
+ EXPECT_NO_THROW({
+ EXPECT_LT(numeric_limits<int128_t>::min(), numeric_limits<int64_t>::min());
+ });
+
+ // Check that UINT64_MAX < INT128_MAX.
+ EXPECT_NO_THROW({
+ EXPECT_LT(numeric_limits<uint64_t>::max(), numeric_limits<int128_t>::max());
+ });
+
+ // Check that int128_t is default-initialized to zero. Not a strict
+ // requirement by Kea, but something that the current implementation ensures.
+ int128_t i128;
+ EXPECT_NO_THROW({
+ EXPECT_EQ(0, i128);
+ });
+
+ // Shifting to the right beyond zero does not result in an underflow error.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(0, uint128_t(1) >> 2);
+ });
+
+ // Check that dividing by zero throws.
+ EXPECT_THROW_MSG(int128_t(1) / 0, overflow_error, "Division by zero.");
+
+ // Check that underflowing results in an error.
+ i128 = numeric_limits<int128_t>::min();
+ EXPECT_THROW_MSG(i128 - 1, overflow_error, "overflow in addition");
+ EXPECT_THROW_MSG(i128 + i128, overflow_error, "overflow in addition");
+
+ // Check that overflowing results in an error.
+ i128 = numeric_limits<int128_t>::max();
+ EXPECT_THROW_MSG(i128 + 1, overflow_error, "overflow in addition");
+ EXPECT_THROW_MSG(i128 + i128, overflow_error, "overflow in addition");
+ EXPECT_THROW_MSG(2 * i128, overflow_error, "overflow in multiplication");
+ EXPECT_THROW_MSG(i128 << 1, overflow_error, "Shift out of range");
+}
+
+// Checks that uint128_t behaves like an unsigned integer should.
+TEST(BigintTest, uint128) {
+ // Check addition with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(24, uint128_t(16) + uint128_t(8));
+ });
+
+ // Check subtraction with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(48, uint128_t(64) - uint128_t(16));
+ });
+
+ // Check multiplication with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(8, uint128_t(2) * uint128_t(4));
+ });
+
+ // Check division with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(16, uint128_t(64) / uint128_t(4));
+ });
+
+ // Check rounded division with small numbers.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(16, uint128_t(65) / uint128_t(4));
+ });
+
+ // Check that UINT128_MIN is 0.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(0, numeric_limits<uint128_t>::min());
+ });
+
+ // Check that UINT64_MAX < UINT128_MAX.
+ EXPECT_NO_THROW({
+ EXPECT_LT(numeric_limits<uint64_t>::max(), numeric_limits<int128_t>::max());
+ });
+
+ // Check that INT128_MAX < UINT128_MAX.
+ EXPECT_NO_THROW({
+ EXPECT_LT(numeric_limits<uint64_t>::max(), numeric_limits<int128_t>::max());
+ });
+
+ // Check that uint128_t is default-initialized to zero. Not a strict
+ // requirement by Kea, but something that the current implementation ensures.
+ uint128_t u128;
+ EXPECT_NO_THROW({
+ EXPECT_EQ(0, u128);
+ });
+
+ // Shifting to the right beyond zero does not result in an underflow error.
+ EXPECT_NO_THROW({
+ EXPECT_EQ(0, uint128_t(1) >> 2);
+ });
+
+ // Check that dividing by zero throws.
+ EXPECT_THROW_MSG(uint128_t(1) / 0, overflow_error, "Division by zero.");
+
+ // Check that underflowing results in an error.
+ u128 = numeric_limits<uint128_t>::min();
+ EXPECT_THROW_MSG(u128 - 1, range_error, "Subtraction resulted in a negative value, but the type is unsigned");
+
+ // Check that overflowing results in an error.
+ u128 = numeric_limits<uint128_t>::max();
+ EXPECT_THROW_MSG(u128 + 1, overflow_error, "overflow in addition");
+ EXPECT_THROW_MSG(u128 + u128, overflow_error, "overflow in addition");
+ EXPECT_THROW_MSG(2 * u128, overflow_error, "overflow in multiplication");
+ EXPECT_THROW_MSG(u128 << 1, overflow_error, "Shift out of range");
+}
+
+} // namespace
diff --git a/src/lib/util/tests/boost_time_utils_unittest.cc b/src/lib/util/tests/boost_time_utils_unittest.cc
new file mode 100644
index 0000000..643578e
--- /dev/null
+++ b/src/lib/util/tests/boost_time_utils_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright (C) 2015-2024 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 <util/boost_time_utils.h>
+
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace boost::posix_time;
+using namespace boost::gregorian;
+
+/// Check the ptimeToText() function returns a numeric month.
+/// Note durationToText() is called by ptimeToText() so is tested too.
+
+// The Posix time epoch is 1970
+TEST(BoostTimeUtilsTest, epoch) {
+ time_t tepoch = 0;
+ ptime pepoch = from_time_t(tepoch);
+
+ // We're going to loop through precision values starting with 0 through
+ // the max supported precision. Each pass should after the first, should
+ // add an additional level of precision: secs, secs/10, secs/100,
+ // secs/1000 and so on. The initial string has no fraction seconds.
+ std::string expected("1970-01-01 00:00:00");
+ std::string sepoch;
+ for (size_t precision = 0; precision <= MAX_FSECS_PRECISION; ++precision) {
+ if (precision == 1) {
+ // Adding fractional seconds so we need append a decimal point.
+ expected.push_back('.');
+ }
+
+ if (precision >= 1) {
+ // Adding an additional level of precision, append a zero.
+ expected.push_back('0');
+ }
+
+ // Now let's see if we get the correct precision in the text.
+ sepoch = ptimeToText(pepoch, precision);
+ EXPECT_EQ(expected, sepoch) << " test precision:" << precision;
+ }
+
+ // Expected string should have same precision as default, so
+ // test the default.
+ sepoch = ptimeToText(pepoch);
+ EXPECT_EQ(expected, sepoch);
+
+ // Now test a requested precision beyond default. We should
+ // get the default precision.
+ sepoch = ptimeToText(pepoch, MAX_FSECS_PRECISION + 1);
+ EXPECT_EQ(expected, sepoch);
+
+}
+
+// The 2015 Bastille day
+TEST(BoostTimeUtilsTest, bastilleDay) {
+ time_duration tdbast =
+ hours(12) + minutes(13) + seconds(14) + milliseconds(500);
+ ptime pbast(date(2015, Jul, 14), tdbast);
+
+ // We're going to loop through precision values starting with 0 through
+ // the max supported precision. Each pass should after the first, should
+ // add an additional level of precision: secs, secs/10, secs/100,
+ // secs/1000 and so on. The initial string has no fraction seconds.
+ std::string expected("2015-07-14 12:13:14");
+ std::string sbast;
+ for (size_t precision = 0; precision <= MAX_FSECS_PRECISION; ++precision) {
+ if (precision == 1) {
+ // Adding fractional seconds so we need append a decimal point
+ // and the digit 5 (i.e. 500 ms = .5 secs).
+ expected.push_back('.');
+ expected.push_back('5');
+ } else if (precision > 1) {
+ // Adding an additional level of precision, append a zero.
+ expected.push_back('0');
+ }
+
+ // Now let's see if we get the correct precision in the text.
+ sbast = ptimeToText(pbast, precision);
+ EXPECT_EQ(expected, sbast) << " test precision:" << precision;
+ }
+
+ // Expected string should have same precision as default, so
+ // test the default.
+ sbast = ptimeToText(pbast);
+ EXPECT_EQ(expected, sbast);
+
+ // Now test a requested precision beyond default. We should
+ // get the default precision.
+ sbast = ptimeToText(pbast, MAX_FSECS_PRECISION + 1);
+ EXPECT_EQ(expected, sbast);
+}
diff --git a/src/lib/util/tests/buffer_unittest.cc b/src/lib/util/tests/buffer_unittest.cc
new file mode 100644
index 0000000..1c98571
--- /dev/null
+++ b/src/lib/util/tests/buffer_unittest.cc
@@ -0,0 +1,312 @@
+// Copyright (C) 2009-2024 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 <exceptions/exceptions.h>
+#include <util/buffer.h>
+
+#ifdef EXPECT_DEATH
+#include <util/unittests/resource.h>
+#include <util/unittests/check_valgrind.h>
+#endif /* EXPECT_DEATH */
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::util;
+
+namespace {
+
+class BufferTest : public ::testing::Test {
+protected:
+ BufferTest() : ibuffer(testdata, sizeof(testdata)), obuffer(0),
+ expected_size(0) {
+ data16 = (2 << 8) | 3;
+ data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7;
+ memset(vdata, 0, sizeof(testdata));
+ }
+
+ InputBuffer ibuffer;
+ OutputBuffer obuffer;
+ static const uint8_t testdata[5];
+ uint8_t vdata[sizeof(testdata)];
+ size_t expected_size;
+ uint16_t data16;
+ uint32_t data32;
+ std::vector<uint8_t> datav;
+};
+
+const uint8_t BufferTest::testdata[5] = {1, 2, 3, 4, 5};
+
+TEST_F(BufferTest, inputBufferRead) {
+ EXPECT_EQ(5, ibuffer.getLength());
+ EXPECT_EQ(1, ibuffer.peekUint8());
+ EXPECT_EQ(0, ibuffer.getPosition());
+ EXPECT_EQ(1, ibuffer.readUint8());
+ EXPECT_EQ(1, ibuffer.getPosition());
+ data16 = ibuffer.peekUint16();
+ EXPECT_EQ(1, ibuffer.getPosition());
+ EXPECT_EQ(data16, ibuffer.readUint16());
+ EXPECT_EQ((2 << 8) | 3, data16);
+ EXPECT_EQ(3, ibuffer.getPosition());
+ ibuffer.setPosition(1);
+ EXPECT_EQ(1, ibuffer.getPosition());
+ data32 = ibuffer.peekUint32();
+ EXPECT_EQ(1, ibuffer.getPosition());
+ EXPECT_EQ(data32, ibuffer.readUint32());
+ EXPECT_EQ((2 << 24) | (3 << 16) | (4 << 8) | 5, data32);
+ ibuffer.setPosition(0);
+ memset(vdata, 0, sizeof(vdata));
+ ibuffer.peekData(vdata, sizeof(vdata));
+ EXPECT_EQ(0, memcmp(vdata, testdata, sizeof(testdata)));
+ EXPECT_EQ(0, ibuffer.getPosition());
+ memset(vdata, 0, sizeof(vdata));
+ ibuffer.readData(vdata, sizeof(vdata));
+ EXPECT_EQ(0, memcmp(vdata, testdata, sizeof(testdata)));
+ EXPECT_EQ(sizeof(vdata), ibuffer.getPosition());
+ ibuffer.setPosition(0);
+ datav.clear();
+ ibuffer.peekVector(datav, sizeof(vdata));
+ ASSERT_EQ(sizeof(vdata), datav.size());
+ EXPECT_EQ(0, memcmp(&vdata[0], testdata, sizeof(testdata)));
+ EXPECT_EQ(0, ibuffer.getPosition());
+ datav.clear();
+ ibuffer.readVector(datav, sizeof(vdata));
+ ASSERT_EQ(sizeof(vdata), datav.size());
+ EXPECT_EQ(0, memcmp(&vdata[0], testdata, sizeof(testdata)));
+ EXPECT_EQ(sizeof(vdata), ibuffer.getPosition());
+}
+
+TEST_F(BufferTest, inputBufferException) {
+ EXPECT_THROW(ibuffer.setPosition(6), isc::OutOfRange);
+
+ ibuffer.setPosition(sizeof(testdata));
+ EXPECT_THROW(ibuffer.peekUint8(), isc::OutOfRange);
+ EXPECT_THROW(ibuffer.readUint8(), isc::OutOfRange);
+
+ ibuffer.setPosition(sizeof(testdata) - 1);
+ EXPECT_THROW(ibuffer.peekUint16(), isc::OutOfRange);
+ EXPECT_THROW(ibuffer.readUint16(), isc::OutOfRange);
+
+ ibuffer.setPosition(sizeof(testdata) - 3);
+ EXPECT_THROW(ibuffer.peekUint32(), isc::OutOfRange);
+ EXPECT_THROW(ibuffer.readUint32(), isc::OutOfRange);
+
+ ibuffer.setPosition(sizeof(testdata) - 4);
+ EXPECT_THROW(ibuffer.peekData(vdata, sizeof(vdata)), isc::OutOfRange);
+ EXPECT_THROW(ibuffer.readData(vdata, sizeof(vdata)), isc::OutOfRange);
+ EXPECT_THROW(ibuffer.peekVector(datav, sizeof(vdata)), isc::OutOfRange);
+ EXPECT_THROW(ibuffer.readVector(datav, sizeof(vdata)), isc::OutOfRange);
+}
+
+TEST_F(BufferTest, outputBufferExtend) {
+ EXPECT_EQ(0, obuffer.getCapacity());
+ EXPECT_EQ(0, obuffer.getLength());
+ obuffer.writeUint8(10);
+ EXPECT_LT(0, obuffer.getCapacity());
+ EXPECT_EQ(1, obuffer.getLength());
+}
+
+TEST_F(BufferTest, outputBufferWrite) {
+ obuffer.writeUint8(1);
+ expected_size += sizeof(uint8_t);
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ const uint8_t* cp = obuffer.getData();
+ EXPECT_EQ(1, *cp);
+
+ obuffer.writeUint16(data16);
+ expected_size += sizeof(data16);
+ cp = obuffer.getData();
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ EXPECT_EQ(2, *(cp + 1));
+ EXPECT_EQ(3, *(cp + 2));
+
+ obuffer.writeUint32(data32);
+ expected_size += sizeof(data32);
+ cp = obuffer.getData();
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ EXPECT_EQ(4, *(cp + 3));
+ EXPECT_EQ(5, *(cp + 4));
+ EXPECT_EQ(6, *(cp + 5));
+ EXPECT_EQ(7, *(cp + 6));
+
+ obuffer.writeData(testdata, sizeof(testdata));
+ expected_size += sizeof(testdata);
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ cp = obuffer.getData();
+ EXPECT_EQ(0, memcmp(cp + 7, testdata, sizeof(testdata)));
+
+ datav = obuffer.getVector();
+ ASSERT_EQ(expected_size, datav.size());
+ std::vector<uint8_t> expected = { 1, 2, 3, 4, 5, 6, 7 };
+ expected.insert(expected.end(), testdata, testdata + sizeof(testdata));
+ ASSERT_EQ(expected_size, expected.size());
+ EXPECT_EQ(0, memcmp(&expected[0], &datav[0], expected_size));
+}
+
+TEST_F(BufferTest, outputBufferWriteAt) {
+ obuffer.writeUint32(data32);
+ expected_size += sizeof(data32);
+
+ // overwrite 2nd byte
+ obuffer.writeUint8At(4, 1);
+ EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change
+ const uint8_t* cp = obuffer.getData();
+ EXPECT_EQ(4, *(cp + 1));
+
+ // overwrite 2nd and 3rd bytes
+ obuffer.writeUint16At(data16, 1);
+ EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change
+ cp = obuffer.getData();
+ EXPECT_EQ(2, *(cp + 1));
+ EXPECT_EQ(3, *(cp + 2));
+
+ // overwrite 3rd and 4th bytes
+ obuffer.writeUint16At(data16, 2);
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ cp = obuffer.getData();
+ EXPECT_EQ(2, *(cp + 2));
+ EXPECT_EQ(3, *(cp + 3));
+
+ EXPECT_THROW(obuffer.writeUint8At(data16, 5), isc::OutOfRange);
+ EXPECT_THROW(obuffer.writeUint8At(data16, 4), isc::OutOfRange);
+ EXPECT_THROW(obuffer.writeUint16At(data16, 3), isc::OutOfRange);
+ EXPECT_THROW(obuffer.writeUint16At(data16, 4), isc::OutOfRange);
+ EXPECT_THROW(obuffer.writeUint16At(data16, 5), isc::OutOfRange);
+}
+
+TEST_F(BufferTest, outputBufferSkip) {
+ obuffer.skip(4);
+ EXPECT_EQ(4, obuffer.getLength());
+
+ obuffer.skip(2);
+ EXPECT_EQ(6, obuffer.getLength());
+}
+
+TEST_F(BufferTest, outputBufferTrim) {
+ obuffer.writeData(testdata, sizeof(testdata));
+ EXPECT_EQ(5, obuffer.getLength());
+
+ obuffer.trim(1);
+ EXPECT_EQ(4, obuffer.getLength());
+
+ obuffer.trim(2);
+ EXPECT_EQ(2, obuffer.getLength());
+
+ EXPECT_THROW(obuffer.trim(3), OutOfRange);
+}
+
+TEST_F(BufferTest, outputBufferReadAt) {
+ obuffer.writeData(testdata, sizeof(testdata));
+ for (size_t i = 0; i < sizeof(testdata); ++i) {
+ EXPECT_EQ(testdata[i], obuffer[i]);
+ }
+ EXPECT_THROW(obuffer[sizeof(testdata)], isc::OutOfRange);
+}
+
+TEST_F(BufferTest, outputBufferClear) {
+ obuffer.writeData(testdata, sizeof(testdata));
+ const uint8_t* cp = obuffer.getData();
+ obuffer.clear();
+ EXPECT_EQ(0, obuffer.getLength());
+ EXPECT_FALSE(obuffer.getData());
+}
+
+TEST_F(BufferTest, outputBufferCopy) {
+ EXPECT_NO_THROW({
+ obuffer.writeData(testdata, sizeof(testdata));
+
+ OutputBuffer copy(obuffer);
+ ASSERT_EQ(sizeof(testdata), copy.getLength());
+ ASSERT_NE(obuffer.getData(), copy.getData());
+ for (size_t i = 0; i < sizeof(testdata); ++i) {
+ EXPECT_EQ(testdata[i], copy[i]);
+ if (i + 1 < sizeof(testdata)) {
+ obuffer.writeUint16At(0, i);
+ }
+ EXPECT_EQ(testdata[i], copy[i]);
+ }
+ obuffer.clear();
+ ASSERT_EQ(sizeof(testdata), copy.getLength());
+ });
+}
+
+TEST_F(BufferTest, outputEmptyBufferCopy) {
+ EXPECT_NO_THROW({
+ OutputBuffer copy(obuffer);
+ ASSERT_EQ(0, copy.getLength());
+ EXPECT_FALSE(copy.getData());
+ });
+}
+
+TEST_F(BufferTest, outputBufferAssign) {
+ EXPECT_NO_THROW({
+ OutputBuffer another(0);
+ another.clear();
+ obuffer.writeData(testdata, sizeof(testdata));
+
+ another = obuffer;
+ ASSERT_EQ(sizeof(testdata), another.getLength());
+ ASSERT_NE(obuffer.getData(), another.getData());
+ for (size_t i = 0; i < sizeof(testdata); ++i) {
+ EXPECT_EQ(testdata[i], another[i]);
+ if (i + 1 < sizeof(testdata)) {
+ obuffer.writeUint16At(0, i);
+ }
+ EXPECT_EQ(testdata[i], another[i]);
+ }
+ obuffer.clear();
+ ASSERT_EQ(sizeof(testdata), another.getLength());
+ });
+}
+
+TEST_F(BufferTest, outputEmptyBufferAssign) {
+ OutputBuffer copy(0);
+ EXPECT_NO_THROW(copy = obuffer;);
+ EXPECT_EQ(0, copy.getLength());
+ EXPECT_EQ(0, copy.getData());
+}
+
+// Check assign to self doesn't break stuff
+TEST_F(BufferTest, outputBufferAssignSelf) {
+ EXPECT_NO_THROW(obuffer = obuffer);
+}
+
+TEST_F(BufferTest, inputBufferReadVectorChunks) {
+ std::vector<uint8_t> vec;
+
+ // check that vector can read the whole buffer
+ ibuffer.readVector(vec, 3);
+ EXPECT_EQ(3, vec.size());
+ EXPECT_EQ(0, memcmp(&vec[0], testdata, 3));
+ EXPECT_NO_THROW(ibuffer.readVector(vec, 2));
+ ASSERT_EQ(2, vec.size());
+ EXPECT_EQ(0, memcmp(&vec[0], &testdata[3], 2));
+}
+
+// Tests whether uint64 can be written properly.
+TEST_F(BufferTest, writeUint64) {
+ uint64_t val1 = 0x0102030405060708ul;
+ uint64_t val2 = 0xfffffffffffffffful;
+ uint8_t exp_val1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ uint8_t exp_val2[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ obuffer.writeUint64(val1);
+ ASSERT_EQ(sizeof(uint64_t), obuffer.getLength());
+ const uint8_t* cp = obuffer.getData();
+ EXPECT_TRUE(cp);
+ EXPECT_FALSE(memcmp(exp_val1, obuffer.getData(), sizeof(uint64_t)));
+
+ EXPECT_NO_THROW(obuffer.clear());
+ obuffer.writeUint64(val2);
+ ASSERT_EQ(sizeof(uint64_t), obuffer.getLength());
+ cp = obuffer.getData();
+ EXPECT_TRUE(cp);
+ EXPECT_FALSE(memcmp(exp_val2, obuffer.getData(), sizeof(uint64_t)));
+}
+
+}
diff --git a/src/lib/util/tests/chrono_time_utils_unittest.cc b/src/lib/util/tests/chrono_time_utils_unittest.cc
new file mode 100644
index 0000000..f94ef7b
--- /dev/null
+++ b/src/lib/util/tests/chrono_time_utils_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright (C) 2015-2024 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 <util/chrono_time_utils.h>
+
+#include <string.h>
+#include <time.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace std::chrono;
+using namespace isc::util;
+
+/// Check the clockToText() function returns a numeric month.
+TEST(ChronoTimeUtilsTest, epoch) {
+ // The system clock is a wall clock using the local time zone so
+ // the epoch is zero only at some places or of course if the
+ // system is in UTC...
+ struct tm epoch;
+ memset(&epoch, 0, sizeof(epoch));
+ epoch.tm_year = 70;
+ epoch.tm_mday = 1;
+ epoch.tm_isdst = -1;
+ time_t tepoch = mktime(&epoch);
+ system_clock::time_point pepoch = system_clock::from_time_t(tepoch);
+
+ // We're going to loop through precision values starting with 0 through
+ // the max supported precision. Each pass should after the first, should
+ // add an additional level of precision: secs, secs/10, secs/100,
+ // secs/1000 and so on. The initial string has no fraction seconds.
+ std::string expected("1970-01-01 00:00:00");
+ std::string sepoch;
+ for (size_t precision = 0; precision <= MAX_FSECS_PRECISION; ++precision) {
+ if (precision == 1) {
+ // Adding fractional seconds so we need append a decimal point.
+ expected.push_back('.');
+ }
+
+ if (precision >= 1) {
+ // Adding an additional level of precision, append a zero.
+ expected.push_back('0');
+ }
+
+ // Now let's see if we get the correct precision in the text.
+ sepoch = clockToText(pepoch, precision);
+ EXPECT_EQ(expected, sepoch) << " test precision:" << precision;
+ }
+
+ // Expected string should have same precision as default, so
+ // test the default.
+ sepoch = clockToText(pepoch);
+ EXPECT_EQ(expected, sepoch);
+
+ // Now test a requested precision beyond default. We should
+ // get the default precision.
+ sepoch = clockToText(pepoch, MAX_FSECS_PRECISION + 1);
+ EXPECT_EQ(expected, sepoch);
+
+}
+
+/// Check the durationToText() works as expected.
+/// Note durationToText() is not called by clockToText().
+TEST(ChronoTimeUtilsTest, duration) {
+ system_clock::duration p123 = hours(1) + minutes(2) + seconds(3);
+
+ // We're going to loop through precision values starting with 0 through
+ // the max supported precision. Each pass should after the first, should
+ // add an additional level of precision: secs, secs/10, secs/100,
+ // secs/1000 and so on. The initial string has no fraction seconds.
+ std::string expected("01:02:03");
+ std::string s123;
+ for (size_t precision = 0; precision <= MAX_FSECS_PRECISION; ++precision) {
+ if (precision == 1) {
+ // Adding fractional seconds so we need append a decimal point.
+ expected.push_back('.');
+ }
+
+ if (precision >= 1) {
+ // Adding an additional level of precision, append a zero.
+ expected.push_back('0');
+ }
+
+ // Now let's see if we get the correct precision in the text.
+ s123 = durationToText(p123, precision);
+ EXPECT_EQ(expected, s123) << " test precision:" << precision;
+ }
+
+ // Expected string should have same precision as default, so
+ // test the default.
+ s123 = durationToText(p123);
+ EXPECT_EQ(expected, s123);
+
+ // Now test a requested precision beyond default. We should
+ // get the default precision.
+ s123 = durationToText(p123, MAX_FSECS_PRECISION + 1);
+ EXPECT_EQ(expected, s123);
+}
+
+// The 2015 Bastille day
+TEST(ChronoTimeUtilsTest, bastilleDay) {
+ struct tm tm;
+ tm.tm_year = 2015 - 1900;
+ tm.tm_mon = 7 - 1;
+ tm.tm_mday = 14;
+ tm.tm_hour = 12;
+ tm.tm_min = 13;
+ tm.tm_sec = 14;
+ tm.tm_isdst = -1;
+ time_t tbast = mktime(&tm);
+ system_clock::time_point tpbast = system_clock::from_time_t(tbast);
+ tpbast += milliseconds(500);
+
+ // We're going to loop through precision values starting with 0 through
+ // the max supported precision. Each pass should after the first, should
+ // add an additional level of precision: secs, secs/10, secs/100,
+ // secs/1000 and so on. The initial string has no fraction seconds.
+ std::string expected("2015-07-14 12:13:14");
+ std::string sbast;
+ for (size_t precision = 0; precision <= MAX_FSECS_PRECISION; ++precision) {
+ if (precision == 1) {
+ // Adding fractional seconds so we need append a decimal point
+ // and the digit 5 (i.e. 500 ms = .5 secs).
+ expected.push_back('.');
+ expected.push_back('5');
+ } else if (precision > 1) {
+ // Adding an additional level of precision, append a zero.
+ expected.push_back('0');
+ }
+
+ // Now let's see if we get the correct precision in the text.
+ sbast = clockToText(tpbast, precision);
+ EXPECT_EQ(expected, sbast) << " test precision:" << precision;
+ }
+
+ // Expected string should have same precision as default, so
+ // test the default.
+ sbast = clockToText(tpbast);
+ EXPECT_EQ(expected, sbast);
+
+ // Now test a requested precision beyond default. We should
+ // get the default precision.
+ sbast = clockToText(tpbast, MAX_FSECS_PRECISION + 1);
+ EXPECT_EQ(expected, sbast);
+}
+
+// Try steady clock duration.
+TEST(ChronoTimeUtilsTest, steadyClock) {
+ steady_clock::duration p12345 = hours(1) + minutes(2) + seconds(3) +
+ milliseconds(4) + microseconds(5);
+ std::string expected("01:02:03.004005");
+ std::string s12345 = durationToText(p12345, 6);
+ EXPECT_EQ(expected, s12345);
+}
diff --git a/src/lib/util/tests/csv_file_unittest.cc b/src/lib/util/tests/csv_file_unittest.cc
new file mode 100644
index 0000000..f55ec55
--- /dev/null
+++ b/src/lib/util/tests/csv_file_unittest.cc
@@ -0,0 +1,700 @@
+// Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <util/csv_file.h>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+namespace {
+
+using namespace isc::util;
+
+// This test exercises escaping and unescaping of characters.
+TEST(CSVRowTest, escapeUnescape) {
+ std::string orig(",FO^O\\,B?,AR,");
+
+ // We'll escape commas, question marks, and carets.
+ std::string escaped = CSVRow::escapeCharacters(orig, ",?^");
+ EXPECT_EQ ("&#x2cFO&#x5eO\\&#x2cB&#x3f&#x2cAR&#x2c", escaped);
+
+ // Now make sure we can unescape it correctly.
+ std::string unescaped = CSVRow::unescapeCharacters(escaped);
+ EXPECT_EQ (orig, unescaped);
+
+ // Make sure that an incident occurrence of just the escape tag
+ // is left intact.
+ orig = ("no&#xescape");
+ escaped = CSVRow::escapeCharacters(orig, ",");
+ unescaped = CSVRow::unescapeCharacters(orig);
+ EXPECT_EQ (orig, unescaped);
+
+ // Make sure that an incidental occurrence of a valid
+ // escape tag sequence left intact.
+ orig = ("no&#x2cescape");
+ escaped = CSVRow::escapeCharacters(orig, ",");
+ unescaped = CSVRow::unescapeCharacters(escaped);
+ EXPECT_EQ (orig, unescaped);
+}
+
+// This test checks that the single data row is parsed.
+TEST(CSVRow, parse) {
+ CSVRow row0("foo,bar,foo-bar");
+ ASSERT_EQ(3, row0.getValuesCount());
+ EXPECT_EQ("foo", row0.readAt(0));
+ EXPECT_EQ("bar", row0.readAt(1));
+ EXPECT_EQ("foo-bar", row0.readAt(2));
+
+ row0.parse("bar,,foo-bar");
+ ASSERT_EQ(3, row0.getValuesCount());
+ EXPECT_EQ("bar", row0.readAt(0));
+ EXPECT_TRUE(row0.readAt(1).empty());
+ EXPECT_EQ("foo-bar", row0.readAt(2));
+
+ row0.parse("bar,foo&#x2c-bar");
+ ASSERT_EQ(2, row0.getValuesCount());
+ EXPECT_EQ("bar", row0.readAt(0));
+ // Read the second column as-is and escaped
+ EXPECT_EQ("foo&#x2c-bar", row0.readAt(1));
+ EXPECT_EQ("foo,-bar", row0.readAtEscaped(1));
+
+ CSVRow row1("foo-bar|foo|bar|", '|');
+ ASSERT_EQ(4, row1.getValuesCount());
+ EXPECT_EQ("foo-bar", row1.readAt(0));
+ EXPECT_EQ("foo", row1.readAt(1));
+ EXPECT_EQ("bar", row1.readAt(2));
+ EXPECT_TRUE(row1.readAt(3).empty());
+
+ row1.parse("");
+ ASSERT_EQ(1, row1.getValuesCount());
+ EXPECT_TRUE(row1.readAt(0).empty());
+}
+
+// Verifies that empty columns are handled correctly.
+TEST(CSVRow, emptyColumns) {
+ // Should get four columns, all blank except column the second one.
+ CSVRow row(",one,,");
+ ASSERT_EQ(4, row.getValuesCount());
+ EXPECT_EQ("", row.readAt(0));
+ EXPECT_EQ("one", row.readAt(1));
+ EXPECT_EQ("", row.readAt(2));
+ EXPECT_EQ("", row.readAt(3));
+}
+
+// Verifies that empty columns are handled correctly.
+TEST(CSVRow, oneColumn) {
+ // Should get one column
+ CSVRow row("zero");
+ ASSERT_EQ(1, row.getValuesCount());
+ EXPECT_EQ("zero", row.readAt(0));
+}
+
+// This test checks that the text representation of the CSV row
+// is created correctly.
+TEST(CSVRow, render) {
+ CSVRow row0(3);
+ row0.writeAt(0, "foo");
+ row0.writeAt(1, "foo-bar");
+ row0.writeAt(2, "bar");
+
+ std::string text;
+ ASSERT_NO_THROW(text = row0.render());
+ EXPECT_EQ(text, "foo,foo-bar,bar");
+
+ CSVRow row1(4, ';');
+ row1.writeAt(0, "foo");
+ row1.writeAt(2, "bar");
+ row1.writeAt(3, 10);
+
+ ASSERT_NO_THROW(text = row1.render());
+ EXPECT_EQ(text, "foo;;bar;10");
+
+ CSVRow row2(0);
+ ASSERT_NO_THROW(text = row2.render());
+ EXPECT_TRUE(text.empty());
+}
+
+// This test checks that the data values can be set for the CSV row.
+TEST(CSVRow, writeAt) {
+ CSVRow row(4);
+ row.writeAt(0, 10);
+ row.writeAt(1, "foo");
+ row.writeAt(2, "bar");
+ row.writeAtEscaped(3, "bar,one,two");
+
+ EXPECT_EQ("10", row.readAt(0));
+ EXPECT_EQ("foo", row.readAt(1));
+ EXPECT_EQ("bar", row.readAt(2));
+ // Read third column as-is and unescaped
+ EXPECT_EQ("bar&#x2cone&#x2ctwo", row.readAt(3));
+ EXPECT_EQ("bar,one,two", row.readAtEscaped(3));
+
+ EXPECT_THROW(row.writeAt(4, 20), CSVFileError);
+ EXPECT_THROW(row.writeAt(4, "foo"), CSVFileError);
+}
+
+// Checks whether writeAt() and append() can be mixed together.
+TEST(CSVRow, append) {
+ CSVRow row(3);
+
+ EXPECT_EQ(3, row.getValuesCount());
+
+ row.writeAt(0, "alpha");
+ ASSERT_NO_THROW(row.append("delta"));
+ EXPECT_EQ(4, row.getValuesCount());
+ row.writeAt(1, "beta");
+ row.writeAt(2, "gamma");
+ ASSERT_NO_THROW(row.append("epsilon"));
+ EXPECT_EQ(5, row.getValuesCount());
+
+ std::string text;
+ ASSERT_NO_THROW(text = row.render());
+ EXPECT_EQ("alpha,beta,gamma,delta,epsilon", text);
+}
+
+// This test checks that a row can be trimmed of
+// a given number of elements
+TEST(CSVRow, trim) {
+ CSVRow row("zero,one,two,three,four");
+ ASSERT_EQ(5, row.getValuesCount());
+ EXPECT_EQ("zero", row.readAt(0));
+ EXPECT_EQ("one", row.readAt(1));
+ EXPECT_EQ("two", row.readAt(2));
+ EXPECT_EQ("three", row.readAt(3));
+ EXPECT_EQ("four", row.readAt(4));
+
+ ASSERT_THROW(row.trim(10), CSVFileError);
+
+ // Verify that we can erase just one
+ ASSERT_NO_THROW(row.trim(1));
+ ASSERT_EQ(4, row.getValuesCount());
+ EXPECT_EQ("zero", row.readAt(0));
+ EXPECT_EQ("one", row.readAt(1));
+ EXPECT_EQ("two", row.readAt(2));
+ EXPECT_EQ("three", row.readAt(3));
+
+ // Verify we can trim more than one
+ ASSERT_NO_THROW(row.trim(2));
+ ASSERT_EQ(2, row.getValuesCount());
+ EXPECT_EQ("zero", row.readAt(0));
+ EXPECT_EQ("one", row.readAt(1));
+}
+
+/// @brief Test fixture class for testing operations on CSV file.
+///
+/// It implements basic operations on files, such as reading writing
+/// file removal and checking presence of the file. This is used by
+/// unit tests to verify correctness of the file created by the
+/// CSVFile class.
+class CSVFileTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Sets the path to the CSV file used throughout the tests.
+ /// The name of the file is test.csv and it is located in the
+ /// current build folder.
+ ///
+ /// It also deletes any dangling files after previous tests.
+ CSVFileTest();
+
+ /// @brief Destructor.
+ ///
+ /// Deletes the test CSV file if any.
+ virtual ~CSVFileTest();
+
+ /// @brief Prepends the absolute path to the file specified
+ /// as an argument.
+ ///
+ /// @param filename Name of the file.
+ /// @return Absolute path to the test file.
+ static std::string absolutePath(const std::string& filename);
+
+ /// @brief Check if test file exists on disk.
+ bool exists() const;
+
+ /// @brief Reads whole CSV file.
+ ///
+ /// @return Contents of the file.
+ std::string readFile() const;
+
+ /// @brief Removes existing file (if any).
+ int removeFile() const;
+
+ /// @brief Creates file with contents.
+ ///
+ /// @param contents Contents of the file.
+ void writeFile(const std::string& contents) const;
+
+ /// @brief Absolute path to the file used in the tests.
+ std::string testfile_;
+
+};
+
+CSVFileTest::CSVFileTest()
+ : testfile_(absolutePath("test.csv")) {
+ static_cast<void>(removeFile());
+}
+
+CSVFileTest::~CSVFileTest() {
+ static_cast<void>(removeFile());
+}
+
+std::string
+CSVFileTest::absolutePath(const std::string& filename) {
+ std::ostringstream s;
+ s << TEST_DATA_BUILDDIR << "/" << filename;
+ return (s.str());
+}
+
+bool
+CSVFileTest::exists() const {
+ std::ifstream fs(testfile_.c_str());
+ bool ok = fs.good();
+ fs.close();
+ return (ok);
+}
+
+std::string
+CSVFileTest::readFile() const {
+ std::ifstream fs(testfile_.c_str());
+ if (!fs.is_open()) {
+ return ("");
+ }
+ std::string contents((std::istreambuf_iterator<char>(fs)),
+ std::istreambuf_iterator<char>());
+ fs.close();
+ return (contents);
+}
+
+int
+CSVFileTest::removeFile() const {
+ return (remove(testfile_.c_str()));
+}
+
+void
+CSVFileTest::writeFile(const std::string& contents) const {
+ std::ofstream fs(testfile_.c_str(), std::ofstream::out);
+ if (fs.is_open()) {
+ fs << contents;
+ fs.close();
+ }
+}
+
+// This test checks that the function which is used to add columns of the
+// CSV file works as expected.
+TEST_F(CSVFileTest, addColumn) {
+ boost::scoped_ptr<CSVFile> csv(new CSVFile(testfile_));
+ // Add two columns.
+ ASSERT_NO_THROW(csv->addColumn("animal"));
+ ASSERT_NO_THROW(csv->addColumn("color"));
+ // Make sure we can't add duplicates.
+ EXPECT_THROW(csv->addColumn("animal"), CSVFileError);
+ EXPECT_THROW(csv->addColumn("color"), CSVFileError);
+ // But we should still be able to add unique columns.
+ EXPECT_NO_THROW(csv->addColumn("age"));
+ EXPECT_NO_THROW(csv->addColumn("comments"));
+ // Assert that the file is opened, because the rest of the test relies
+ // on this.
+ ASSERT_NO_THROW(csv->recreate());
+ ASSERT_TRUE(exists());
+
+ // Make sure we can't add columns (even unique) when the file is open.
+ ASSERT_THROW(csv->addColumn("zoo"), CSVFileError);
+ // Close the file.
+ ASSERT_NO_THROW(csv->close());
+ // And check that now it is possible to add the column.
+ EXPECT_NO_THROW(csv->addColumn("zoo"));
+}
+
+// This test checks that the appropriate file name is initialized.
+TEST_F(CSVFileTest, getFilename) {
+ CSVFile csv(testfile_);
+ EXPECT_EQ(testfile_, csv.getFilename());
+}
+
+// This test checks that the file can be opened, its whole content is
+// parsed correctly and data may be appended. It also checks that empty
+// row is returned when EOF is reached.
+TEST_F(CSVFileTest, openReadAllWrite) {
+ // Create a new CSV file that contains a header and two data rows.
+ writeFile("animal,age,color\n"
+ "cat,10,white\n"
+ "lion,15,yellow\n");
+
+ // Open this file and check that the header is parsed.
+ boost::scoped_ptr<CSVFile> csv(new CSVFile(testfile_));
+ ASSERT_NO_THROW(csv->open());
+ ASSERT_EQ(3, csv->getColumnCount());
+ EXPECT_EQ("animal", csv->getColumnName(0));
+ EXPECT_EQ("age", csv->getColumnName(1));
+ EXPECT_EQ("color", csv->getColumnName(2));
+
+ // Read first row.
+ CSVRow row;
+ ASSERT_TRUE(csv->next(row));
+ ASSERT_EQ(3, row.getValuesCount());
+ EXPECT_EQ("cat", row.readAt(0));
+ EXPECT_EQ("10", row.readAt(1));
+ EXPECT_EQ("white", row.readAt(2));
+
+ // Read second row.
+ ASSERT_TRUE(csv->next(row));
+ ASSERT_EQ(3, row.getValuesCount());
+ EXPECT_EQ("lion", row.readAt(0));
+ EXPECT_EQ("15", row.readAt(1));
+ EXPECT_EQ("yellow", row.readAt(2));
+
+ // There is no 3rd row, so the empty one should be returned.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ(CSVFile::EMPTY_ROW(), row);
+
+ // It should be fine to read again, but again empty row should be returned.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ(CSVFile::EMPTY_ROW(), row);
+
+ // Now, let's try to append something to this file.
+ CSVRow row_write(3);
+ row_write.writeAt(0, "dog");
+ row_write.writeAt(1, 2);
+ row_write.writeAt(2, "blue");
+ ASSERT_NO_THROW(csv->append(row_write));
+
+ // Close the file.
+ ASSERT_NO_THROW(csv->flush());
+ csv->close();
+
+ // Check the file contents are correct.
+ EXPECT_EQ("animal,age,color\n"
+ "cat,10,white\n"
+ "lion,15,yellow\n"
+ "dog,2,blue\n",
+ readFile());
+
+ // Any attempt to read from the file or write to it should now fail.
+ EXPECT_FALSE(csv->next(row));
+ EXPECT_THROW(csv->append(row_write), CSVFileError);
+
+ CSVRow row_write2(3);
+ row_write2.writeAt(0, "bird");
+ row_write2.writeAt(1, 3);
+ row_write2.writeAt(2, "purple");
+
+ // Reopen the file, seek to the end of file so as we can append
+ // some more data.
+ ASSERT_NO_THROW(csv->open(true));
+ // The file pointer should be at the end of file, so an attempt
+ // to read should result in an empty row.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ(CSVFile::EMPTY_ROW(), row);
+ // We should be able to append new data.
+ ASSERT_NO_THROW(csv->append(row_write2));
+ ASSERT_NO_THROW(csv->flush());
+ csv->close();
+ // Check that new data has been appended.
+ EXPECT_EQ("animal,age,color\n"
+ "cat,10,white\n"
+ "lion,15,yellow\n"
+ "dog,2,blue\n"
+ "bird,3,purple\n",
+ readFile());
+}
+
+// This test checks that contents may be appended to a file which hasn't
+// been fully parsed/read.
+TEST_F(CSVFileTest, openReadPartialWrite) {
+ // Create a CSV file with two rows in it.
+ writeFile("animal,age,color\n"
+ "cat,10,white\n"
+ "lion,15,yellow\n");
+
+ // Open this file.
+ boost::scoped_ptr<CSVFile> csv(new CSVFile(testfile_));
+ ASSERT_NO_THROW(csv->open());
+
+ // Read the first row.
+ CSVRow row0(0);
+ ASSERT_NO_THROW(csv->next(row0));
+ ASSERT_EQ(3, row0.getValuesCount());
+ EXPECT_EQ("cat", row0.readAt(0));
+ EXPECT_EQ("10", row0.readAt(1));
+ EXPECT_EQ("white", row0.readAt(2));
+
+ // There is still second row to be read. But, it should be possible to
+ // skip reading it and append new row to the end of file.
+ CSVRow row_write(3);
+ row_write.writeAt(0, "dog");
+ row_write.writeAt(1, 2);
+ row_write.writeAt(2, "blue");
+ ASSERT_NO_THROW(csv->append(row_write));
+
+ // At this point, the file pointer is at the end of file, so reading
+ // should return empty row.
+ CSVRow row1(0);
+ ASSERT_NO_THROW(csv->next(row1));
+ EXPECT_EQ(CSVFile::EMPTY_ROW(), row1);
+
+ // Close the file.
+ ASSERT_NO_THROW(csv->flush());
+ csv->close();
+
+ // Check that there are two initial lines and one new there.
+ EXPECT_EQ("animal,age,color\n"
+ "cat,10,white\n"
+ "lion,15,yellow\n"
+ "dog,2,blue\n",
+ readFile());
+
+}
+
+// This test checks that the new CSV file is created and header
+// is written to it. It also checks that data rows can be
+// appended to it.
+TEST_F(CSVFileTest, recreate) {
+ boost::scoped_ptr<CSVFile> csv(new CSVFile(testfile_));
+ csv->addColumn("animal");
+ csv->addColumn("color");
+ csv->addColumn("age");
+ csv->addColumn("comments");
+ ASSERT_NO_THROW(csv->recreate());
+ ASSERT_TRUE(exists());
+
+ CSVRow row0(4);
+ row0.writeAt(0, "dog");
+ row0.writeAt(1, "grey");
+ row0.writeAt(2, 3);
+ row0.writeAt(3, "nice one");
+ ASSERT_NO_THROW(csv->append(row0));
+
+ CSVRow row1(4);
+ row1.writeAt(0, "cat");
+ row1.writeAt(1, "black");
+ row1.writeAt(2, 2);
+ ASSERT_NO_THROW(csv->append(row1));
+
+ ASSERT_NO_THROW(csv->flush());
+ csv->close();
+
+ EXPECT_EQ("animal,color,age,comments\n"
+ "dog,grey,3,nice one\n"
+ "cat,black,2,\n",
+ readFile());
+}
+
+// This test checks that the error is reported when the size of the row being
+// read doesn't match the number of columns of the CSV file.
+TEST_F(CSVFileTest, validate) {
+ // Create CSV file with 2 invalid rows in it: one too long, one too short.
+ // Apart from that, there are two valid columns that should be read
+ // successfully.
+ writeFile("animal,age,color\n"
+ "cat,10,white\n"
+ "lion,15,yellow,black\n"
+ "dog,3,green\n"
+ "elephant,11\n");
+
+ boost::scoped_ptr<CSVFile> csv(new CSVFile(testfile_));
+ ASSERT_NO_THROW(csv->open());
+ // First row is correct.
+ CSVRow row0;
+ ASSERT_TRUE(csv->next(row0));
+ EXPECT_EQ("cat", row0.readAt(0));
+ EXPECT_EQ("10", row0.readAt(1));
+ EXPECT_EQ("white", row0.readAt(2));
+ EXPECT_EQ("success", csv->getReadMsg());
+ // This row is too long.
+ CSVRow row1;
+ EXPECT_FALSE(csv->next(row1));
+ EXPECT_NE("success", csv->getReadMsg());
+ // This row is correct.
+ CSVRow row2;
+ ASSERT_TRUE(csv->next(row2));
+ EXPECT_EQ("dog", row2.readAt(0));
+ EXPECT_EQ("3", row2.readAt(1));
+ EXPECT_EQ("green", row2.readAt(2));
+ EXPECT_EQ("success", csv->getReadMsg());
+ // This row is too short.
+ CSVRow row3;
+ EXPECT_FALSE(csv->next(row3));
+ EXPECT_NE("success", csv->getReadMsg());
+}
+
+// Test test checks that exception is thrown when the header of the CSV file
+// parsed, doesn't match the columns specified.
+TEST_F(CSVFileTest, validateHeader) {
+ // Create CSV file with 3 columns.
+ writeFile("animal,age,color\n"
+ "cat,10,white\n"
+ "lion,15,yellow,black\n");
+
+ // Invalid order of columns.
+ boost::scoped_ptr<CSVFile> csv(new CSVFile(testfile_));
+ csv->addColumn("color");
+ csv->addColumn("animal");
+ csv->addColumn("age");
+ EXPECT_THROW(csv->open(), CSVFileError);
+
+ // Too many columns.
+ csv.reset(new CSVFile(testfile_));
+ csv->addColumn("animal");
+ csv->addColumn("age");
+ csv->addColumn("color");
+ csv->addColumn("notes");
+ EXPECT_THROW(csv->open(), CSVFileError);
+
+ // Too few columns.
+ csv.reset(new CSVFile(testfile_));
+ csv->addColumn("animal");
+ csv->addColumn("age");
+ EXPECT_THROW(csv->open(), CSVFileError);
+}
+
+// This test checks that the exists method of the CSVFile class properly
+// checks that the file exists.
+TEST_F(CSVFileTest, exists) {
+ // Create a new CSV file that contains a header and two data rows.
+ writeFile("animal,age,color\n"
+ "cat,10,white\n"
+ "lion,15,yellow\n");
+
+ boost::scoped_ptr<CSVFile> csv(new CSVFile(testfile_));
+ // The CSVFile class should return true even if the file hasn't been
+ // opened.
+ EXPECT_TRUE(csv->exists());
+ // Now open the file and make sure it still returns true.
+ ASSERT_NO_THROW(csv->open());
+ EXPECT_TRUE(csv->exists());
+
+ // Close the file and remove it.
+ csv->close();
+ EXPECT_EQ(0, removeFile());
+
+ // The file should not exist.
+ EXPECT_FALSE(csv->exists());
+}
+
+// Check that a single header without a trailing blank line can be parsed.
+TEST_F(CSVFileTest, parseHeaderWithoutTrailingBlankLine) {
+ // Create a new CSV file that only contains a header without a new line.
+ writeFile("animal,age,color");
+
+ // Open this file and check that the header is parsed.
+ CSVFile csv(testfile_);
+ ASSERT_NO_THROW(csv.open());
+ ASSERT_EQ(3, csv.getColumnCount());
+ EXPECT_EQ("animal", csv.getColumnName(0));
+ EXPECT_EQ("age", csv.getColumnName(1));
+ EXPECT_EQ("color", csv.getColumnName(2));
+
+ // Attempt to read the next row which doesn't exist.
+ CSVRow row;
+ ASSERT_TRUE(csv.next(row));
+ EXPECT_EQ(CSVFile::EMPTY_ROW(), row);
+
+ // Close the file.
+ csv.close();
+}
+
+// Check that content without a trailing blank line can be parsed.
+TEST_F(CSVFileTest, parseContentWithoutTrailingBlankLine) {
+ // Now create a new CSV file that contains header plus data, but the last
+ // line is missing a new line.
+ writeFile("animal,age,color\n"
+ "cat,4,white\n"
+ "lion,8,yellow");
+
+ // Open this file and check that the header is parsed.
+ CSVFile csv(testfile_);
+ ASSERT_NO_THROW(csv.open());
+ ASSERT_EQ(3, csv.getColumnCount());
+ EXPECT_EQ("animal", csv.getColumnName(0));
+ EXPECT_EQ("age", csv.getColumnName(1));
+ EXPECT_EQ("color", csv.getColumnName(2));
+
+ // Check the first data row.
+ CSVRow row;
+ ASSERT_TRUE(csv.next(row));
+ EXPECT_EQ("cat", row.readAt(0));
+ EXPECT_EQ("4", row.readAt(1));
+ EXPECT_EQ("white", row.readAt(2));
+ EXPECT_EQ("success", csv.getReadMsg());
+
+ // Check the second data row.
+ ASSERT_TRUE(csv.next(row));
+ EXPECT_EQ("lion", row.readAt(0));
+ EXPECT_EQ("8", row.readAt(1));
+ EXPECT_EQ("yellow", row.readAt(2));
+ EXPECT_EQ("success", csv.getReadMsg());
+
+ // Attempt to read the next row which doesn't exist.
+ ASSERT_TRUE(csv.next(row));
+ EXPECT_EQ(CSVFile::EMPTY_ROW(), row);
+
+ // Close the file.
+ csv.close();
+}
+
+// Check that blank lines are skipped when reading from a file.
+TEST_F(CSVFileTest, parseContentWithBlankLines) {
+ for (char const* const& content : {
+ // Single intermediary blank line
+ "animal,age,color\n"
+ "cat,4,white\n"
+ "\n"
+ "lion,8,yellow\n",
+
+ // Blank lines all over
+ "\n"
+ "\n"
+ "animal,age,color\n"
+ "\n"
+ "\n"
+ "cat,4,white\n"
+ "\n"
+ "\n"
+ "lion,8,yellow\n"
+ "\n"
+ "\n",
+ }) {
+ // Create a new CSV file.
+ writeFile(content);
+
+ // Open this file and check that the header is parsed.
+ CSVFile csv(testfile_);
+ ASSERT_NO_THROW(csv.open());
+ ASSERT_EQ(3, csv.getColumnCount());
+ EXPECT_EQ("animal", csv.getColumnName(0));
+ EXPECT_EQ("age", csv.getColumnName(1));
+ EXPECT_EQ("color", csv.getColumnName(2));
+
+ // Check the first data row.
+ CSVRow row;
+ ASSERT_TRUE(csv.next(row));
+ EXPECT_EQ("cat", row.readAt(0));
+ EXPECT_EQ("4", row.readAt(1));
+ EXPECT_EQ("white", row.readAt(2));
+ EXPECT_EQ("success", csv.getReadMsg());
+
+ // Check the second non-blank data row.
+ ASSERT_TRUE(csv.next(row));
+ EXPECT_EQ("lion", row.readAt(0));
+ EXPECT_EQ("8", row.readAt(1));
+ EXPECT_EQ("yellow", row.readAt(2));
+ EXPECT_EQ("success", csv.getReadMsg());
+
+ // Attempt to read the next row which doesn't exist.
+ ASSERT_TRUE(csv.next(row));
+ EXPECT_EQ(CSVFile::EMPTY_ROW(), row);
+
+ // Close the file.
+ csv.close();
+ }
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/util/tests/dhcp_space_unittest.cc b/src/lib/util/tests/dhcp_space_unittest.cc
new file mode 100644
index 0000000..e5269c4
--- /dev/null
+++ b/src/lib/util/tests/dhcp_space_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright (C) 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 <util/dhcp_space.h>
+
+#include <cstring>
+
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace isc::util;
+
+TEST(DhcpSpace, cString) {
+ EXPECT_EQ(std::strcmp(cStringDhcpSpace<DHCPv4>(), "4"), 0);
+ EXPECT_EQ(std::strcmp(cStringDhcpSpace<DHCPv6>(), "6"), 0);
+}
+
+TEST(DhcpSpace, format) {
+ EXPECT_EQ(formatDhcpSpace<DHCPv4>("dhcp{}"), "dhcp4");
+ EXPECT_EQ(formatDhcpSpace<DHCPv6>("dhcp{}"), "dhcp6");
+
+ EXPECT_EQ(formatDhcpSpace<DHCPv4>("Dhcp{}.subnet{}"), "Dhcp4.subnet4");
+ EXPECT_EQ(formatDhcpSpace<DHCPv6>("Dhcp{}.subnet{}"), "Dhcp6.subnet6");
+}
+
+} // namespace
diff --git a/src/lib/util/tests/doubles_unittest.cc b/src/lib/util/tests/doubles_unittest.cc
new file mode 100644
index 0000000..1c17f90
--- /dev/null
+++ b/src/lib/util/tests/doubles_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright (C) 2019,2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <util/doubles.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util;
+
+namespace {
+
+// Exercises isc::util::areDoublesEquivalent().
+TEST(Doubles, areDoublesEquivalent) {
+ std::vector<uint8_t> data;
+
+ // Default tolerance is 0.000001
+ EXPECT_TRUE(areDoublesEquivalent( 1.0000000, 1.0000005));
+ EXPECT_FALSE(areDoublesEquivalent(1.0000000, 1.000005));
+
+ // Check custom tolerance.
+ EXPECT_TRUE(areDoublesEquivalent( 1.000, 1.005, 0.01));
+ EXPECT_FALSE(areDoublesEquivalent(1.000, 1.005, 0.001));
+}
+
+}
diff --git a/src/lib/util/tests/encode_unittest.cc b/src/lib/util/tests/encode_unittest.cc
new file mode 100644
index 0000000..c39beb6
--- /dev/null
+++ b/src/lib/util/tests/encode_unittest.cc
@@ -0,0 +1,395 @@
+// Copyright (C) 2024 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 <exceptions/exceptions.h>
+#include <util/encode/encode.h>
+#include <testutils/gtest_utils.h>
+
+#include <gtest/gtest.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <vector>
+#include <functional>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::encode;
+
+namespace {
+
+/// @brief Defines a pointer to BaseNEncoder instances.
+typedef boost::shared_ptr<BaseNEncoder> BaseNEncoderPtr;
+
+/// @brief Defines an encoding function.
+typedef std::function<std::string (const std::vector<uint8_t>&)> EncodeFunc;
+
+/// @brief Defines a decoding function.
+typedef std::function<void (const std::string&, std::vector<uint8_t>&)> DecodeFunc;
+
+/// @brief Test fixture for exercising BaseNEncoder derivatives.
+class EncodeDecodeTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor
+ ///
+ /// @param encoder pointer to an encoder instance to use in tests
+ /// @param encode_func encoding function to test
+ /// @param decode_func decoding function to test
+ EncodeDecodeTest(BaseNEncoderPtr encoder, EncodeFunc encode_func, DecodeFunc decode_func)
+ : encoder_(encoder), encode_func_(encode_func), decode_func_(decode_func) {
+ // From RFC4648 test vectors.
+ valid_input_strings_.push_back("");
+ valid_input_strings_.push_back("f");
+ valid_input_strings_.push_back("fo");
+ valid_input_strings_.push_back("foo");
+ valid_input_strings_.push_back("foob");
+ valid_input_strings_.push_back("fooba");
+ valid_input_strings_.push_back("foobar");
+ }
+
+ /// @brief Destructor
+ ~EncodeDecodeTest() = default;
+
+ /// @brief Verifies encoding and decoding of test vectors from RFC4648
+ ///
+ /// Tests encoding and decoding functions using RFC supplied test vectors
+ /// by:
+ /// -# Encoding an input string and verifying the output against the expected
+ /// encoded string.
+ /// -# Decoding the encoded output and verifying it against the original input
+ /// string
+ /// -# If the algorithm is case sensitive, convert the encoded output string to
+ /// lower case and recheck that it is decoded properly
+ void encodeDecode() {
+ ASSERT_EQ(expected_encoded_strings_.size(), valid_input_strings_.size());
+ // For each valid input string:
+ // -# encode it and verify the encoded result is as expected
+ // -# decode the encoded result and verify it yields the original valid
+ // input string
+ // -# convert the encoded result to lower case and verify decoding
+ // yields correct result.
+ auto expected_output_str = expected_encoded_strings_.begin();
+ for (auto const& input : valid_input_strings_) {
+ std::vector<uint8_t>input_data(input.begin(), input.end());
+ std::string output_str;
+ ASSERT_NO_THROW_LOG(output_str = (encode_func_)(input_data));
+ ASSERT_EQ(output_str, *expected_output_str) << "input string: [" << input << "]";
+ ++expected_output_str;
+
+ std::vector<uint8_t> decoded_output;
+ ASSERT_NO_THROW_LOG((decode_func_)(output_str, decoded_output));
+ ASSERT_EQ(decoded_output, input_data);
+
+ if (!encoder_->isCaseSensitive()) {
+ const std::string lower_case_str = boost::algorithm::to_lower_copy(output_str);
+ decoded_output.clear();
+ ASSERT_NO_THROW_LOG((decode_func_)(lower_case_str, decoded_output));
+ ASSERT_EQ(decoded_output, input_data);
+ }
+ }
+ }
+
+ /// @brief Verifies that a list of encoded strings produces the expected
+ /// decoded results.
+ ///
+ /// @param encoded_strings list of encoded strings to decode
+ /// @param expected_strings list of expected decoded data as strings
+ void decode(std::vector<std::string>& encoded_strings,
+ std::vector<std::string>& expected_strings) {
+
+ ASSERT_EQ(encoded_strings.size(), expected_strings.size());
+ auto expected_str = expected_strings.begin();
+ for (auto const& encoded_str : encoded_strings) {
+ std::vector<uint8_t> decoded_output;
+ ASSERT_NO_THROW_LOG((decode_func_)(encoded_str, decoded_output));
+ std::string tmp(decoded_output.begin(), decoded_output.end());
+ EXPECT_EQ(tmp, *expected_str);
+ ++expected_str;
+ }
+ }
+
+ /// @brief Verifies that a list of invalid encoded strings fail to
+ /// decode appropriately
+ ///
+ /// @param encoded_strings list of invalid encoded strings
+ void decodeInvalid(std::vector<std::string>& encoded_strings) {
+ for (auto const& encoded_str : encoded_strings) {
+ std::vector<uint8_t> decoded_output;
+ EXPECT_THROW((decode_func_)(encoded_str, decoded_output), BadValue);
+ }
+ }
+
+ /// @brief Verifies the integrity to encoder's digit set and bit table.
+ void mapTest() {
+ size_t num_digits = strlen(encoder_->getDigitSet());
+ size_t whitespaces = 0;
+ size_t valid_digits = 0;
+ size_t bad_chars = 0;
+ size_t pad_chars = 0;
+ size_t upper_cased = 0;
+
+ auto pad_char = encoder_->getPadChar();
+
+ // Ensure the bit table is the proper size.
+ ASSERT_EQ(encoder_->getBitsTable().size(), 256);
+
+ // Iterate over the whole ASCII character set:
+ // 1. Convert the ASCII value to its encoded binary bit value.
+ // 2. Classify the value as whitespace, invalid, pad or valid
+ // 3. For valid digits verify they exist in the digit set
+ for (uint16_t ascii = 0; ascii < 256; ++ascii) {
+ // Look up the binary data for the digit.
+ // No value under 256 should throw.
+ uint8_t bits;
+ ASSERT_NO_THROW_LOG(bits = encoder_->digitToBits(ascii));
+
+ // Classify the bits value we found.
+ switch(bits) {
+ case 0xee:
+ ASSERT_TRUE(isspace(ascii));
+ ++whitespaces;
+ break;
+ case 0xff:
+ ++bad_chars;
+ break;
+ default: {
+ if (pad_char && ascii == pad_char) {
+ ++pad_chars;
+ } else {
+ // Verify the ascii value is in the digit set.
+ if (encoder_->isCaseSensitive()) {
+ ASSERT_TRUE(strchr(encoder_->getDigitSet(), ascii))
+ << "ascii: " << std::hex << ascii;
+ ++valid_digits;
+ } else {
+ auto check_ascii = toupper(ascii);
+ ASSERT_TRUE(strchr(encoder_->getDigitSet(), check_ascii))
+ << "ascii: " << std::hex << ascii
+ << " check_ascii: " << std::hex << check_ascii;
+
+ if (check_ascii == ascii){
+ ++valid_digits;
+ } else {
+ ++upper_cased;
+ }
+ }
+ }
+
+ break;
+ }}
+ }
+
+ // Verify that we see all valid digits.
+ EXPECT_EQ(valid_digits, num_digits);
+
+ // Verify that all of the ASCII values are accounted for.
+ EXPECT_EQ((valid_digits + upper_cased + whitespaces + bad_chars + pad_chars), 256)
+ << " : " << valid_digits
+ << " + " << upper_cased
+ << " + " << whitespaces
+ << " + " << bad_chars
+ << " + " << pad_chars;
+ }
+
+ BaseNEncoderPtr encoder_;
+ EncodeFunc encode_func_;
+ DecodeFunc decode_func_;
+ std::vector<std::string> valid_input_strings_;
+ std::vector<std::string> expected_encoded_strings_;
+};
+
+
+/// @brief Test Fixture for Base64 encoding
+class Base64Test : public EncodeDecodeTest {
+public:
+ Base64Test()
+ : EncodeDecodeTest(BaseNEncoderPtr(new Base64Encoder()), encodeBase64, decodeBase64) {
+ // From RFC4648 test vectors.
+ expected_encoded_strings_.push_back("");
+ expected_encoded_strings_.push_back("Zg==");
+ expected_encoded_strings_.push_back("Zm8=");
+ expected_encoded_strings_.push_back("Zm9v");
+ expected_encoded_strings_.push_back("Zm9vYg==");
+ expected_encoded_strings_.push_back("Zm9vYmE=");
+ expected_encoded_strings_.push_back("Zm9vYmFy");
+ }
+};
+
+/// @brief Test Fixture for Base32Hex encoding
+class Base32HexTest : public EncodeDecodeTest {
+public:
+ Base32HexTest()
+ : EncodeDecodeTest(BaseNEncoderPtr(new Base32HexEncoder()), encodeBase32Hex, decodeBase32Hex) {
+ // From RFC4648 test vectors.
+ expected_encoded_strings_.push_back("");
+ expected_encoded_strings_.push_back("CO======");
+ expected_encoded_strings_.push_back("CPNG====");
+ expected_encoded_strings_.push_back("CPNMU===");
+ expected_encoded_strings_.push_back("CPNMUOG=");
+ expected_encoded_strings_.push_back("CPNMUOJ1");
+ expected_encoded_strings_.push_back("CPNMUOJ1E8======");
+ }
+};
+
+/// @brief Test Fixture for Base16 encoding
+class Base16Test : public EncodeDecodeTest {
+public:
+ Base16Test()
+ : EncodeDecodeTest(BaseNEncoderPtr(new Base16Encoder()), encodeHex, decodeHex) {
+ // From RFC4648 test vectors.
+ expected_encoded_strings_.push_back("");
+ expected_encoded_strings_.push_back("66");
+ expected_encoded_strings_.push_back("666F");
+ expected_encoded_strings_.push_back("666F6F");
+ expected_encoded_strings_.push_back("666F6F62");
+ expected_encoded_strings_.push_back("666F6F6261");
+ expected_encoded_strings_.push_back("666F6F626172");
+ }
+};
+
+// Verify RFC test vectors for Base64
+TEST_F(Base64Test, validEncodeDecode) {
+ encodeDecode();
+}
+
+// Verify whitespaces are handled properly in Base64
+TEST_F(Base64Test, whiteSpace) {
+ std::vector<std::string> encoded_strings = {
+ "Zm 9v\tYmF\ny",
+ "Zm9vYg==",
+ "Zm9vYmE=\n",
+ " Zm9vYmE=\n",
+ " ",
+ "\n\t"
+ };
+
+ std::vector<std::string> expected_strings = {
+ "foobar",
+ "foob",
+ "fooba",
+ "fooba",
+ "",
+ ""
+ };
+
+ decode(encoded_strings, expected_strings);
+}
+
+// Verify invalid encodings are handled properly in Base64
+TEST_F(Base64Test, decodeInvalid) {
+ std::vector<std::string> encoded_strings = {
+ // Incomplete input.
+ "Zm9vYmF",
+ // Only up to 2 padding characters are allowed.
+ "A===",
+ "A= ==",
+ // Intermediate padding isn't allowed.
+ "YmE=YmE=",
+ // Non canonical form isn't allowed.
+ "Zm9=",
+ "Zm==",
+ };
+
+ decodeInvalid(encoded_strings);
+}
+
+// Verify mappings for Base64
+TEST_F(Base64Test, mappingCheck) {
+ mapTest();
+}
+
+// Verify RFC test vectors for Base32Hex
+TEST_F(Base32HexTest, validEncodeDecode) {
+ encodeDecode();
+}
+
+// Verify whitespaces are handled properly in Base32Hex
+TEST_F(Base32HexTest, whiteSpace) {
+ std::vector<std::string> encoded_strings = {
+ "CP NM\tUOG=",
+ "CPNMU===\n",
+ " CP NM\tUOG=",
+ " "
+ };
+
+ std::vector<std::string> expected_strings = {
+ "foob",
+ "foo",
+ "foob",
+ ""
+ };
+
+ decode(encoded_strings, expected_strings);
+}
+
+// Verify invalid encodings are handled properly in Base32Hex
+TEST_F(Base32HexTest, decodeInvalid) {
+ std::vector<std::string> encoded_strings = {
+ // Incomplete input.
+ "CPNMUOJ",
+ // Invalid number of padding characters.
+ "CPNMU0==",
+ "CO0=====",
+ "CO=======",
+ // Intermediate padding isn't allowed.
+ "CPNMUOG=CPNMUOG=",
+ // Non canonical form isn't allowed.
+ "0P======"
+ };
+
+ decodeInvalid(encoded_strings);
+}
+
+// Verify mappings for Base32Hex
+TEST_F(Base32HexTest, mappingCheck) {
+ mapTest();
+}
+
+// Verify RFC test vectors for Base16
+TEST_F(Base16Test, validEncodeDecode) {
+ encodeDecode();
+}
+
+// Verify whitespaces are handled properly in Base16
+TEST_F(Base16Test, whiteSpace) {
+ std::vector<std::string> encoded_strings = {
+ "66 6F\t6F62",
+ "66 6F6F\n",
+ " 66\v\t6F6F62",
+ " "
+ };
+
+ std::vector<std::string> expected_strings = {
+ "foob",
+ "foo",
+ "foob",
+ ""
+ };
+
+ decode(encoded_strings, expected_strings);
+}
+
+// Verify invalid encodings are handled properly in Base16
+TEST_F(Base16Test, decodeInvalid) {
+ std::vector<std::string> encoded_strings = {
+ // Non hex digits should fail.
+ "lx",
+ // Encoded string must have an even number of characters.
+ "dea"
+ };
+
+ decodeInvalid(encoded_strings);
+}
+
+// Verify mappings for Base16
+TEST_F(Base16Test, mappingCheck) {
+ mapTest();
+}
+
+}
diff --git a/src/lib/util/tests/fd_share_tests.cc b/src/lib/util/tests/fd_share_tests.cc
new file mode 100644
index 0000000..f870ec2
--- /dev/null
+++ b/src/lib/util/tests/fd_share_tests.cc
@@ -0,0 +1,72 @@
+// Copyright (C) 2011-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 <util/io/fd.h>
+#include <util/io/fd_share.h>
+
+#include <util/unittests/check_valgrind.h>
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <cstdio>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// We test that we can transfer a pipe over other pipe
+TEST(FDShare, transfer) {
+
+ if (!isc::util::unittests::runningOnValgrind()) {
+ // Get a pipe and fork
+ int pipes[2];
+ ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
+ const pid_t sender(fork());
+ ASSERT_NE(-1, sender);
+ if (sender) { // We are in parent
+ // Close the other side of pipe, we want only writable one
+ EXPECT_NE(-1, close(pipes[0]));
+ // Get a process to check data
+ int fd(0);
+ const pid_t checker(check_output(&fd, "data", 4));
+ ASSERT_NE(-1, checker);
+ // Now, send the file descriptor, close it and close the pipe
+ EXPECT_NE(-1, send_fd(pipes[1], fd));
+ EXPECT_NE(-1, close(pipes[1]));
+ EXPECT_NE(-1, close(fd));
+ // Check both subprocesses ended well
+ EXPECT_TRUE(process_ok(sender));
+ EXPECT_TRUE(process_ok(checker));
+ } else { // We are in child. We do not use ASSERT here
+ // Close the write end, we only read
+ if (close(pipes[1])) {
+ exit(1);
+ }
+ // Get the file descriptor
+ const int fd(recv_fd(pipes[0]));
+ if (fd == -1) {
+ exit(1);
+ }
+ // This pipe is not needed
+ if (close(pipes[0])) {
+ exit(1);
+ }
+ // Send "data" through the received fd, close it and be done
+ if (!write_data(fd, "data", 4) || close(fd) == -1) {
+ exit(1);
+ }
+ exit(0);
+ }
+ }
+}
+
+}
diff --git a/src/lib/util/tests/fd_tests.cc b/src/lib/util/tests/fd_tests.cc
new file mode 100644
index 0000000..eee597c
--- /dev/null
+++ b/src/lib/util/tests/fd_tests.cc
@@ -0,0 +1,71 @@
+// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <util/unittests/check_valgrind.h>
+
+#include <util/io/fd.h>
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// Make sure the test is large enough and does not fit into one
+// read or write request
+const size_t TEST_DATA_SIZE = 8 * 1024 * 1024;
+
+class FDTest : public ::testing::Test {
+public:
+ unsigned char *data, *buffer;
+
+ /// @brief Constructor
+ FDTest() :
+ // We do not care what is inside, we just need it to be the same
+ data(new unsigned char[TEST_DATA_SIZE]),
+ buffer(NULL) {
+ memset(data, 0, TEST_DATA_SIZE);
+ }
+
+ /// @brief Destructor
+ ~FDTest() {
+ delete[] data;
+ delete[] buffer;
+ }
+};
+
+// Test we read what was sent
+TEST_F(FDTest, read) {
+ if (!isc::util::unittests::runningOnValgrind()) {
+ int read_pipe(0);
+ buffer = new unsigned char[TEST_DATA_SIZE];
+ pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(feeder, 0);
+ ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
+ EXPECT_TRUE(process_ok(feeder));
+ EXPECT_EQ(TEST_DATA_SIZE, received);
+ EXPECT_EQ(0, memcmp(data, buffer, received));
+ }
+}
+
+// Test we write the correct thing
+TEST_F(FDTest, write) {
+ if (!isc::util::unittests::runningOnValgrind()) {
+ int write_pipe(0);
+ pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(checker, 0);
+ EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
+ EXPECT_EQ(0, close(write_pipe));
+ EXPECT_TRUE(process_ok(checker));
+ }
+}
+
+}
diff --git a/src/lib/util/tests/filesystem_unittests.cc b/src/lib/util/tests/filesystem_unittests.cc
new file mode 100644
index 0000000..548070a
--- /dev/null
+++ b/src/lib/util/tests/filesystem_unittests.cc
@@ -0,0 +1,125 @@
+// Copyright (C) 2015-2024 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 <exceptions/exceptions.h>
+#include <testutils/gtest_utils.h>
+#include <util/filesystem.h>
+
+#include <fstream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::util::file;
+using namespace std;
+
+namespace {
+
+/// @brief Test fixture class for testing operations on files.
+struct FileUtilTest : ::testing::Test {
+ /// @brief Destructor.
+ ///
+ /// Deletes the test file if any.
+ virtual ~FileUtilTest() {
+ string test_file_name(TEST_DATA_BUILDDIR "/fu.test");
+ static_cast<void>(remove(test_file_name.c_str()));
+ }
+};
+
+/// @brief Check that an error is returned by getContent on non-existent file.
+TEST_F(FileUtilTest, notExist) {
+ EXPECT_THROW_MSG(getContent("/does/not/exist"), BadValue,
+ "Expected a file at path '/does/not/exist'");
+}
+
+/// @brief Check that an error is returned by getContent on not regular file.
+TEST_F(FileUtilTest, notRegular) {
+ EXPECT_THROW_MSG(getContent("/"), BadValue, "Expected '/' to be a regular file");
+}
+
+/// @brief Check getContent.
+TEST_F(FileUtilTest, getContent) {
+ string file_name(TEST_DATA_BUILDDIR "/fu.test");
+ ofstream fs(file_name.c_str(), ofstream::out | ofstream::trunc);
+ ASSERT_TRUE(fs.is_open());
+ fs << "abdc";
+ fs.close();
+ string content;
+ EXPECT_NO_THROW_LOG(content = getContent(file_name));
+ EXPECT_EQ("abdc", content);
+}
+
+/// @brief Check isDir.
+TEST_F(FileUtilTest, isDir) {
+ EXPECT_TRUE(isDir("/dev"));
+ EXPECT_FALSE(isDir("/dev/null"));
+ EXPECT_FALSE(isDir("/this/does/not/exist"));
+ EXPECT_FALSE(isDir("/etc/hosts"));
+}
+
+/// @brief Check isFile.
+TEST_F(FileUtilTest, isFile) {
+ EXPECT_TRUE(isFile(ABS_SRCDIR "/filesystem_unittests.cc"));
+ EXPECT_FALSE(isFile(TEST_DATA_BUILDDIR));
+}
+
+/// @brief Check that the components are split correctly.
+TEST(PathTest, components) {
+ // Complete name
+ Path fname("/alpha/beta/gamma.delta");
+ EXPECT_EQ("/alpha/beta/", fname.parentPath());
+ EXPECT_EQ("gamma", fname.stem());
+ EXPECT_EQ(".delta", fname.extension());
+ EXPECT_EQ("gamma.delta", fname.filename());
+}
+
+/// @brief Check replaceExtension.
+TEST(PathTest, replaceExtension) {
+ Path fname("a.b");
+
+ EXPECT_EQ("a", fname.replaceExtension("").str());
+ EXPECT_EQ("a.f", fname.replaceExtension(".f").str());
+ EXPECT_EQ("a.f", fname.replaceExtension("f").str());
+ EXPECT_EQ("a./c/d/", fname.replaceExtension(" /c/d/ ").str());
+ EXPECT_EQ("a.f", fname.replaceExtension("/c/d/e.f").str());
+ EXPECT_EQ("a.f", fname.replaceExtension("e.f").str());
+}
+
+/// @brief Check replaceParentPath.
+TEST(PathTest, replaceParentPath) {
+ Path fname("a.b");
+ EXPECT_EQ("", fname.parentPath());
+ EXPECT_EQ("a.b", fname.str());
+
+ fname.replaceParentPath("/just/some/dir/");
+ EXPECT_EQ("/just/some/dir/", fname.parentPath());
+ EXPECT_EQ("/just/some/dir/a.b", fname.str());
+
+ fname.replaceParentPath("/just/some/dir");
+ EXPECT_EQ("/just/some/dir/", fname.parentPath());
+ EXPECT_EQ("/just/some/dir/a.b", fname.str());
+
+ fname.replaceParentPath("/");
+ EXPECT_EQ("/", fname.parentPath());
+ EXPECT_EQ("/a.b", fname.str());
+
+ fname.replaceParentPath("");
+ EXPECT_EQ("", fname.parentPath());
+ EXPECT_EQ("a.b", fname.str());
+
+ fname = Path("/first/a.b");
+ EXPECT_EQ("/first/", fname.parentPath());
+ EXPECT_EQ("/first/a.b", fname.str());
+
+ fname.replaceParentPath("/just/some/dir");
+ EXPECT_EQ("/just/some/dir/", fname.parentPath());
+ EXPECT_EQ("/just/some/dir/a.b", fname.str());
+}
+
+} // namespace
diff --git a/src/lib/util/tests/hash_unittest.cc b/src/lib/util/tests/hash_unittest.cc
new file mode 100644
index 0000000..f789e51
--- /dev/null
+++ b/src/lib/util/tests/hash_unittest.cc
@@ -0,0 +1,34 @@
+// Copyright (C) 2018 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 <util/hash.h>
+
+#include <gtest/gtest.h>
+
+#include <cstring>
+#include <vector>
+
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+TEST(HashTest, empty) {
+ EXPECT_EQ(14695981039346656037ull, Hash64::hash(0, 0));
+}
+
+TEST(HashTest, foobar) {
+ EXPECT_EQ(9625390261332436968ull, Hash64::hash(string("foobar")));
+}
+
+TEST(HashTest, chongo) {
+ EXPECT_EQ(5080352029159061781ull,
+ Hash64::hash(string("chongo was here!\n")));
+}
+
+}
diff --git a/src/lib/util/tests/io_unittests.cc b/src/lib/util/tests/io_unittests.cc
new file mode 100644
index 0000000..320060f
--- /dev/null
+++ b/src/lib/util/tests/io_unittests.cc
@@ -0,0 +1,236 @@
+// Copyright (C) 2011-2024 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/.
+
+/// \brief Test of asiolink utilities
+///
+/// Tests the functionality of the asiolink utilities code by comparing them
+/// with the equivalent methods in isc::dns::[Input/Output]Buffer.
+
+#include <config.h>
+
+#include <testutils/gtest_utils.h>
+#include <util/buffer.h>
+#include <util/io.h>
+
+#include <bitset>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+struct ReadWriteUintTest : ::testing::Test {
+ template <typename uint_t>
+ string showDiff(uint_t const left, uint8_t* const right) {
+ bitset<8 * sizeof(uint_t)> const bitset_left(left);
+ bitset<8 * sizeof(uint_t)> const bitset_right(*(reinterpret_cast<uint_t*>(right)));
+ string const bitstring_left(separateBytes(bitset_left.to_string()));
+ string const bitstring_right(separateBytes(bitset_right.to_string()));
+ string const diff(::testing::internal::edit_distance::CreateUnifiedDiff({bitstring_left},
+ {bitstring_right}));
+ return (diff);
+ }
+
+private:
+ string separateBytes(string const& input) {
+ size_t const position(8);
+ stringstream ss;
+ for (size_t i = 0; i < input.size(); i = i + position) {
+ if (i) {
+ ss << " ";
+ }
+ ss << input.substr(i, position);
+ }
+ return (ss.str());
+ }
+};
+
+/// @brief Check whether uint16_t can be read from a buffer properly.
+TEST_F(ReadWriteUintTest, readUint16) {
+ uint16_t const test16[] = {0, 1, 2000, 0x8000, 0xffff};
+ uint8_t data[] = {0, 0, 0, 0};
+
+ // Make sure that we can read data, regardless of the memory alignment.
+ // That is why we need to repeat it 2 times.
+ for (size_t offset = 0; offset < sizeof(uint16_t); ++offset) {
+ for (size_t i = 0; i < sizeof(test16) / sizeof(uint16_t); ++i) {
+ SCOPED_TRACE("offset " + to_string(offset) + ", iteration " + to_string(i));
+
+ uint16_t tmp = htons(test16[i]);
+ memcpy(&data[offset], &tmp, sizeof(uint16_t));
+
+ EXPECT_EQ(test16[i], readUint16(&data[offset], sizeof(uint16_t)));
+ }
+ }
+}
+
+/// @brief Check whether reading an uint16_t results in an exception.
+TEST_F(ReadWriteUintTest, readUint16OutOfRange) {
+ uint8_t data[] = {0};
+ EXPECT_THROW_MSG(readUint16(data, 1), OutOfRange,
+ "Expected buffer to be long enough to read a 2-byte integer, but got 1 byte "
+ "instead");
+}
+
+/// @brief Check whether uint16_t can be written to a buffer properly.
+TEST_F(ReadWriteUintTest, writeUint16) {
+ uint16_t const test16[] = {0, 1, 2000, 0x8000, 0xffff};
+ uint8_t data[4];
+
+ // Make sure that we can write data, regardless of the memory alignment.
+ // That's why we need to repeat 2 times.
+ for (size_t offset = 0; offset < sizeof(uint16_t); ++offset) {
+ for (size_t i = 0; i < sizeof(test16) / sizeof(uint16_t); ++i) {
+ SCOPED_TRACE("offset " + to_string(offset) + ", iteration " + to_string(i));
+
+ uint8_t* ptr = writeUint16(test16[i], &data[offset], sizeof(uint16_t));
+
+ EXPECT_EQ(&data[offset] + sizeof(uint16_t), ptr);
+
+ uint16_t tmp = htons(test16[i]);
+
+ EXPECT_EQ(0, memcmp(&tmp, &data[offset], sizeof(uint16_t)))
+ << showDiff(tmp, &data[offset]);
+ }
+ }
+}
+
+/// @brief Check whether writing an uint16_t results in an exception.
+TEST_F(ReadWriteUintTest, writeUint16OutOfRange) {
+ uint16_t i16 = 42;
+ uint8_t data[1];
+ EXPECT_THROW_MSG(writeUint16(i16, data, sizeof(data)), OutOfRange,
+ "Expected buffer to be long enough to write a 2-byte integer, but got 1 byte "
+ "instead");
+}
+
+/// @brief Check whether uint32_t can be read from a buffer properly.
+TEST_F(ReadWriteUintTest, readUint32) {
+ uint32_t const test32[] = {0, 1, 2000, 0x80000000, 0xffffffff};
+ uint8_t data[] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ // Make sure that we can read data, regardless of the memory alignment.
+ // That is why we need to repeat it 4 times.
+ for (size_t offset = 0; offset < sizeof(uint32_t); ++offset) {
+ for (size_t i = 0; i < sizeof(test32) / sizeof(uint32_t); ++i) {
+ SCOPED_TRACE("offset " + to_string(offset) + ", iteration " + to_string(i));
+
+ uint32_t tmp = htonl(test32[i]);
+ memcpy(&data[offset], &tmp, sizeof(uint32_t));
+
+ EXPECT_EQ(test32[i], readUint32(&data[offset], sizeof(uint32_t)));
+ }
+ }
+}
+
+/// @brief Check whether reading an uint32_t results in an exception.
+TEST_F(ReadWriteUintTest, readUint32OutOfRange) {
+ uint8_t data[] = {0, 0, 0};
+ EXPECT_THROW_MSG(readUint32(data, 3), OutOfRange,
+ "Expected buffer to be long enough to read a 4-byte integer, but got 3 bytes "
+ "instead");
+}
+
+/// @brief Check whether uint32_t can be written to a buffer properly.
+TEST_F(ReadWriteUintTest, writeUint32) {
+ uint32_t const test32[] = {0, 1, 2000, 0x80000000, 0xffffffff};
+ uint8_t data[8];
+
+ // Make sure that we can write data, regardless of the memory alignment.
+ // That's why we need to repeat 4 times.
+ for (size_t offset = 0; offset < sizeof(uint32_t); ++offset) {
+ for (size_t i = 0; i < sizeof(test32) / sizeof(uint32_t); ++i) {
+ SCOPED_TRACE("offset " + to_string(offset) + ", iteration " + to_string(i));
+
+ uint8_t* ptr = writeUint32(test32[i], &data[offset], sizeof(uint32_t));
+
+ EXPECT_EQ(&data[offset] + sizeof(uint32_t), ptr);
+
+ uint32_t tmp = htonl(test32[i]);
+
+ EXPECT_EQ(0, memcmp(&tmp, &data[offset], sizeof(uint32_t)))
+ << showDiff(tmp, &data[offset]);
+ }
+ }
+}
+
+/// @brief Check whether writing an uint32_t results in an exception.
+TEST_F(ReadWriteUintTest, writeUint32OutOfRange) {
+ uint32_t i32 = 28;
+ uint8_t data[3];
+ EXPECT_THROW_MSG(writeUint32(i32, data, sizeof(data)), OutOfRange,
+ "Expected buffer to be long enough to write a 4-byte integer, but got 3 bytes "
+ "instead");
+}
+
+/// @brief Check whether uint64_t can be read from a buffer properly.
+TEST_F(ReadWriteUintTest, readUint64) {
+ uint64_t const test64[] = {0, 1, 2000, 0x80000000, 0xffffffff, 0xfffffffffffffff};
+ uint8_t data[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ // Make sure that we can read data, regardless of the memory alignment.
+ // That is why we need to repeat it 8 times.
+ for (size_t offset = 0; offset < sizeof(uint64_t); ++offset) {
+ for (size_t i = 0; i < sizeof(test64) / sizeof(uint64_t); ++i) {
+ SCOPED_TRACE("offset " + to_string(offset) + ", iteration " + to_string(i));
+
+ uint64_t tmp = (uint64_t(htonl(test64[i])) << 32) | htonl(test64[i] >> 32);
+ memcpy(&data[offset], &tmp, sizeof(uint64_t));
+
+ EXPECT_EQ(test64[i], readUint64(&data[offset], sizeof(uint64_t)));
+ }
+ }
+}
+
+/// @brief Check whether reading an uint64_t results in an exception.
+TEST_F(ReadWriteUintTest, readUint64OutOfRange) {
+ uint8_t buf[] = {0, 0, 0, 0, 0, 0, 0};
+
+ EXPECT_THROW_MSG(readUint64(buf, 7), OutOfRange,
+ "Expected buffer to be long enough to read a 8-byte integer, but got 7 bytes "
+ "instead");
+}
+
+/// @brief Check whether uint64 can be written to a buffer properly.
+TEST_F(ReadWriteUintTest, writeUint64) {
+ uint64_t const test64[] = {0, 1, 2000, 0x80000000, 0xffffffff, 0xfffffffffffffff};
+ uint8_t data[16];
+
+ // Make sure that we can write data, regardless of the memory alignment.
+ // That's why we need to repeat 8 times.
+ for (size_t offset = 0; offset < sizeof(uint64_t); ++offset) {
+ for (size_t i = 0; i < sizeof(test64) / sizeof(uint64_t); ++i) {
+ SCOPED_TRACE("offset " + to_string(offset) + ", iteration " + to_string(i));
+
+ uint8_t* ptr = writeUint64(test64[i], &data[offset], sizeof(uint64_t));
+
+ EXPECT_EQ(&data[offset] + sizeof(uint64_t), ptr);
+
+ uint64_t tmp = (uint64_t(htonl(test64[i])) << 32) | htonl(test64[i] >> 32);
+
+ EXPECT_EQ(0, memcmp(&tmp, &data[offset], sizeof(uint64_t)))
+ << showDiff(tmp, &data[offset]);
+ }
+ }
+}
+
+/// @brief Check whether writing an uint64_t results in an exception.
+TEST_F(ReadWriteUintTest, writeUint64OutOfRange) {
+ uint64_t i64 = 28;
+ uint8_t data[7];
+ EXPECT_THROW_MSG(writeUint64(i64, data, sizeof(data)), OutOfRange,
+ "Expected buffer to be long enough to write a 8-byte integer, but got 7 bytes "
+ "instead");
+}
+
+} // namespace
diff --git a/src/lib/util/tests/labeled_value_unittest.cc b/src/lib/util/tests/labeled_value_unittest.cc
new file mode 100644
index 0000000..c994156
--- /dev/null
+++ b/src/lib/util/tests/labeled_value_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright (C) 2013-2016 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 <util/labeled_value.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Verifies basic construction and accessors for LabeledValue.
+TEST(LabeledValue, construction) {
+ /// Verify that an empty label is not allowed.
+ ASSERT_THROW(LabeledValue(1, ""), LabeledValueError);
+
+ /// Verify that a valid constructor works.
+ LabeledValuePtr lvp;
+ ASSERT_NO_THROW(lvp.reset(new LabeledValue(1, "NotBlank")));
+ ASSERT_TRUE(lvp);
+
+ // Verify that the value can be accessed.
+ EXPECT_EQ(1, lvp->getValue());
+
+ // Verify that the label can be accessed.
+ EXPECT_EQ("NotBlank", lvp->getLabel());
+}
+
+/// @brief Verifies the logical operators defined for LabeledValue.
+TEST(LabeledValue, operators) {
+ LabeledValuePtr lvp1;
+ LabeledValuePtr lvp1Also;
+ LabeledValuePtr lvp2;
+
+ // Create three instances, two of which have the same numeric value.
+ ASSERT_NO_THROW(lvp1.reset(new LabeledValue(1, "One")));
+ ASSERT_NO_THROW(lvp1Also.reset(new LabeledValue(1, "OneAlso")));
+ ASSERT_NO_THROW(lvp2.reset(new LabeledValue(2, "Two")));
+
+ // Verify each of the operators.
+ EXPECT_TRUE(*lvp1 == *lvp1Also);
+ EXPECT_TRUE(*lvp1 != *lvp2);
+ EXPECT_TRUE(*lvp1 < *lvp2);
+ EXPECT_FALSE(*lvp2 < *lvp1);
+}
+
+/// @brief Verifies the default constructor for LabeledValueSet.
+TEST(LabeledValueSet, construction) {
+ ASSERT_NO_THROW (LabeledValueSet());
+}
+
+/// @brief Verifies the basic operations of a LabeledValueSet.
+/// Essentially we verify that we can define a set of valid entries and
+/// look them up without issue.
+TEST(LabeledValueSet, basicOperation) {
+ const char* labels[] = {"Zero", "One", "Two", "Three" };
+ LabeledValueSet lvset;
+ LabeledValuePtr lvp;
+
+ // Verify the we cannot add an empty pointer to the set.
+ EXPECT_THROW(lvset.add(lvp), LabeledValueError);
+
+ // Verify that we can add an entry to the set via pointer.
+ ASSERT_NO_THROW(lvp.reset(new LabeledValue(0, labels[0])));
+ EXPECT_NO_THROW(lvset.add(lvp));
+
+ // Verify that we cannot add a duplicate entry.
+ EXPECT_THROW(lvset.add(lvp), LabeledValueError);
+
+ // Add the remaining entries using add(int,char*) variant.
+ for (int i = 1; i < 3; i++) {
+ EXPECT_NO_THROW(lvset.add(i, labels[i]));
+ }
+
+ // Verify that we can't add a duplicate entry this way either.
+ EXPECT_THROW ((lvset.add(0, labels[0])), LabeledValueError);
+
+ // Verify that we can look up all of the defined entries properly.
+ for (int i = 1; i < 3; i++) {
+ EXPECT_TRUE(lvset.isDefined(i));
+ EXPECT_NO_THROW(lvp = lvset.get(i));
+ EXPECT_EQ(lvp->getValue(), i);
+ EXPECT_EQ(lvp->getLabel(), labels[i]);
+ EXPECT_EQ(lvset.getLabel(i), labels[i]);
+ }
+
+ // Verify behavior for a value that is not defined.
+ EXPECT_FALSE(lvset.isDefined(4));
+ EXPECT_NO_THROW(lvp = lvset.get(4));
+ EXPECT_FALSE(lvp);
+ EXPECT_EQ(lvset.getLabel(4), LabeledValueSet::UNDEFINED_LABEL);
+}
+
+}
diff --git a/src/lib/util/tests/memory_segment_common_unittest.cc b/src/lib/util/tests/memory_segment_common_unittest.cc
new file mode 100644
index 0000000..f95021d
--- /dev/null
+++ b/src/lib/util/tests/memory_segment_common_unittest.cc
@@ -0,0 +1,100 @@
+// Copyright (C) 2013-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 <util/memory_segment.h>
+
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+
+#include <cstring>
+#include <stdint.h>
+
+namespace isc {
+namespace util {
+namespace test {
+
+void
+checkSegmentNamedAddress(MemorySegment& segment, bool out_of_segment_ok) {
+ // NULL name is not allowed.
+ EXPECT_THROW(segment.getNamedAddress(NULL), InvalidParameter);
+
+ // If the name does not exist, false should be returned.
+ EXPECT_FALSE(segment.getNamedAddress("test address").first);
+
+ // Now set it
+ void* ptr32 = segment.allocate(sizeof(uint32_t));
+ const uint32_t test_val = 42;
+ *static_cast<uint32_t*>(ptr32) = test_val;
+ EXPECT_FALSE(segment.setNamedAddress("test address", ptr32));
+
+ // NULL name isn't allowed.
+ EXPECT_THROW(segment.setNamedAddress(NULL, ptr32), InvalidParameter);
+ EXPECT_THROW(segment.getNamedAddress(NULL), InvalidParameter);
+ EXPECT_THROW(segment.clearNamedAddress(NULL), InvalidParameter);
+
+ // Empty names are not allowed.
+ EXPECT_THROW(segment.setNamedAddress("", ptr32), InvalidParameter);
+ EXPECT_THROW(segment.getNamedAddress(""), InvalidParameter);
+ EXPECT_THROW(segment.clearNamedAddress(""), InvalidParameter);
+
+ // Names beginning with _ are not allowed.
+ EXPECT_THROW(segment.setNamedAddress("_foo", ptr32), InvalidParameter);
+ EXPECT_THROW(segment.getNamedAddress("_foo"), InvalidParameter);
+ EXPECT_THROW(segment.clearNamedAddress("_foo"), InvalidParameter);
+
+ // we can now get it; the stored value should be intact.
+ MemorySegment::NamedAddressResult result =
+ segment.getNamedAddress("test address");
+ EXPECT_TRUE(result.first);
+ EXPECT_EQ(test_val, *static_cast<const uint32_t*>(result.second));
+
+ // Override it.
+ void* ptr16 = segment.allocate(sizeof(uint16_t));
+ const uint16_t test_val16 = 4200;
+ *static_cast<uint16_t*>(ptr16) = test_val16;
+ EXPECT_FALSE(segment.setNamedAddress("test address", ptr16));
+ result = segment.getNamedAddress("test address");
+ EXPECT_TRUE(result.first);
+ EXPECT_EQ(test_val16, *static_cast<const uint16_t*>(result.second));
+
+ // Clear it. Then we won't be able to find it any more.
+ EXPECT_TRUE(segment.clearNamedAddress("test address"));
+ EXPECT_FALSE(segment.getNamedAddress("test address").first);
+
+ // duplicate attempt of clear will result in false as it doesn't exist.
+ EXPECT_FALSE(segment.clearNamedAddress("test address"));
+
+ // Setting NULL is okay.
+ EXPECT_FALSE(segment.setNamedAddress("null address", NULL));
+ result = segment.getNamedAddress("null address");
+ EXPECT_TRUE(result.first);
+ EXPECT_FALSE(result.second);
+
+ // If the underlying implementation performs explicit check against
+ // out-of-segment address, confirm the behavior.
+ if (!out_of_segment_ok) {
+ uint8_t ch = 'A';
+ EXPECT_THROW(segment.setNamedAddress("local address", &ch),
+ MemorySegmentError);
+ }
+
+ // clean them up all
+ segment.deallocate(ptr32, sizeof(uint32_t));
+ EXPECT_FALSE(segment.allMemoryDeallocated()); // not fully deallocated
+ segment.deallocate(ptr16, sizeof(uint16_t)); // not yet
+ EXPECT_FALSE(segment.allMemoryDeallocated());
+ EXPECT_TRUE(segment.clearNamedAddress("null address"));
+ // null name isn't allowed:
+ EXPECT_THROW(segment.clearNamedAddress(NULL), InvalidParameter);
+ EXPECT_TRUE(segment.allMemoryDeallocated()); // now everything is gone
+}
+
+}
+}
+}
diff --git a/src/lib/util/tests/memory_segment_common_unittest.h b/src/lib/util/tests/memory_segment_common_unittest.h
new file mode 100644
index 0000000..435ff12
--- /dev/null
+++ b/src/lib/util/tests/memory_segment_common_unittest.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2013-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 <util/memory_segment.h>
+
+namespace isc {
+namespace util {
+namespace test {
+
+/// \brief Implementation dependent checks on memory segment named addresses.
+///
+/// This function contains a set of test cases for given memory segment
+/// regarding "named address" methods. The test cases basically only depend
+/// on the base class interfaces, but if the underlying implementation does
+/// not check if the given address to setNamedAddress() belongs to the segment,
+/// out_of_segment_ok should be set to true.
+void checkSegmentNamedAddress(MemorySegment& segment, bool out_of_segment_ok);
+
+}
+}
+}
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/tests/memory_segment_local_unittest.cc b/src/lib/util/tests/memory_segment_local_unittest.cc
new file mode 100644
index 0000000..d1aa52d
--- /dev/null
+++ b/src/lib/util/tests/memory_segment_local_unittest.cc
@@ -0,0 +1,117 @@
+// Copyright (C) 2012-2016 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 <util/tests/memory_segment_common_unittest.h>
+
+#include <util/memory_segment_local.h>
+#include <exceptions/exceptions.h>
+#include <gtest/gtest.h>
+#include <boost/scoped_ptr.hpp>
+#include <memory>
+#include <limits.h>
+
+using namespace std;
+using namespace isc::util;
+
+namespace {
+
+TEST(MemorySegmentLocal, TestLocal) {
+ boost::scoped_ptr<MemorySegment> segment(new MemorySegmentLocal());
+
+ // By default, nothing is allocated.
+ EXPECT_TRUE(segment->allMemoryDeallocated());
+
+ void* ptr = segment->allocate(1024);
+
+ // Now, we have an allocation:
+ EXPECT_FALSE(segment->allMemoryDeallocated());
+
+ void* ptr2 = segment->allocate(42);
+
+ // Still:
+ EXPECT_FALSE(segment->allMemoryDeallocated());
+
+ // These should not fail, because the buffers have been allocated.
+ EXPECT_NO_FATAL_FAILURE(memset(ptr, 0, 1024));
+ EXPECT_NO_FATAL_FAILURE(memset(ptr, 0, 42));
+
+ segment->deallocate(ptr, 1024);
+
+ // Still:
+ EXPECT_FALSE(segment->allMemoryDeallocated());
+
+ segment->deallocate(ptr2, 42);
+
+ // Now, we have an deallocated everything:
+ EXPECT_TRUE(segment->allMemoryDeallocated());
+}
+
+/// @todo: disabled, see ticket #3510
+TEST(MemorySegmentLocal, DISABLED_TestTooMuchMemory) {
+ boost::scoped_ptr<MemorySegment> segment(new MemorySegmentLocal());
+
+ // Although it should be perfectly fine to use the ULONG_MAX
+ // instead of LONG_MAX as the size_t value should be unsigned,
+ // Valgrind appears to be using the signed value and hence the
+ // maximum positive value is LONG_MAX for Valgrind. But, this
+ // should be sufficient to test the "too much memory" conditions.
+ EXPECT_THROW(segment->allocate(LONG_MAX), bad_alloc);
+}
+
+TEST(MemorySegmentLocal, TestBadDeallocate) {
+ boost::scoped_ptr<MemorySegment> segment(new MemorySegmentLocal());
+
+ // By default, nothing is allocated.
+ EXPECT_TRUE(segment->allMemoryDeallocated());
+
+ void* ptr = segment->allocate(1024);
+
+ // Now, we have an allocation:
+ EXPECT_FALSE(segment->allMemoryDeallocated());
+
+ // This should not throw
+ EXPECT_NO_THROW(segment->deallocate(ptr, 1024));
+
+ // Now, we have an deallocated everything:
+ EXPECT_TRUE(segment->allMemoryDeallocated());
+
+ ptr = segment->allocate(1024);
+
+ // Now, we have another allocation:
+ EXPECT_FALSE(segment->allMemoryDeallocated());
+
+ // This should throw as the size passed to deallocate() is larger
+ // than what was allocated.
+ EXPECT_THROW(segment->deallocate(ptr, 2048), isc::OutOfRange);
+
+ // This should not throw
+ EXPECT_NO_THROW(segment->deallocate(ptr, 1024));
+
+ // Now, we have an deallocated everything:
+ EXPECT_TRUE(segment->allMemoryDeallocated());
+}
+
+TEST(MemorySegmentLocal, TestNullDeallocate) {
+ boost::scoped_ptr<MemorySegment> segment(new MemorySegmentLocal());
+
+ // By default, nothing is allocated.
+ EXPECT_TRUE(segment->allMemoryDeallocated());
+
+ // NULL deallocation is a no-op.
+ EXPECT_NO_THROW(segment->deallocate(NULL, 1024));
+
+ // This should still return true.
+ EXPECT_TRUE(segment->allMemoryDeallocated());
+}
+
+TEST(MemorySegmentLocal, namedAddress) {
+ MemorySegmentLocal segment;
+ isc::util::test::checkSegmentNamedAddress(segment, true);
+}
+
+} // anonymous namespace
diff --git a/src/lib/util/tests/multi_threading_mgr_unittest.cc b/src/lib/util/tests/multi_threading_mgr_unittest.cc
new file mode 100644
index 0000000..400a7e9
--- /dev/null
+++ b/src/lib/util/tests/multi_threading_mgr_unittest.cc
@@ -0,0 +1,517 @@
+// Copyright (C) 2019-2023 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 <exceptions/exceptions.h>
+#include <util/multi_threading_mgr.h>
+#include <testutils/gtest_utils.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util;
+using namespace isc;
+
+/// @brief Fixture used to reset multi-threading before and after each test.
+struct MultiThreadingMgrTest : ::testing::Test {
+ /// @brief Constructor.
+ MultiThreadingMgrTest() {
+ MultiThreadingMgr::instance().apply(false, 0, 0);
+ }
+
+ /// @brief Destructor.
+ ~MultiThreadingMgrTest() {
+ MultiThreadingMgr::instance().apply(false, 0, 0);
+ }
+
+ /// @brief Check thread pool state.
+ ///
+ /// @param mode The multi-threading mode.
+ /// @param size The thread pool size.
+ /// @param count The thread queue size.
+ /// @param running The running threads count.
+ /// @param in_cs Flag which indicates if running inside critical section.
+ /// @param enabled Flag which indicates if thread pool is started and running.
+ /// @param paused Flag which indicates if thread pool is started and paused.
+ void checkState(bool mode, size_t size, size_t count, size_t running,
+ bool in_cs = false, bool enabled = false, bool paused = false) {
+ EXPECT_EQ(MultiThreadingMgr::instance().getMode(), mode);
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPoolSize(), size);
+ EXPECT_EQ(MultiThreadingMgr::instance().getPacketQueueSize(), count);
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPool().size(), running);
+ EXPECT_EQ(MultiThreadingMgr::instance().isInCriticalSection(), in_cs);
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPool().enabled(), enabled);
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPool().paused(), paused);
+ }
+};
+
+/// @brief Verifies that the default mode is false (MT disabled).
+TEST_F(MultiThreadingMgrTest, defaultMode) {
+ // MT should be disabled
+ EXPECT_FALSE(MultiThreadingMgr::instance().getMode());
+}
+
+/// @brief Verifies that the mode setter works.
+TEST_F(MultiThreadingMgrTest, setMode) {
+ // enable MT
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().setMode(true));
+ // MT should be enabled
+ EXPECT_TRUE(MultiThreadingMgr::instance().getMode());
+ // disable MT
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().setMode(false));
+ // MT should be disabled
+ EXPECT_FALSE(MultiThreadingMgr::instance().getMode());
+}
+
+/// @brief Verifies that accessing the thread pool works.
+TEST_F(MultiThreadingMgrTest, threadPool) {
+ // get the thread pool
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().getThreadPool());
+}
+
+/// @brief Verifies that the thread pool size setter works.
+TEST_F(MultiThreadingMgrTest, threadPoolSize) {
+ // default thread count is 0
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPoolSize(), 0);
+ // set thread count to 16
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().setThreadPoolSize(16));
+ // thread count should be 16
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPoolSize(), 16);
+ // set thread count to 0
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().setThreadPoolSize(0));
+ // thread count should be 0
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPoolSize(), 0);
+}
+
+/// @brief Verifies that the packet queue size setter works.
+TEST_F(MultiThreadingMgrTest, packetQueueSize) {
+ // default queue size is 0
+ EXPECT_EQ(MultiThreadingMgr::instance().getPacketQueueSize(), 0);
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPool().getMaxQueueSize(), 0);
+ // set queue size to 16
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().setPacketQueueSize(16));
+ // queue size should be 16
+ EXPECT_EQ(MultiThreadingMgr::instance().getPacketQueueSize(), 16);
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPool().getMaxQueueSize(), 16);
+ // set queue size to 0
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().setPacketQueueSize(0));
+ // queue size should be 0
+ EXPECT_EQ(MultiThreadingMgr::instance().getPacketQueueSize(), 0);
+ EXPECT_EQ(MultiThreadingMgr::instance().getThreadPool().getMaxQueueSize(), 0);
+}
+
+/// @brief Verifies that detecting thread count works.
+TEST_F(MultiThreadingMgrTest, detectThreadCount) {
+ // detecting thread count should work
+ EXPECT_NE(MultiThreadingMgr::detectThreadCount(), 0);
+}
+
+/// @brief Verifies that apply settings works.
+TEST_F(MultiThreadingMgrTest, applyConfig) {
+ checkState(false, 0, 0, 0);
+ // enable MT with 16 threads and queue size 256
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 16, 256));
+ checkState(true, 16, 256, 16, false, true);
+ // disable MT
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(false, 16, 256));
+ checkState(false, 0, 0, 0);
+ // enable MT with auto scaling
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 0, 0));
+ checkState(true, MultiThreadingMgr::detectThreadCount(), 0, MultiThreadingMgr::detectThreadCount(), false, true);
+ // disable MT
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(false, 0, 0));
+ checkState(false, 0, 0, 0);
+}
+
+/// @brief Verifies that the critical section flag works.
+TEST_F(MultiThreadingMgrTest, criticalSectionFlag) {
+ checkState(false, 0, 0, 0);
+ // exit critical section
+ EXPECT_THROW(MultiThreadingMgr::instance().exitCriticalSection(), InvalidOperation);
+ // critical section should be disabled
+ EXPECT_FALSE(MultiThreadingMgr::instance().isInCriticalSection());
+ // enter critical section
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().enterCriticalSection());
+ // critical section should be enabled
+ EXPECT_TRUE(MultiThreadingMgr::instance().isInCriticalSection());
+ // enable MT with 16 threads and queue size 256
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 16, 256));
+ checkState(true, 16, 256, 0, true);
+ // exit critical section
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().exitCriticalSection());
+ // critical section should be disabled
+ EXPECT_FALSE(MultiThreadingMgr::instance().isInCriticalSection());
+ // exit critical section
+ EXPECT_THROW(MultiThreadingMgr::instance().exitCriticalSection(), InvalidOperation);
+ // critical section should be disabled
+ EXPECT_FALSE(MultiThreadingMgr::instance().isInCriticalSection());
+ // disable MT
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(false, 0, 0));
+ checkState(false, 0, 0, 0);
+}
+
+/// @brief Verifies that the critical section works.
+TEST_F(MultiThreadingMgrTest, criticalSection) {
+ checkState(false, 0, 0, 0);
+ // apply multi-threading configuration with 16 threads and queue size 256
+ MultiThreadingMgr::instance().apply(true, 16, 256);
+ checkState(true, 16, 256, 16, false, true);
+ // use scope to test constructor and destructor
+ {
+ MultiThreadingCriticalSection cs;
+ checkState(true, 16, 256, 16, true, true, true);
+ // use scope to test constructor and destructor
+ {
+ MultiThreadingCriticalSection inner_cs;
+ checkState(true, 16, 256, 16, true, true, true);
+ }
+ checkState(true, 16, 256, 16, true, true, true);
+ }
+ checkState(true, 16, 256, 16, false, true);
+ // use scope to test constructor and destructor
+ {
+ MultiThreadingCriticalSection cs;
+ checkState(true, 16, 256, 16, true, true, true);
+ // apply multi-threading configuration with 64 threads and queue size 4
+ MultiThreadingMgr::instance().apply(true, 64, 4);
+ checkState(true, 64, 4, 0, true);
+ }
+ checkState(true, 64, 4, 64, false, true);
+ // use scope to test constructor and destructor
+ {
+ MultiThreadingCriticalSection cs;
+ checkState(true, 64, 4, 64, true, true, true);
+ // apply multi-threading configuration with 0 threads
+ MultiThreadingMgr::instance().apply(false, 64, 256);
+ checkState(false, 0, 0, 0, true);
+ }
+ checkState(false, 0, 0, 0);
+ // use scope to test constructor and destructor
+ {
+ MultiThreadingCriticalSection cs;
+ checkState(false, 0, 0, 0, true);
+ // use scope to test constructor and destructor
+ {
+ MultiThreadingCriticalSection inner_cs;
+ checkState(false, 0, 0, 0, true);
+ }
+ checkState(false, 0, 0, 0, true);
+ }
+ checkState(false, 0, 0, 0);
+ // use scope to test constructor and destructor
+ {
+ MultiThreadingCriticalSection cs;
+ checkState(false, 0, 0, 0, true);
+ // apply multi-threading configuration with 64 threads
+ MultiThreadingMgr::instance().apply(true, 64, 256);
+ checkState(true, 64, 256, 0, true);
+ }
+ checkState(true, 64, 256, 64, false, true);
+ // apply multi-threading configuration with 0 threads
+ MultiThreadingMgr::instance().apply(false, 0, 0);
+ checkState(false, 0, 0, 0);
+}
+
+/// @brief Checks that the lock works only when multi-threading is enabled and
+/// only during its lifetime.
+TEST(MultiThreadingLockTest, scope) {
+ // Check that the mutex is unlocked by default at first.
+ std::mutex mutex;
+ ASSERT_TRUE(mutex.try_lock());
+ mutex.unlock();
+
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().setMode(false));
+
+ // Check that the lock does not locks the mutex if multi-threading is disabled.
+ {
+ MultiThreadingLock lock(mutex);
+ ASSERT_TRUE(mutex.try_lock());
+ mutex.unlock();
+ }
+
+ // Check that the mutex is still unlocked when the lock goes out of scope.
+ ASSERT_TRUE(mutex.try_lock());
+ mutex.unlock();
+
+ EXPECT_NO_THROW(MultiThreadingMgr::instance().setMode(true));
+
+ // Check that the lock actively locks the mutex if multi-threading is enabled.
+ {
+ MultiThreadingLock lock(mutex);
+ ASSERT_FALSE(mutex.try_lock());
+ }
+
+ // Check that the mutex is unlocked when the lock goes out of scope.
+ ASSERT_TRUE(mutex.try_lock());
+ mutex.unlock();
+}
+
+/// @brief Test fixture for exercised CriticalSection callbacks.
+class CriticalSectionCallbackTest : public ::testing::Test {
+public:
+ /// @brief Constructor.
+ CriticalSectionCallbackTest() {
+ MultiThreadingMgr::instance().apply(false, 0, 0);
+ }
+
+ /// @brief Destructor.
+ ~CriticalSectionCallbackTest() {
+ MultiThreadingMgr::instance().apply(false, 0, 0);
+ }
+
+ /// @brief A callback that adds the value 1 to invocations lists.
+ void one() {
+ invocations_.push_back(1);
+ }
+
+ /// @brief A callback that adds the value 2 to invocations lists.
+ void two() {
+ invocations_.push_back(2);
+ }
+
+ /// @brief A callback that adds the value 3 to invocations lists.
+ void three() {
+ invocations_.push_back(3);
+ }
+
+ /// @brief A callback that adds the value 4 to invocations lists.
+ void four() {
+ invocations_.push_back(4);
+ }
+
+ /// @brief A callback that throws @ref isc::Exception which is ignored.
+ void ignoredException() {
+ isc_throw(isc::Exception, "ignored");
+ }
+
+ /// @brief A callback that throws @ref isc::MultiThreadingInvalidOperation
+ /// which is propagated to the scope of the
+ /// @ref MultiThreadingCriticalSection constructor.
+ void observedException() {
+ isc_throw(isc::MultiThreadingInvalidOperation, "observed");
+ }
+
+ /// @brief Indicates whether or not the DHCP thread pool is running.
+ ///
+ /// @return True if the pool is running, false otherwise.
+ bool isThreadPoolRunning() {
+ return (!MultiThreadingMgr::instance().getThreadPool().paused());
+ }
+
+ /// @brief Checks callback invocations over a series of nested
+ /// CriticalSections.
+ ///
+ /// @param entries A vector of the invocation values that should
+ /// be present after entry into the outermost CriticalSection. The
+ /// expected values should be in the order the callbacks were added
+ /// to the MultiThreadingMgr's list of callbacks.
+ /// @param exits A vector of the invocation values that should
+ /// be present after exiting the outermost CriticalSection. The
+ /// expected values should be in the order the callbacks were added
+ /// to the MultiThreadingMgr's list of callbacks.
+ /// @param should_throw The flag indicating if the CriticalSection should
+ /// throw, simulating a dead-lock scenario when a processing thread tries
+ /// to stop the thread pool.
+ void runCriticalSections(std::vector<int> entries, std::vector<int>exits,
+ bool should_throw = false) {
+ // Pool must be running.
+ ASSERT_TRUE(isThreadPoolRunning());
+
+ // Clear the invocations list.
+ invocations_.clear();
+
+ // Use scope to create nested CriticalSections.
+ if (!should_throw) {
+ // Enter a critical section.
+ MultiThreadingCriticalSection cs;
+
+ // Thread pool should be stopped.
+ ASSERT_FALSE(isThreadPoolRunning());
+
+ if (entries.size()) {
+ // We expect entry invocations.
+ ASSERT_EQ(invocations_.size(), entries.size());
+ ASSERT_EQ(invocations_, entries);
+ } else {
+ // We do not expect entry invocations.
+ ASSERT_FALSE(invocations_.size());
+ }
+
+ // Clear the invocations list.
+ invocations_.clear();
+
+ {
+ // Enter another CriticalSection.
+ MultiThreadingCriticalSection inner_cs;
+
+ // Thread pool should still be stopped.
+ ASSERT_FALSE(isThreadPoolRunning());
+
+ // We should not have had any callback invocations.
+ ASSERT_FALSE(invocations_.size());
+ }
+
+ // After exiting inner section, the thread pool should
+ // still be stopped.
+ ASSERT_FALSE(isThreadPoolRunning());
+
+ // We should not have had more callback invocations.
+ ASSERT_FALSE(invocations_.size());
+ } else {
+ ASSERT_THROW(MultiThreadingCriticalSection cs, MultiThreadingInvalidOperation);
+
+ if (entries.size()) {
+ // We expect entry invocations.
+ ASSERT_EQ(invocations_.size(), entries.size());
+ ASSERT_EQ(invocations_, entries);
+ } else {
+ // We do not expect entry invocations.
+ ASSERT_FALSE(invocations_.size());
+ }
+
+ // Clear the invocations list.
+ invocations_.clear();
+ }
+
+ // After exiting the outer section, the thread pool should
+ // match the thread count.
+ ASSERT_TRUE(isThreadPoolRunning());
+
+ if (exits.size()) {
+ // We expect exit invocations.
+ ASSERT_EQ(invocations_, exits);
+ } else {
+ // We do not expect exit invocations.
+ ASSERT_FALSE(invocations_.size());
+ }
+ }
+
+ /// @brief A list of values set by callback invocations.
+ std::vector<int> invocations_;
+};
+
+/// @brief Verifies critical section callback maintenance:
+/// catch invalid pairs, add pairs, remove pairs.
+TEST_F(CriticalSectionCallbackTest, addAndRemove) {
+ auto& mgr = MultiThreadingMgr::instance();
+
+ // Cannot add with a blank name.
+ ASSERT_THROW_MSG(mgr.addCriticalSectionCallbacks("", [](){}, [](){}, [](){}),
+ BadValue, "CSCallbackSetList - name cannot be empty");
+
+ // Cannot add with an empty check callback.
+ ASSERT_THROW_MSG(mgr.addCriticalSectionCallbacks("bad", nullptr, [](){}, [](){}),
+ BadValue, "CSCallbackSetList - check callback for bad cannot be empty");
+
+ // Cannot add with an empty exit callback.
+ ASSERT_THROW_MSG(mgr.addCriticalSectionCallbacks("bad", [](){}, nullptr, [](){}),
+ BadValue, "CSCallbackSetList - entry callback for bad cannot be empty");
+
+ // Cannot add with an empty exit callback.
+ ASSERT_THROW_MSG(mgr.addCriticalSectionCallbacks("bad", [](){}, [](){}, nullptr),
+ BadValue, "CSCallbackSetList - exit callback for bad cannot be empty");
+
+ // Should be able to add foo.
+ ASSERT_NO_THROW_LOG(mgr.addCriticalSectionCallbacks("foo", [](){}, [](){}, [](){}));
+
+ // Should not be able to add foo twice.
+ ASSERT_THROW_MSG(mgr.addCriticalSectionCallbacks("foo", [](){}, [](){}, [](){}),
+ BadValue, "CSCallbackSetList - callbacks for foo already exist");
+
+ // Should be able to add bar.
+ ASSERT_NO_THROW_LOG(mgr.addCriticalSectionCallbacks("bar", [](){}, [](){}, [](){}));
+
+ // Should be able to remove foo.
+ ASSERT_NO_THROW_LOG(mgr.removeCriticalSectionCallbacks("foo"));
+
+ // Should be able to remove foo twice without issue.
+ ASSERT_NO_THROW_LOG(mgr.removeCriticalSectionCallbacks("foo"));
+
+ // Should be able to remove all without issue.
+ ASSERT_NO_THROW_LOG(mgr.removeAllCriticalSectionCallbacks());
+}
+
+/// @brief Verifies that the critical section callbacks work.
+TEST_F(CriticalSectionCallbackTest, invocations) {
+ // get the thread pool instance
+ auto& thread_pool = MultiThreadingMgr::instance().getThreadPool();
+ // thread pool should be stopped
+ EXPECT_EQ(thread_pool.size(), 0);
+
+ // Add two sets of CriticalSection call backs.
+ MultiThreadingMgr::instance().addCriticalSectionCallbacks("oneAndTwo",
+ std::bind(&CriticalSectionCallbackTest::ignoredException, this),
+ std::bind(&CriticalSectionCallbackTest::one, this),
+ std::bind(&CriticalSectionCallbackTest::two, this));
+
+ MultiThreadingMgr::instance().addCriticalSectionCallbacks("threeAndFour",
+ std::bind(&CriticalSectionCallbackTest::ignoredException, this),
+ std::bind(&CriticalSectionCallbackTest::three, this),
+ std::bind(&CriticalSectionCallbackTest::four, this));
+
+ // Apply multi-threading configuration with 16 threads and queue size 256.
+ MultiThreadingMgr::instance().apply(true, 16, 256);
+
+ // Make three passes over nested CriticalSections to ensure
+ // callbacks execute at the appropriate times and we can do
+ // so repeatedly.
+ for (int i = 0; i < 3; ++i) {
+ runCriticalSections({1 ,3}, {4, 2});
+ }
+
+ // Now remove the first set of callbacks.
+ MultiThreadingMgr::instance().removeCriticalSectionCallbacks("oneAndTwo");
+
+ // Retest CriticalSections.
+ runCriticalSections({3}, {4});
+
+ // Now remove the remaining callbacks.
+ MultiThreadingMgr::instance().removeAllCriticalSectionCallbacks();
+
+ // Retest CriticalSections.
+ runCriticalSections({}, {});
+}
+
+/// @brief Verifies that the critical section callbacks work.
+TEST_F(CriticalSectionCallbackTest, invocationsWithExceptions) {
+ // get the thread pool instance
+ auto& thread_pool = MultiThreadingMgr::instance().getThreadPool();
+ // thread pool should be stopped
+ EXPECT_EQ(thread_pool.size(), 0);
+
+ // Apply multi-threading configuration with 16 threads and queue size 256.
+ MultiThreadingMgr::instance().apply(true, 16, 256);
+
+ // Add two sets of CriticalSection call backs.
+ MultiThreadingMgr::instance().addCriticalSectionCallbacks("observed",
+ std::bind(&CriticalSectionCallbackTest::observedException, this),
+ std::bind(&CriticalSectionCallbackTest::one, this),
+ std::bind(&CriticalSectionCallbackTest::two, this));
+
+ MultiThreadingMgr::instance().addCriticalSectionCallbacks("ignored",
+ std::bind(&CriticalSectionCallbackTest::ignoredException, this),
+ std::bind(&CriticalSectionCallbackTest::three, this),
+ std::bind(&CriticalSectionCallbackTest::four, this));
+
+ // Make three passes over nested CriticalSections to ensure
+ // callbacks execute at the appropriate times and we can do
+ // so repeatedly.
+ for (int i = 0; i < 3; ++i) {
+ runCriticalSections({}, {}, true);
+ }
+
+ // Now remove the first set of callbacks.
+ MultiThreadingMgr::instance().removeCriticalSectionCallbacks("observed");
+
+ // Retest CriticalSections.
+ runCriticalSections({3}, {4});
+
+ // Now remove the remaining callbacks.
+ MultiThreadingMgr::instance().removeAllCriticalSectionCallbacks();
+
+ // Retest CriticalSections.
+ runCriticalSections({}, {});
+}
diff --git a/src/lib/util/tests/optional_unittest.cc b/src/lib/util/tests/optional_unittest.cc
new file mode 100644
index 0000000..71830ab
--- /dev/null
+++ b/src/lib/util/tests/optional_unittest.cc
@@ -0,0 +1,162 @@
+// Copyright (C) 2015-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 <util/optional.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace isc::util;
+
+// This test checks that the constructors work correctly.
+TEST(OptionalTest, constructor) {
+ // Explicitly set a value via constructor. The value becomes
+ // specified.
+ Optional<int> value1(10);
+ EXPECT_EQ(10, value1.get());
+ EXPECT_FALSE(value1.unspecified());
+
+ // Do not set a value in a constructor. The value should be
+ // unspecified.
+ Optional<int> value2;
+ EXPECT_EQ(0, value2.get());
+ EXPECT_TRUE(value2.unspecified());
+
+ // Use the non-default value for second parameter.
+ Optional<bool> value3(true, true);
+ EXPECT_TRUE(value3.get());
+ EXPECT_TRUE(value3.unspecified());
+}
+
+// This test checks if the constructors for a string value
+// work correctly.
+TEST(OptionalTest, constructorString) {
+ Optional<std::string> value1("foo");
+ EXPECT_EQ("foo", value1.get());
+ EXPECT_FALSE(value1.unspecified());
+
+ Optional<std::string> value2;
+ EXPECT_TRUE(value2.get().empty());
+ EXPECT_TRUE(value2.unspecified());
+}
+
+// This test checks if the assignment operator assigning an actual
+// value to the optional value works as expected.
+TEST(OptionalTest, assignValue) {
+ Optional<int> value(10, true);
+ EXPECT_EQ(10, value.get());
+ EXPECT_TRUE(value.unspecified());
+
+ // Assign a new value.
+ value = 111;
+ EXPECT_EQ(111, value.get());
+ EXPECT_FALSE(value.unspecified());
+
+ // Assign another value.
+ value = 1000;
+ EXPECT_EQ(1000, value.get());
+ EXPECT_FALSE(value.unspecified());
+}
+
+// This test checks if the assignment operator assigning an actual
+// string value to the optional value works as expected.
+TEST(OptionalTest, assignStringValue) {
+ Optional<std::string> value("foo");
+ EXPECT_EQ("foo", value.get());
+ EXPECT_FALSE(value.unspecified());
+
+ value = "bar";
+ EXPECT_EQ("bar", value.get());
+ EXPECT_FALSE(value.unspecified());
+
+ value = "foobar";
+ EXPECT_EQ("foobar", value.get());
+ EXPECT_FALSE(value.unspecified());
+}
+
+// This test checks that it is possible to modify the flag that indicates
+// if the value is specified or unspecified.
+TEST(OptionalTest, modifyUnspecified) {
+ Optional<int> value;
+ EXPECT_TRUE(value.unspecified());
+
+ value.unspecified(false);
+ EXPECT_FALSE(value.unspecified());
+
+ value.unspecified(true);
+ EXPECT_TRUE(value.unspecified());
+}
+
+// This test checks if the type case operator returns correct value.
+TEST(OptionalTest, typeCastOperator) {
+ Optional<int> value(-10);
+ EXPECT_EQ(-10, value.get());
+ EXPECT_FALSE(value.unspecified());
+
+ int actual = value;
+ EXPECT_EQ(-10, actual);
+}
+
+// This test checks if the type case operator returns correct string
+// value.
+TEST(OptionalTest, stringCastOperator) {
+ Optional<std::string> value("xyz");
+ EXPECT_EQ("xyz", value.get());
+ EXPECT_FALSE(value.unspecified());
+
+ std::string actual = value;
+ EXPECT_EQ("xyz", actual);
+}
+
+// This test checks that the equality operators work as expected.
+TEST(OptionalTest, equality) {
+ int exp_value = 1234;
+ Optional<int> value(1234);
+ EXPECT_TRUE(value == exp_value);
+ EXPECT_FALSE(value != exp_value);
+}
+
+// This test checks that the equality operators for strings work as
+// expected.
+TEST(OptionalTest, stringEquality) {
+ const char* exp_value = "foo";
+ Optional<std::string> value("foo");
+ EXPECT_TRUE(value == exp_value);
+ EXPECT_FALSE(value != exp_value);
+}
+
+// This test checks that an exception is thrown when calling an empty()
+// method on non-string optional value.
+TEST(OptionalTest, empty) {
+ Optional<int> value(10);
+ EXPECT_THROW(value.empty(), isc::InvalidOperation);
+}
+
+// This test checks that no exception is thrown when calling an empty()
+// method on string optional value and that it returns an expected
+// boolean value.
+TEST(OptionalTest, stringEmpty) {
+ Optional<std::string> value("foo");
+ bool is_empty = true;
+ ASSERT_NO_THROW(is_empty = value.empty());
+ EXPECT_FALSE(is_empty);
+
+ value = "";
+ ASSERT_NO_THROW(is_empty = value.empty());
+ EXPECT_TRUE(is_empty);
+}
+
+// Checks that the valueOr function works correctly.
+TEST(OptionalTest, valueOr) {
+ Optional<std::string> optional("foo");
+ EXPECT_EQ(optional.valueOr("bar"), "foo");
+
+ Optional<std::string> unspecified_optional;
+ EXPECT_EQ(unspecified_optional.valueOr("bar"), "bar");
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/util/tests/pid_file_unittest.cc b/src/lib/util/tests/pid_file_unittest.cc
new file mode 100644
index 0000000..5f00d72
--- /dev/null
+++ b/src/lib/util/tests/pid_file_unittest.cc
@@ -0,0 +1,206 @@
+// Copyright (C) 2015-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <util/pid_file.h>
+#include <gtest/gtest.h>
+#include <fstream>
+#include <signal.h>
+#include <stdint.h>
+
+namespace {
+using namespace isc::util;
+
+// Filenames used for testing.
+const char* TESTNAME = "pid_file.test";
+
+class PIDFileTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor
+ PIDFileTest() = default;
+
+ /// @brief Destructor
+ virtual ~PIDFileTest() = default;
+
+ /// @brief Prepends the absolute path to the file specified
+ /// as an argument.
+ ///
+ /// @param filename Name of the file.
+ /// @return Absolute path to the test file.
+ static std::string absolutePath(const std::string& filename);
+
+ /// @brief Generate a random number for use as a PID
+ ///
+ /// @param start - the start of the range we want the PID in
+ /// @param range - the size of the range for our PID
+ ///
+ /// @return returns a random value between start and start + range
+ int randomizePID(const uint32_t start, const uint32_t range) {
+ int pid;
+
+ for (pid = (random() % range) + start;
+ kill(pid, 0) == 0;
+ ++pid)
+ ;
+
+ return (pid);
+ }
+
+protected:
+ /// @brief Removes any old test files before the test
+ virtual void SetUp() {
+ removeTestFile();
+ }
+
+ /// @brief Removes any remaining test files after the test
+ virtual void TearDown() {
+ removeTestFile();
+ }
+
+private:
+ /// @brief Removes any remaining test files
+ void removeTestFile() const {
+ static_cast<void>(remove(absolutePath(TESTNAME).c_str()));
+ }
+
+};
+
+std::string
+PIDFileTest::absolutePath(const std::string& filename) {
+ std::ostringstream s;
+ s << TEST_DATA_BUILDDIR << "/" << filename;
+
+ return (s.str());
+}
+
+/// @brief Test file writing and deletion. Start by removing
+/// any leftover file. Then write a known PID to the file and
+/// attempt to read the file and verify the PID. Next write
+/// a second and verify a second PID to verify that an existing
+/// file is properly overwritten.
+
+TEST_F(PIDFileTest, writeAndDelete) {
+ PIDFile pid_file(absolutePath(TESTNAME));
+ std::ifstream fs;
+ int pid(0);
+
+ // Write a known process id
+ pid_file.write(10);
+
+ // Read the file and compare the pid
+ fs.open(absolutePath(TESTNAME).c_str(), std::ifstream::in);
+ fs >> pid;
+ EXPECT_TRUE(fs.good());
+ EXPECT_EQ(pid, 10);
+ fs.close();
+
+ // Write a second known process id
+ pid_file.write(20);
+
+ // And compare the second pid
+ fs.open(absolutePath(TESTNAME).c_str(), std::ifstream::in);
+ fs >> pid;
+ EXPECT_TRUE(fs.good());
+ EXPECT_EQ(pid, 20);
+ fs.close();
+
+ // Delete the file
+ pid_file.deleteFile();
+
+ // And verify that it's gone
+ fs.open(absolutePath(TESTNAME).c_str(), std::ifstream::in);
+ EXPECT_FALSE(fs.good());
+ fs.close();
+}
+
+/// @brief Test checking a PID. Write the PID of the current
+/// process to the PID file then verify that check indicates
+/// the process is running.
+TEST_F(PIDFileTest, pidInUse) {
+ PIDFile pid_file(absolutePath(TESTNAME));
+
+ // Write the current PID
+ pid_file.write();
+
+ // Check if we think the process is running
+ EXPECT_EQ(getpid(), pid_file.check());
+}
+
+/// @brief Test checking a PID. Write a PID that isn't in use
+/// to the PID file and verify that check indicates the process
+/// isn't running. The PID may get used between when we select it
+/// and write the file and when we check it. To minimize false
+/// errors if the first call to check fails we try again with a
+/// different range of values and only if both attempts fail do
+/// we declare the test to have failed.
+TEST_F(PIDFileTest, pidNotInUse) {
+ PIDFile pid_file(absolutePath(TESTNAME));
+ int pid;
+
+ // get a pid between 10000 and 20000
+ pid = randomizePID(10000, 10000);
+
+ // write it
+ pid_file.write(pid);
+
+ // Check to see if we think the process is running
+ if (pid_file.check() == 0) {
+ return;
+ }
+
+ // get a pid between 40000 and 50000
+ pid = randomizePID(10000, 40000);
+
+ // write it
+ pid_file.write(pid);
+
+ // Check to see if we think the process is running
+ EXPECT_EQ(0, pid_file.check());
+}
+
+/// @brief Test checking a PID. Write garbage to the PID file
+/// and verify that check throws an error. In this situation
+/// the caller should probably log an error and may decide to
+/// continue or not depending on the requirements.
+TEST_F(PIDFileTest, pidGarbage) {
+ PIDFile pid_file(absolutePath(TESTNAME));
+ std::ofstream fs;
+
+ // Open the file and write garbage to it
+ fs.open(absolutePath(TESTNAME).c_str(), std::ofstream::out);
+ fs << "text" << std::endl;
+ fs.close();
+
+ // Run the check, we expect to get an exception
+ EXPECT_THROW(pid_file.check(), PIDCantReadPID);
+}
+
+/// @brief Test failing to write a file.
+TEST_F(PIDFileTest, pidWriteFail) {
+ PIDFile pid_file(absolutePath(TESTNAME));
+
+ // Create the test file and change it's permission bits
+ // so we can't write to it.
+ pid_file.write(10);
+ chmod(absolutePath(TESTNAME).c_str(), S_IRUSR);
+
+ // Now try a write to the file, expecting an exception
+ EXPECT_THROW(pid_file.write(10), PIDFileError);
+
+ // Don't forget to restore the write right for the next test
+ chmod(absolutePath(TESTNAME).c_str(), S_IRUSR | S_IWUSR);
+}
+
+/// @brief Test deleting a file that doesn't exist
+TEST_F(PIDFileTest, noDeleteFile) {
+ PIDFile pid_file(absolutePath(TESTNAME));
+
+ // Delete a file we haven't created
+ pid_file.deleteFile();
+}
+} // end of anonymous namespace
diff --git a/src/lib/util/tests/range_utilities_unittest.cc b/src/lib/util/tests/range_utilities_unittest.cc
new file mode 100644
index 0000000..ce94a38
--- /dev/null
+++ b/src/lib/util/tests/range_utilities_unittest.cc
@@ -0,0 +1,50 @@
+// Copyright (C) 2010-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 <stdint.h>
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+#include <vector>
+
+#include <util/range_utilities.h>
+
+using namespace std;
+using namespace isc::util;
+
+TEST(RangeUtilitiesTest, isZero) {
+
+ vector<uint8_t> vec(32,0);
+
+ EXPECT_TRUE(isRangeZero(vec.begin(), vec.end()));
+
+ EXPECT_TRUE(isRangeZero(vec.begin(), vec.begin()+1));
+
+ vec[5] = 1;
+ EXPECT_TRUE(isRangeZero(vec.begin(), vec.begin()+5));
+ EXPECT_FALSE(isRangeZero(vec.begin(), vec.begin()+6));
+}
+
+TEST(RangeUtilitiesTest, randomFill) {
+
+ srandom(time(NULL));
+
+ vector<uint8_t> vec1(16,0);
+ vector<uint8_t> vec2(16,0);
+
+ // Testing if returned value is actually random is extraordinary difficult.
+ // Let's just generate bunch of vectors and see if we get the same
+ // value. If we manage to do that in 100 tries, pseudo-random generator
+ // really sucks.
+ fillRandom(vec1.begin(), vec1.end());
+ for (int i=0; i<100; i++) {
+ fillRandom(vec2.begin(), vec2.end());
+ if (vec1 == vec2)
+ FAIL();
+ }
+
+}
diff --git a/src/lib/util/tests/readwrite_mutex_unittest.cc b/src/lib/util/tests/readwrite_mutex_unittest.cc
new file mode 100644
index 0000000..f5ba98d
--- /dev/null
+++ b/src/lib/util/tests/readwrite_mutex_unittest.cc
@@ -0,0 +1,470 @@
+// Copyright (C) 2020-2024 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 <util/readwrite_mutex.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/make_shared.hpp>
+
+#include <chrono>
+#include <iostream>
+#include <thread>
+#include <unistd.h>
+
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+/// @brief Test Fixture for testing read-write mutexes.
+///
+/// Each not basic test follows the same schema:
+/// @code
+/// main thread work thread
+/// <- started
+/// work ->
+/// enter guard
+/// <- done
+/// terminate ->
+/// return
+/// join
+/// @endcode
+class ReadWriteMutexTest : public ::testing::Test {
+public:
+
+ /// @brief Read-write mutex.
+ ReadWriteMutex rw_mutex_;
+
+ /// @brief Synchronization objects for work threads.
+ struct Sync {
+ bool started = false;
+ mutex started_mtx;
+ condition_variable started_cv;
+ bool work = false;
+ mutex work_mtx;
+ condition_variable work_cv;
+ bool done = false;
+ mutex done_mtx;
+ condition_variable done_cv;
+ bool terminate = false;
+ mutex terminate_mtx;
+ condition_variable terminate_cv;
+ } syncr_, syncw_;
+
+ /// @brief Body of the reader.
+ ///
+ /// @param rw_mutex The read-write mutex.
+ /// @param syncr The reader synchronization object.
+ void reader(ReadWriteMutex& rw_mutex, Sync& syncr) {
+ // Take mutex to wait for main thread signals.
+ unique_lock<mutex> terminate_lock(syncr.terminate_mtx);
+
+ // Signal the thread started.
+ {
+ lock_guard<mutex> lock(syncr.started_mtx);
+ syncr.started = true;
+ }
+
+ // Wait for work.
+ {
+ unique_lock<mutex> work_lock(syncr.work_mtx);
+ // When this thread starts waiting, the main thread is resumed.
+ syncr.started_cv.notify_one();
+ syncr.work_cv.wait(work_lock, [&](){ return syncr.work; });
+ }
+
+ {
+ // Enter a read lock guard.
+ ReadLockGuard rwlock(rw_mutex);
+
+ // Signal the thread holds the guard.
+ {
+ lock_guard<mutex> done_lock(syncr.done_mtx);
+ syncr.done = true;
+ }
+ syncr.done_cv.notify_one();
+ }
+
+ // Wait to terminate.
+ syncr.terminate_cv.wait(terminate_lock, [&](){ return syncr.terminate; });
+ }
+
+ /// @brief Body of the writer.
+ ///
+ /// @param rw_mutex The read-write mutex.
+ /// @param syncw The writer synchronization object.
+ void writer(ReadWriteMutex& rw_mutex, Sync& syncw) {
+ // Take mutex to wait for main thread signals.
+ unique_lock<mutex> terminate_lock(syncw.terminate_mtx);
+
+ // Signal the thread started.
+ {
+ lock_guard<mutex> lock(syncw.started_mtx);
+ syncw.started = true;
+ }
+
+ // Wait for work.
+ {
+ unique_lock<mutex> work_lock(syncw.work_mtx);
+ // When this thread starts waiting, the main thread is resumed.
+ syncw.started_cv.notify_one();
+ syncw.work_cv.wait(work_lock, [&](){ return syncw.work; });
+ }
+
+ {
+ // Enter a write lock guard.
+ WriteLockGuard rwlock(rw_mutex);
+
+ // Signal the thread holds the guard.
+ {
+ lock_guard<mutex> done_lock(syncw.done_mtx);
+ syncw.done = true;
+ }
+ syncw.done_cv.notify_one();
+ }
+
+ // Wait to terminate.
+ syncw.terminate_cv.wait(terminate_lock, [&](){ return syncw.terminate; });
+ }
+};
+
+// Verify basic read lock guard.
+TEST_F(ReadWriteMutexTest, basicRead) {
+ ReadLockGuard lock(rw_mutex_);
+}
+
+// Verify basic write lock guard.
+TEST_F(ReadWriteMutexTest, basicWrite) {
+ WriteLockGuard lock(rw_mutex_);
+}
+
+// Verify read lock guard using a thread.
+TEST_F(ReadWriteMutexTest, read) {
+ // Take mutex to wait for work thread signals.
+ boost::shared_ptr<std::thread> thread;
+ {
+ unique_lock<mutex> started_lock(syncr_.started_mtx);
+
+ // Create a work thread.
+ thread = boost::make_shared<std::thread>([this](){ reader(rw_mutex_, syncr_); });
+
+ // Wait work thread to start.
+ syncr_.started_cv.wait(started_lock, [this](){ return syncr_.started; });
+
+ unique_lock<mutex> done_lock(syncr_.done_mtx);
+
+ // Signal the thread to work.
+ {
+ lock_guard<mutex> work_lock(syncr_.work_mtx);
+ syncr_.work = true;
+ }
+ syncr_.work_cv.notify_one();
+
+ // Wait thread to hold the read lock.
+ syncr_.done_cv.wait(done_lock, [this](){ return syncr_.done; });
+ }
+
+ // Signal the thread to terminate.
+ {
+ lock_guard<mutex> terminate_lock(syncr_.terminate_mtx);
+ syncr_.terminate = true;
+ }
+ syncr_.terminate_cv.notify_one();
+
+ // Join the thread.
+ thread->join();
+}
+
+// Verify write lock guard using a thread.
+TEST_F(ReadWriteMutexTest, write) {
+ // Take mutex to wait for work thread signals.
+ boost::shared_ptr<std::thread> thread;
+ {
+ unique_lock<mutex> started_lock(syncw_.started_mtx);
+
+ // Create a work thread.
+ thread = boost::make_shared<std::thread>([this](){ writer(rw_mutex_, syncw_); });
+
+ // Wait work thread to start.
+ syncw_.started_cv.wait(started_lock, [this](){ return syncw_.started; });
+
+ unique_lock<mutex> done_lock(syncw_.done_mtx);
+
+ // Signal the thread to work.
+ {
+ lock_guard<mutex> work_lock(syncw_.work_mtx);
+ syncw_.work = true;
+ }
+ syncw_.work_cv.notify_one();
+
+ // Wait thread to hold the write lock.
+ syncw_.done_cv.wait(done_lock, [this](){ return syncw_.done; });
+ }
+
+ // Signal the thread to terminate.
+ {
+ lock_guard<mutex> terminate_lock(syncw_.terminate_mtx);
+ syncw_.terminate = true;
+ }
+ syncw_.terminate_cv.notify_one();
+
+ // Join the thread.
+ thread->join();
+}
+
+// Verify read lock guard can be acquired by multiple threads.
+TEST_F(ReadWriteMutexTest, readRead) {
+ // Take mutex to wait for work thread signals.
+ boost::shared_ptr<std::thread> thread;
+ {
+ unique_lock<mutex> started_lock(syncr_.started_mtx);
+
+ // Create a work thread.
+ thread = boost::make_shared<std::thread>([this](){ reader(rw_mutex_, syncr_); });
+
+ // Enter a read lock guard.
+ ReadLockGuard rwlock(rw_mutex_);
+
+ // Wait work thread to start.
+ syncr_.started_cv.wait(started_lock, [this](){ return syncr_.started; });
+
+ unique_lock<mutex> done_lock(syncr_.done_mtx);
+
+ // Signal the thread to work.
+ {
+ lock_guard<mutex> work_lock(syncr_.work_mtx);
+ syncr_.work = true;
+ }
+ syncr_.work_cv.notify_one();
+
+ // Wait thread to hold the read lock.
+ syncr_.done_cv.wait(done_lock, [this](){ return syncr_.done; });
+ }
+
+ // Signal the thread to terminate.
+ {
+ lock_guard<mutex> terminate_lock(syncr_.terminate_mtx);
+ syncr_.terminate = true;
+ }
+ syncr_.terminate_cv.notify_one();
+
+ // Join the thread.
+ thread->join();
+}
+
+// Verify write lock guard is exclusive of a reader.
+TEST_F(ReadWriteMutexTest, readWrite) {
+ // Take mutex to wait for work thread signals.
+ boost::shared_ptr<std::thread> thread;
+ {
+ unique_lock<mutex> started_lock(syncw_.started_mtx);
+
+ // Create a work thread.
+ thread = boost::make_shared<std::thread>([this](){ writer(rw_mutex_, syncw_); });
+
+ // Wait work thread to start.
+ syncw_.started_cv.wait(started_lock, [this](){ return syncw_.started; });
+
+ unique_lock<mutex> done_lock(syncw_.done_mtx);
+
+ {
+ // Enter a read lock guard.
+ ReadLockGuard rwlock(rw_mutex_);
+
+ // Signal the thread to work.
+ {
+ lock_guard<mutex> work_lock(syncw_.work_mtx);
+ syncw_.work = true;
+ }
+ syncw_.work_cv.notify_one();
+
+ // Verify the work thread is waiting for the write lock.
+ cout << "pausing for one second" << std::endl;
+ bool ret = syncw_.done_cv.wait_for(done_lock, chrono::seconds(1), [this](){ return syncw_.done; });
+
+ EXPECT_FALSE(syncw_.done);
+ EXPECT_FALSE(ret);
+
+ // Exiting the read lock guard.
+ }
+
+ // Wait thread to hold the write lock.
+ syncw_.done_cv.wait(done_lock, [this](){ return syncw_.done; });
+ }
+
+ // Signal the thread to terminate.
+ {
+ lock_guard<mutex> terminate_lock(syncw_.terminate_mtx);
+ syncw_.terminate = true;
+ }
+ syncw_.terminate_cv.notify_one();
+
+ // Join the thread.
+ thread->join();
+}
+
+// Verify write lock guard is exclusive of a writer.
+TEST_F(ReadWriteMutexTest, writeWrite) {
+ // Take mutex to wait for work thread signals.
+ boost::shared_ptr<std::thread> thread;
+ {
+ unique_lock<mutex> started_lock(syncw_.started_mtx);
+
+ // Create a work thread.
+ thread = boost::make_shared<std::thread>([this](){ writer(rw_mutex_, syncw_); });
+
+ // Wait work thread to start.
+ syncw_.started_cv.wait(started_lock, [this](){ return syncw_.started; });
+
+ unique_lock<mutex> done_lock(syncw_.done_mtx);
+
+ {
+ // Enter a write lock guard.
+ WriteLockGuard rwlock(rw_mutex_);
+
+ // Signal the thread to work.
+ {
+ lock_guard<mutex> work_lock(syncw_.work_mtx);
+ syncw_.work = true;
+ }
+ syncw_.work_cv.notify_one();
+
+ // Verify the work thread is waiting for the write lock.
+ cout << "pausing for one second" << std::endl;
+ bool ret = syncw_.done_cv.wait_for(done_lock, chrono::seconds(1), [this](){ return syncw_.done; });
+
+ EXPECT_FALSE(syncw_.done);
+ EXPECT_FALSE(ret);
+
+ // Exiting the write lock guard.
+ }
+
+ // Wait thread to hold the write lock.
+ syncw_.done_cv.wait(done_lock, [this](){ return syncw_.done; });
+ }
+
+ // Signal the thread to terminate.
+ {
+ lock_guard<mutex> terminate_lock(syncw_.terminate_mtx);
+ syncw_.terminate = true;
+ }
+ syncw_.terminate_cv.notify_one();
+
+ // Join the thread.
+ thread->join();
+}
+
+// Verify that a writer has the preference.
+TEST_F(ReadWriteMutexTest, readWriteRead) {
+ // Take mutex to wait for work thread signals.
+ boost::shared_ptr<std::thread> threadw;
+ {
+ unique_lock<mutex> startedw_lock(syncw_.started_mtx);
+
+ // First thread is a writer.
+ threadw = boost::make_shared<std::thread>([this](){ writer(rw_mutex_, syncw_); });
+
+ // Wait work thread to start.
+ syncw_.started_cv.wait(startedw_lock, [this](){ return syncw_.started; });
+ }
+
+ boost::shared_ptr<std::thread> threadr;
+ {
+ unique_lock<mutex> startedr_lock(syncr_.started_mtx);
+
+ // Second thread is a reader.
+ threadr = boost::make_shared<std::thread>([this](){ reader(rw_mutex_, syncr_); });
+
+ // Wait work thread to start.
+ syncr_.started_cv.wait(startedr_lock, [this](){ return syncr_.started; });
+ }
+
+ {
+ unique_lock<mutex> donew_lock(syncw_.done_mtx);
+ {
+ // Enter a read lock guard.
+ ReadLockGuard rwlock(rw_mutex_);
+
+ // Signal the writer thread to work.
+ {
+ lock_guard<mutex> work_lock(syncw_.work_mtx);
+ syncw_.work = true;
+ }
+ syncw_.work_cv.notify_one();
+
+ // Verify the writer thread is waiting for the write lock.
+ cout << "pausing for one second" << std::endl;
+ bool ret = syncw_.done_cv.wait_for(donew_lock, chrono::seconds(1), [this](){ return syncw_.done; });
+
+ EXPECT_FALSE(syncw_.done);
+ EXPECT_FALSE(ret);
+
+ {
+ unique_lock<mutex> doner_lock(syncr_.done_mtx);
+
+ // Signal the reader thread to work.
+ {
+ lock_guard<mutex> work_lock(syncr_.work_mtx);
+ syncr_.work = true;
+ }
+ syncr_.work_cv.notify_one();
+
+ // Verify the reader thread is waiting for the read lock.
+ cout << "pausing for one second" << std::endl;
+ ret = syncr_.done_cv.wait_for(doner_lock, chrono::seconds(1), [this](){ return syncr_.done; });
+
+ EXPECT_FALSE(syncr_.done);
+ EXPECT_FALSE(ret);
+ }
+ // Exiting the read lock guard.
+ }
+
+ {
+ unique_lock<mutex> doner_lock(syncr_.done_mtx);
+ // Verify the reader thread is still waiting for the read lock.
+ cout << "pausing for one second" << std::endl;
+ bool ret = syncr_.done_cv.wait_for(doner_lock, chrono::seconds(1), [this](){ return syncr_.done; });
+
+ EXPECT_FALSE(syncr_.done);
+ EXPECT_FALSE(ret);
+ }
+
+ // Wait writer thread to hold the write lock.
+ syncw_.done_cv.wait(donew_lock, [this](){ return syncw_.done; });
+ }
+
+ {
+ unique_lock<mutex> doner_lock(syncr_.done_mtx);
+ // Wait reader thread to hold the read lock.
+ syncr_.done_cv.wait(doner_lock, [this](){ return syncr_.done; });
+ }
+
+ // Signal the writer thread to terminate.
+ {
+ lock_guard<mutex> terminate_lock(syncw_.terminate_mtx);
+ syncw_.terminate = true;
+ }
+ syncw_.terminate_cv.notify_one();
+
+ // Join the writer thread.
+ threadw->join();
+
+ // Signal the reader thread to terminate.
+ {
+ lock_guard<mutex> terminate_lock(syncr_.terminate_mtx);
+ syncr_.terminate = true;
+ }
+ syncr_.terminate_cv.notify_one();
+
+ // Join the reader thread.
+ threadr->join();
+}
+
+}
diff --git a/src/lib/util/tests/run_unittests.cc b/src/lib/util/tests/run_unittests.cc
new file mode 100644
index 0000000..1e13878
--- /dev/null
+++ b/src/lib/util/tests/run_unittests.cc
@@ -0,0 +1,18 @@
+// Copyright (C) 2011-2024 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 <util/unittests/run_all.h>
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/util/tests/staged_value_unittest.cc b/src/lib/util/tests/staged_value_unittest.cc
new file mode 100644
index 0000000..bcfc677
--- /dev/null
+++ b/src/lib/util/tests/staged_value_unittest.cc
@@ -0,0 +1,106 @@
+// Copyright (C) 2015-2017 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 <util/staged_value.h>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace isc::util;
+
+// This test verifies that the value can be assigned and committed.
+TEST(StagedValueTest, assignAndCommit) {
+ // Initially the value should be set to a default
+ StagedValue<int> value;
+ ASSERT_EQ(0, value.getValue());
+
+ // Set the new value without committing it and make sure it
+ // can be retrieved.
+ value.setValue(4);
+ ASSERT_EQ(4, value.getValue());
+
+ // Commit the value and make sure it still can be retrieved.
+ value.commit();
+ ASSERT_EQ(4, value.getValue());
+
+ // Set new value and retrieve it.
+ value.setValue(10);
+ ASSERT_EQ(10, value.getValue());
+
+ // Do it again and commit it.
+ value.setValue(20);
+ ASSERT_EQ(20, value.getValue());
+ value.commit();
+ EXPECT_EQ(20, value.getValue());
+}
+
+// This test verifies that the value can be reverted if it hasn't been
+// committed.
+TEST(StagedValueTest, revert) {
+ // Set the value and commit.
+ StagedValue<int> value;
+ value.setValue(123);
+ value.commit();
+
+ // Set new value and do not commit.
+ value.setValue(500);
+ // The new value should be the one returned.
+ ASSERT_EQ(500, value.getValue());
+ // But, reverting gets us back to original value.
+ value.revert();
+ EXPECT_EQ(123, value.getValue());
+ // Reverting again doesn't have any effect.
+ value.revert();
+ EXPECT_EQ(123, value.getValue());
+}
+
+// This test verifies that the value can be restored to an original one.
+TEST(StagedValueTest, reset) {
+ // Set the new value and commit.
+ StagedValue<int> value;
+ value.setValue(123);
+ value.commit();
+
+ // Override the value but do not commit.
+ value.setValue(500);
+
+ // Resetting should take us back to default value.
+ value.reset();
+ EXPECT_EQ(0, value.getValue());
+ value.revert();
+ EXPECT_EQ(0, value.getValue());
+}
+
+// This test verifies that second commit doesn't modify a value.
+TEST(StagedValueTest, commit) {
+ // Set the value and commit.
+ StagedValue<int> value;
+ value.setValue(123);
+ value.commit();
+
+ // Second commit should have no effect.
+ value.commit();
+ EXPECT_EQ(123, value.getValue());
+}
+
+// This test checks that type conversion operator works correctly.
+TEST(StagedValueTest, conversionOperator) {
+ StagedValue<int> value;
+ value.setValue(244);
+ EXPECT_EQ(244, value);
+}
+
+// This test checks that the assignment operator works correctly.
+TEST(StagedValueTest, assignmentOperator) {
+ StagedValue<int> value;
+ value = 111;
+ EXPECT_EQ(111, value);
+}
+
+
+} // end of anonymous namespace
diff --git a/src/lib/util/tests/state_model_unittest.cc b/src/lib/util/tests/state_model_unittest.cc
new file mode 100644
index 0000000..eaaba73
--- /dev/null
+++ b/src/lib/util/tests/state_model_unittest.cc
@@ -0,0 +1,916 @@
+// Copyright (C) 2013-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 <util/state_model.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Test derivation of StateModel for exercising state model mechanics.
+///
+/// This class facilitates testing by making non-public methods accessible so
+/// they can be invoked directly in test routines. It implements a very
+/// rudimentary state model, sufficient to test the state model mechanics
+/// supplied by the base class.
+class StateModelTest : public StateModel, public testing::Test {
+public:
+
+ ///@brief StateModelTest states
+ ///@brief Fake state used for handler mapping tests.
+ static const int DUMMY_ST = SM_DERIVED_STATE_MIN + 1;
+
+ ///@brief Starting state for the test state model.
+ static const int READY_ST = SM_DERIVED_STATE_MIN + 2;
+
+ ///@brief State which simulates doing asynchronous work.
+ static const int DO_WORK_ST = SM_DERIVED_STATE_MIN + 3;
+
+ ///@brief State which finishes off processing.
+ static const int DONE_ST = SM_DERIVED_STATE_MIN + 4;
+
+ ///@brief State in which model is always paused.
+ static const int PAUSE_ALWAYS_ST = SM_DERIVED_STATE_MIN + 5;
+
+ ///@brief State in which model is paused at most once.
+ static const int PAUSE_ONCE_ST = SM_DERIVED_STATE_MIN + 6;
+
+ // StateModelTest events
+ ///@brief Event used to trigger initiation of asynchronous work.
+ static const int WORK_START_EVT = SM_DERIVED_EVENT_MIN + 1;
+
+ ///@brief Event issued when the asynchronous work "completes".
+ static const int WORK_DONE_EVT = SM_DERIVED_EVENT_MIN + 2;
+
+ ///@brief Event issued when all the work is done.
+ static const int ALL_DONE_EVT = SM_DERIVED_EVENT_MIN + 3;
+
+ ///@brief Event used to trigger an attempt to transition to bad state
+ static const int FORCE_UNDEFINED_ST_EVT = SM_DERIVED_EVENT_MIN + 4;
+
+ ///@brief Event used to trigger an attempt to transition to bad state
+ static const int SIMULATE_ERROR_EVT = SM_DERIVED_EVENT_MIN + 5;
+
+ ///@brief Event used to indicate that state machine is unpaused.
+ static const int UNPAUSED_EVT = SM_DERIVED_EVENT_MIN + 6;
+
+ /// @brief Constructor
+ ///
+ /// Parameters match those needed by StateModel.
+ StateModelTest() : dummy_called_(false), work_completed_(false),
+ failure_explanation_("") {
+ }
+ /// @brief Destructor
+ virtual ~StateModelTest() {
+ }
+
+ /// @brief Fetches the value of the dummy called flag.
+ bool getDummyCalled() {
+ return (dummy_called_);
+ }
+
+ /// @brief StateHandler for fake state, DummyState.
+ ///
+ /// It simply sets the dummy called flag to indicate that this method
+ /// was invoked.
+ void dummyHandler() {
+ dummy_called_ = true;
+ }
+
+ /// @brief Returns the failure explanation string.
+ ///
+ /// This value is set only via onModelFailure and it stores whatever
+ /// explanation that method was passed.
+ const std::string& getFailureExplanation() {
+ return (failure_explanation_);
+ }
+
+ /// @brief Returns indication of whether or not the model succeeded.
+ ///
+ /// If true, this indicates that the test model executed correctly through
+ /// to completion. The flag is only by the DONE_ST handler.
+ bool getWorkCompleted() {
+ return (work_completed_);
+ }
+
+ /// @brief State handler for the READY_ST.
+ ///
+ /// Serves as the starting state handler, it consumes the
+ /// START_EVT "transitioning" to the state, DO_WORK_ST and
+ /// sets the next event to WORK_START_EVT.
+ void readyHandler() {
+ switch(getNextEvent()) {
+ case START_EVT:
+ transition(DO_WORK_ST, WORK_START_EVT);
+ break;
+ default:
+ // its bogus
+ isc_throw(StateModelError, "readyHandler:invalid event: "
+ << getContextStr());
+ }
+ }
+
+ /// @brief State handler for the DO_WORK_ST.
+ ///
+ /// Simulates a state that starts some form of asynchronous work.
+ /// When next event is WORK_START_EVT it sets the status to pending
+ /// and signals the state model must "wait" for an event by setting
+ /// next event to NOP_EVT.
+ ///
+ /// When next event is IO_COMPLETED_EVT, it transitions to the state,
+ /// DONE_ST, and sets the next event to WORK_DONE_EVT.
+ void doWorkHandler() {
+ switch(getNextEvent()) {
+ case WORK_START_EVT:
+ postNextEvent(NOP_EVT);
+ break;
+ case WORK_DONE_EVT:
+ work_completed_ = true;
+ transition(DONE_ST, ALL_DONE_EVT);
+ break;
+ case FORCE_UNDEFINED_ST_EVT:
+ transition(9999, ALL_DONE_EVT);
+ break;
+ case SIMULATE_ERROR_EVT:
+ throw std::logic_error("Simulated Unexpected Error");
+ break;
+ default:
+ // its bogus
+ isc_throw(StateModelError, "doWorkHandler:invalid event: "
+ << getContextStr());
+ }
+ }
+
+ /// @brief State handler for the DONE_ST.
+ ///
+ /// This is the last state in the model. Note that it sets the
+ /// status to completed and next event to NOP_EVT.
+ void doneWorkHandler() {
+ switch(getNextEvent()) {
+ case ALL_DONE_EVT:
+ endModel();
+ break;
+ default:
+ // its bogus
+ isc_throw(StateModelError, "doneWorkHandler:invalid event: "
+ << getContextStr());
+ }
+ }
+
+ /// @brief State handler for PAUSE_ALWAYS_ST and PAUSE_ONCE_ST.
+ void pauseHandler() {
+ postNextEvent(NOP_EVT);
+ }
+
+ /// @brief Construct the event dictionary.
+ virtual void defineEvents() {
+ // Invoke the base call implementation first.
+ StateModel::defineEvents();
+
+ // Define our events.
+ defineEvent(WORK_START_EVT, "WORK_START_EVT");
+ defineEvent(WORK_DONE_EVT , "WORK_DONE_EVT");
+ defineEvent(ALL_DONE_EVT, "ALL_DONE_EVT");
+ defineEvent(FORCE_UNDEFINED_ST_EVT, "FORCE_UNDEFINED_ST_EVT");
+ defineEvent(SIMULATE_ERROR_EVT, "SIMULATE_ERROR_EVT");
+ defineEvent(UNPAUSED_EVT, "UNPAUSED_EVT");
+ }
+
+ /// @brief Verify the event dictionary.
+ virtual void verifyEvents() {
+ // Invoke the base call implementation first.
+ StateModel::verifyEvents();
+
+ // Verify our events.
+ getEvent(WORK_START_EVT);
+ getEvent(WORK_DONE_EVT);
+ getEvent(ALL_DONE_EVT);
+ getEvent(FORCE_UNDEFINED_ST_EVT);
+ getEvent(SIMULATE_ERROR_EVT);
+ getEvent(UNPAUSED_EVT);
+ }
+
+ /// @brief Construct the state dictionary.
+ virtual void defineStates() {
+ // Invoke the base call implementation first.
+ StateModel::defineStates();
+
+ // Define our states.
+ defineState(DUMMY_ST, "DUMMY_ST",
+ std::bind(&StateModelTest::dummyHandler, this));
+
+ defineState(READY_ST, "READY_ST",
+ std::bind(&StateModelTest::readyHandler, this));
+
+ defineState(DO_WORK_ST, "DO_WORK_ST",
+ std::bind(&StateModelTest::doWorkHandler, this));
+
+ defineState(DONE_ST, "DONE_ST",
+ std::bind(&StateModelTest::doneWorkHandler, this));
+
+ defineState(PAUSE_ALWAYS_ST, "PAUSE_ALWAYS_ST",
+ std::bind(&StateModelTest::pauseHandler, this),
+ STATE_PAUSE_ALWAYS);
+
+ defineState(PAUSE_ONCE_ST, "PAUSE_ONCE_ST",
+ std::bind(&StateModelTest::pauseHandler, this),
+ STATE_PAUSE_ONCE);
+ }
+
+ /// @brief Verify the state dictionary.
+ virtual void verifyStates() {
+ // Invoke the base call implementation first.
+ StateModel::verifyStates();
+
+ // Verify our states.
+ getStateInternal(DUMMY_ST);
+ getStateInternal(READY_ST);
+ getStateInternal(DO_WORK_ST);
+ getStateInternal(DONE_ST);
+ getStateInternal(PAUSE_ALWAYS_ST);
+ getStateInternal(PAUSE_ONCE_ST);
+ }
+
+ /// @brief Manually construct the event and state dictionaries.
+ /// This allows testing without running startModel.
+ void initDictionaries() {
+ ASSERT_NO_THROW(defineEvents());
+ ASSERT_NO_THROW(verifyEvents());
+ ASSERT_NO_THROW(defineStates());
+ ASSERT_NO_THROW(verifyStates());
+ }
+
+ /// @brief Tests the event dictionary entry for the given event value.
+ bool checkEvent(const int value, const std::string& label) {
+ EventPtr event;
+ try {
+ event = getEvent(value);
+ EXPECT_TRUE(event);
+ EXPECT_EQ(value, event->getValue());
+ EXPECT_EQ(label, event->getLabel());
+ } catch (const std::exception& ex) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /// @brief Tests the state dictionary entry for the given state value.
+ bool checkState(const int value, const std::string& label) {
+ EventPtr state;
+ try {
+ state = getState(value);
+ EXPECT_TRUE(state);
+ EXPECT_EQ(value, state->getValue());
+ EXPECT_EQ(label, state->getLabel());
+ } catch (const std::exception& ex) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /// @brief Handler called when the model suffers an execution error.
+ virtual void onModelFailure(const std::string& explanation) {
+ failure_explanation_ = explanation;
+ }
+
+ /// @brief Indicator of whether or not the DUMMY_ST handler has been called.
+ bool dummy_called_;
+
+ /// @brief Indicator of whether or not DONE_ST handler has been called.
+ bool work_completed_;
+
+ /// @brief Stores the failure explanation
+ std::string failure_explanation_;
+};
+
+// Declare them so gtest can see them.
+const int StateModelTest::DUMMY_ST;
+const int StateModelTest::READY_ST;
+const int StateModelTest::DO_WORK_ST;
+const int StateModelTest::DONE_ST;
+const int StateModelTest::WORK_START_EVT;
+const int StateModelTest::WORK_DONE_EVT;
+const int StateModelTest::ALL_DONE_EVT;
+const int StateModelTest::PAUSE_ALWAYS_ST;
+const int StateModelTest::PAUSE_ONCE_ST;
+
+/// @brief Checks the fundamentals of defining and retrieving events.
+TEST_F(StateModelTest, eventDefinition) {
+ // After construction, the event dictionary should be empty. Verify that
+ // getEvent will throw when event is not defined.
+ EXPECT_THROW(getEvent(NOP_EVT), StateModelError);
+
+ // Verify that we can add a handler to the map.
+ ASSERT_NO_THROW(defineEvent(NOP_EVT, "NOP_EVT"));
+
+ // Verify that we can find the event by value and its content is correct.
+ EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
+
+ // Verify that we cannot add a duplicate.
+ ASSERT_THROW(defineEvent(NOP_EVT, "NOP_EVT"), StateModelError);
+
+ // Verify that we can still find the event.
+ EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
+}
+
+/// @brief Tests event dictionary construction and verification.
+TEST_F(StateModelTest, eventDictionary) {
+ // After construction, the event dictionary should be empty.
+ // Make sure that verifyEvents() throws.
+ EXPECT_THROW(verifyEvents(), StateModelError);
+
+ // Construct the dictionary and verify it.
+ EXPECT_NO_THROW(defineEvents());
+ EXPECT_NO_THROW(verifyEvents());
+
+ // Verify base class events are defined.
+ EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
+ EXPECT_TRUE(checkEvent(START_EVT, "START_EVT"));
+ EXPECT_TRUE(checkEvent(END_EVT, "END_EVT"));
+ EXPECT_TRUE(checkEvent(FAIL_EVT, "FAIL_EVT"));
+
+ // Verify stub class events are defined.
+ EXPECT_TRUE(checkEvent(WORK_START_EVT, "WORK_START_EVT"));
+ EXPECT_TRUE(checkEvent(WORK_DONE_EVT, "WORK_DONE_EVT"));
+ EXPECT_TRUE(checkEvent(ALL_DONE_EVT, "ALL_DONE_EVT"));
+ EXPECT_TRUE(checkEvent(FORCE_UNDEFINED_ST_EVT, "FORCE_UNDEFINED_ST_EVT"));
+ EXPECT_TRUE(checkEvent(SIMULATE_ERROR_EVT, "SIMULATE_ERROR_EVT"));
+
+ // Verify that undefined events are handled correctly.
+ EXPECT_THROW(getEvent(9999), StateModelError);
+ EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL, getEventLabel(9999));
+}
+
+/// @brief General testing of event context accessors.
+/// Most if not all of these are also tested as a byproduct off larger tests.
+TEST_F(StateModelTest, eventContextAccessors) {
+ // Construct the event definitions, normally done by startModel.
+ ASSERT_NO_THROW(defineEvents());
+ ASSERT_NO_THROW(verifyEvents());
+
+ // Verify the post-construction values.
+ EXPECT_EQ(NOP_EVT, getNextEvent());
+ EXPECT_EQ(NOP_EVT, getLastEvent());
+
+ // Call setEvent which will update both next event and last event.
+ EXPECT_NO_THROW(postNextEvent(START_EVT));
+
+ // Verify the values are what we expect.
+ EXPECT_EQ(START_EVT, getNextEvent());
+ EXPECT_EQ(NOP_EVT, getLastEvent());
+
+ // Call setEvent again.
+ EXPECT_NO_THROW(postNextEvent(WORK_START_EVT));
+
+ // Verify the values are what we expect.
+ EXPECT_EQ(WORK_START_EVT, getNextEvent());
+ EXPECT_EQ(START_EVT, getLastEvent());
+
+ // Verify that posting an undefined event throws.
+ EXPECT_THROW(postNextEvent(9999), StateModelError);
+}
+
+/// @brief Tests the fundamental methods used for state handler mapping.
+/// Verifies the ability to search for and add entries in the state handler map.
+TEST_F(StateModelTest, stateDefinition) {
+ // After construction, the state dictionary should be empty. Verify that
+ // getState will throw when, state is not defined.
+ EXPECT_THROW(getState(READY_ST), StateModelError);
+
+ // Verify that we can add a state to the dictionary.
+ ASSERT_NO_THROW(defineState(READY_ST, "READY_ST",
+ std::bind(&StateModelTest::dummyHandler,
+ this)));
+
+ // Verify that we can find the state by its value.
+ StatePtr state;
+ EXPECT_NO_THROW(state = getState(READY_ST));
+ EXPECT_TRUE(state);
+
+ // Verify the state's value and label.
+ EXPECT_EQ(READY_ST, state->getValue());
+ EXPECT_EQ("READY_ST", state->getLabel());
+
+ // Now verify that retrieved state's handler executes the correct method.
+ // Make sure the dummy called flag is false prior to invocation.
+ EXPECT_FALSE(getDummyCalled());
+
+ // Invoke the state's handler.
+ EXPECT_NO_THROW(state->run());
+
+ // Verify the dummy called flag is now true.
+ EXPECT_TRUE(getDummyCalled());
+
+ // Verify that we cannot add a duplicate.
+ EXPECT_THROW(defineState(READY_ST, "READY_ST",
+ std::bind(&StateModelTest::readyHandler, this)),
+ StateModelError);
+
+ // Verify that we can still find the state.
+ EXPECT_NO_THROW(getState(READY_ST));
+}
+
+/// @brief Tests state dictionary initialization and validation.
+/// This tests the basic concept of state dictionary initialization and
+/// verification by manually invoking the methods normally called by startModel.
+TEST_F(StateModelTest, stateDictionary) {
+ // Verify that the map validation throws prior to the dictionary being
+ // initialized.
+ EXPECT_THROW(verifyStates(), StateModelError);
+
+ // Construct the dictionary and verify it.
+ ASSERT_NO_THROW(defineStates());
+ EXPECT_NO_THROW(verifyStates());
+
+ // Verify the base class states.
+ EXPECT_TRUE(checkState(NEW_ST, "NEW_ST"));
+ EXPECT_TRUE(checkState(END_ST, "END_ST"));
+
+ // Verify stub class states.
+ EXPECT_TRUE(checkState(DUMMY_ST, "DUMMY_ST"));
+ EXPECT_TRUE(checkState(READY_ST, "READY_ST"));
+ EXPECT_TRUE(checkState(DO_WORK_ST, "DO_WORK_ST"));
+ EXPECT_TRUE(checkState(DONE_ST, "DONE_ST"));
+
+ // Verify that undefined states are handled correctly.
+ EXPECT_THROW(getState(9999), StateModelError);
+ EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL, getStateLabel(9999));
+}
+
+/// @brief General testing of state context accessors.
+/// Most if not all of these are also tested as a byproduct off larger tests.
+TEST_F(StateModelTest, stateContextAccessors) {
+ // setState will throw unless we initialize the handler map.
+ ASSERT_NO_THROW(defineStates());
+ ASSERT_NO_THROW(verifyStates());
+
+ // Verify post-construction state values.
+ EXPECT_EQ(NEW_ST, getCurrState());
+ EXPECT_EQ(NEW_ST, getPrevState());
+
+ // Call setState which will update both state and previous state.
+ EXPECT_NO_THROW(setState(READY_ST));
+
+ // Verify the values are what we expect.
+ EXPECT_EQ(READY_ST, getCurrState());
+ EXPECT_EQ(NEW_ST, getPrevState());
+
+ // Call setState again.
+ EXPECT_NO_THROW(setState(DO_WORK_ST));
+
+ // Verify the values are what we expect.
+ EXPECT_EQ(DO_WORK_ST, getCurrState());
+ EXPECT_EQ(READY_ST, getPrevState());
+
+ // Verify that calling setState with an state that has no handler
+ // will throw.
+ EXPECT_THROW(setState(-1), StateModelError);
+
+ // Verify that calling setState with NEW_ST is ok.
+ EXPECT_NO_THROW(setState(NEW_ST));
+
+ // Verify that calling setState with END_ST is ok.
+ EXPECT_NO_THROW(setState(END_ST));
+
+ // Verify that calling setState with an undefined state throws.
+ EXPECT_THROW(setState(9999), StateModelError);
+}
+
+/// @brief Checks that invoking runModel prior to startModel is not allowed.
+TEST_F(StateModelTest, runBeforeStart) {
+ // Verify that the failure explanation is empty and work is not done.
+ EXPECT_TRUE(getFailureExplanation().empty());
+
+ // Attempt to call runModel before startModel. This should result in an
+ // orderly model failure.
+ ASSERT_NO_THROW(runModel(START_EVT));
+
+ // Check that state and event are correct.
+ EXPECT_EQ(END_ST, getCurrState());
+ EXPECT_EQ(FAIL_EVT, getNextEvent());
+
+ // Verify that failure explanation is not empty.
+ EXPECT_FALSE(getFailureExplanation().empty());
+}
+
+/// @brief Tests that the endModel may be used to transition the model to
+/// a normal conclusion.
+TEST_F(StateModelTest, transitionWithEnd) {
+ // Init dictionaries manually, normally done by startModel.
+ initDictionaries();
+
+ // call transition to move from NEW_ST to DUMMY_ST with START_EVT
+ EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+ // Verify that state and event members are as expected.
+ EXPECT_EQ(DUMMY_ST, getCurrState());
+ EXPECT_EQ(NEW_ST, getPrevState());
+ EXPECT_EQ(START_EVT, getNextEvent());
+ EXPECT_EQ(NOP_EVT, getLastEvent());
+
+ // Call endModel to transition us to the end of the model.
+ EXPECT_NO_THROW(endModel());
+
+ // Verify state and event members are correctly set.
+ EXPECT_EQ(END_ST, getCurrState());
+ EXPECT_EQ(DUMMY_ST, getPrevState());
+ EXPECT_EQ(END_EVT, getNextEvent());
+ EXPECT_EQ(START_EVT, getLastEvent());
+}
+
+/// @brief Tests that the abortModel may be used to transition the model to
+/// failed conclusion.
+TEST_F(StateModelTest, transitionWithAbort) {
+ // Init dictionaries manually, normally done by startModel.
+ initDictionaries();
+
+ // call transition to move from NEW_ST to DUMMY_ST with START_EVT
+ EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+ // Verify that state and event members are as expected.
+ EXPECT_EQ(DUMMY_ST, getCurrState());
+ EXPECT_EQ(NEW_ST, getPrevState());
+ EXPECT_EQ(START_EVT, getNextEvent());
+ EXPECT_EQ(NOP_EVT, getLastEvent());
+
+ // Call endModel to transition us to the end of the model.
+ EXPECT_NO_THROW(abortModel("test invocation"));
+
+ // Verify state and event members are correctly set.
+ EXPECT_EQ(END_ST, getCurrState());
+ EXPECT_EQ(DUMMY_ST, getPrevState());
+ EXPECT_EQ(FAIL_EVT, getNextEvent());
+ EXPECT_EQ(START_EVT, getLastEvent());
+}
+
+/// @brief Tests that the boolean indicators for on state entry and exit
+/// work properly.
+TEST_F(StateModelTest, doFlags) {
+ // Init dictionaries manually, normally done by startModel.
+ initDictionaries();
+
+ // Verify that "do" flags are false.
+ EXPECT_FALSE(doOnEntry());
+ EXPECT_FALSE(doOnExit());
+
+ // call transition to move from NEW_ST to DUMMY_ST with START_EVT
+ EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+ // We are transitioning states, so "do" flags should be true.
+ EXPECT_TRUE(doOnEntry());
+ EXPECT_TRUE(doOnExit());
+
+ // "do" flags are one-shots, so they should now both be false.
+ EXPECT_FALSE(doOnEntry());
+ EXPECT_FALSE(doOnExit());
+
+ // call transition to re-enter same state, "do" flags should be false.
+ EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+ // "do" flags should be false.
+ EXPECT_FALSE(doOnEntry());
+ EXPECT_FALSE(doOnExit());
+
+}
+
+/// @brief Verifies that the model status methods accurately reflect the model
+/// status. It also verifies that the dictionaries can be modified before
+/// the model is running but not after.
+TEST_F(StateModelTest, statusMethods) {
+ // Init dictionaries manually, normally done by startModel.
+ initDictionaries();
+
+ // After construction, state model is "new", all others should be false.
+ EXPECT_TRUE(isModelNew());
+ EXPECT_FALSE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_FALSE(isModelDone());
+ EXPECT_FALSE(didModelFail());
+
+ // Verify that events and states can be added before the model is started.
+ EXPECT_NO_THROW(defineEvent(9998, "9998"));
+ EXPECT_NO_THROW(defineState(9998, "9998",
+ std::bind(&StateModelTest::readyHandler,
+ this)));
+
+ // "START" the model.
+ // Fake out starting the model by calling transition to move from NEW_ST
+ // to DUMMY_ST with START_EVT. If we used startModel this would blow by
+ // the status of "running" but not "waiting".
+ EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+
+ // Verify that events and states cannot be added after the model is started.
+ EXPECT_THROW(defineEvent(9999, "9999"), StateModelError);
+ EXPECT_THROW(defineState(9999, "9999",
+ std::bind(&StateModelTest::readyHandler, this)),
+ StateModelError);
+
+ // The state and event combos set above, should show the model as
+ // "running", all others should be false.
+ EXPECT_FALSE(isModelNew());
+ EXPECT_TRUE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_FALSE(isModelDone());
+ EXPECT_FALSE(didModelFail());
+
+ // call transition to submit NOP_EVT to current state, DUMMY_ST.
+ EXPECT_NO_THROW(transition(DUMMY_ST, NOP_EVT));
+
+ // Verify the status methods are correct: with next event set to NOP_EVT,
+ // model should be "running" and "waiting".
+ EXPECT_FALSE(isModelNew());
+ EXPECT_TRUE(isModelRunning());
+ EXPECT_TRUE(isModelWaiting());
+ EXPECT_FALSE(isModelDone());
+ EXPECT_FALSE(didModelFail());
+
+ // Call endModel to transition us to the end of the model.
+ EXPECT_NO_THROW(endModel());
+
+ // With state set to END_ST, model should be done.
+ EXPECT_FALSE(isModelNew());
+ EXPECT_FALSE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_TRUE(isModelDone());
+ EXPECT_FALSE(didModelFail());
+}
+
+/// @brief Tests that the model status methods are correct after a model
+/// failure.
+TEST_F(StateModelTest, statusMethodsOnFailure) {
+ // Construct the event and state definitions, normally done by startModel.
+ ASSERT_NO_THROW(defineEvents());
+ ASSERT_NO_THROW(verifyEvents());
+ ASSERT_NO_THROW(defineStates());
+ ASSERT_NO_THROW(verifyStates());
+
+ // call transition to move from NEW_ST to DUMMY_ST with START_EVT
+ EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
+
+ // Call endModel to transition us to the end of the model.
+ EXPECT_NO_THROW(abortModel("test invocation"));
+
+ // With state set to END_ST, model should be done.
+ EXPECT_FALSE(isModelNew());
+ EXPECT_FALSE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_TRUE(isModelDone());
+ EXPECT_TRUE(didModelFail());
+}
+
+/// @brief Checks that the context strings accurately reflect context and
+/// are safe to invoke.
+TEST_F(StateModelTest, contextStrs) {
+ // Verify context methods do not throw prior to dictionary init.
+ ASSERT_NO_THROW(getContextStr());
+ ASSERT_NO_THROW(getPrevContextStr());
+
+ // Construct the event and state definitions, normally done by startModel.
+ ASSERT_NO_THROW(defineEvents());
+ ASSERT_NO_THROW(verifyEvents());
+ ASSERT_NO_THROW(defineStates());
+ ASSERT_NO_THROW(verifyStates());
+
+ // transition uses setState and setEvent, testing it tests all three.
+ EXPECT_NO_THROW(transition(READY_ST, START_EVT));
+
+ // Verify the current context string depicts correct state and event.
+ std::string ctx_str;
+ ASSERT_NO_THROW(ctx_str = getContextStr());
+ EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(READY_ST)));
+ EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(START_EVT)));
+
+ // Verify the previous context string depicts correct state and event.
+ ASSERT_NO_THROW(ctx_str = getPrevContextStr());
+ EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(NEW_ST)));
+ EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(NOP_EVT)));
+}
+
+/// @brief Tests that undefined states are handled gracefully.
+/// This test verifies that attempting to transition to an undefined state,
+/// which constitutes a model violation, results in an orderly model failure.
+TEST_F(StateModelTest, undefinedState) {
+ // Verify that the failure explanation is empty and work is not done.
+ EXPECT_TRUE(getFailureExplanation().empty());
+ EXPECT_FALSE(getWorkCompleted());
+
+ // First, lets execute the state model to a known valid point, by
+ // calling startModel. This should run the model through to DO_WORK_ST.
+ ASSERT_NO_THROW(startModel(READY_ST));
+
+ // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
+ EXPECT_EQ(DO_WORK_ST, getCurrState());
+ EXPECT_EQ(NOP_EVT, getNextEvent());
+
+ // Resume the model with next event set to cause the DO_WORK_ST handler
+ // to transition to an undefined state. This should cause it to return
+ // without throwing and yield a failed model.
+ EXPECT_NO_THROW(runModel(FORCE_UNDEFINED_ST_EVT));
+
+ // Verify that status methods are correct: model is done but failed.
+ EXPECT_FALSE(isModelNew());
+ EXPECT_FALSE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_TRUE(isModelDone());
+ EXPECT_TRUE(didModelFail());
+
+ // Verify that failure explanation is not empty.
+ EXPECT_FALSE(getFailureExplanation().empty());
+
+ // Verify that work completed flag is still false.
+ EXPECT_FALSE(getWorkCompleted());
+}
+
+/// @brief Tests that an unexpected exception thrown by a state handler is
+/// handled gracefully. State models are supposed to account for and handle
+/// all errors that they actions (i.e. handlers) may cause. In the event they
+/// do not, this constitutes a model violation. This test verifies such
+/// violations are handled correctly and result in an orderly model failure.
+TEST_F(StateModelTest, unexpectedError) {
+ // Verify that the failure explanation is empty and work is not done.
+ EXPECT_TRUE(getFailureExplanation().empty());
+ EXPECT_FALSE(getWorkCompleted());
+
+ // First, lets execute the state model to a known valid point, by
+ // calling startModel with a start state of READY_ST.
+ // This should run the model through to DO_WORK_ST.
+ ASSERT_NO_THROW(startModel(READY_ST));
+
+ // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
+ EXPECT_EQ(DO_WORK_ST, getCurrState());
+ EXPECT_EQ(NOP_EVT, getNextEvent());
+
+ // Resume the model with next event set to cause the DO_WORK_ST handler
+ // to transition to an undefined state. This should cause it to return
+ // without throwing and yield a failed model.
+ EXPECT_NO_THROW(runModel(SIMULATE_ERROR_EVT));
+
+ // Verify that status methods are correct: model is done but failed.
+ EXPECT_FALSE(isModelNew());
+ EXPECT_FALSE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_TRUE(isModelDone());
+ EXPECT_TRUE(didModelFail());
+
+ // Verify that failure explanation is not empty.
+ EXPECT_FALSE(getFailureExplanation().empty());
+
+ // Verify that work completed flag is still false.
+ EXPECT_FALSE(getWorkCompleted());
+}
+
+/// @brief Tests that undefined events are handled gracefully.
+/// This test verifies that submitting an undefined event to the state machine
+/// results, which constitutes a model violation, results in an orderly model
+/// failure.
+TEST_F(StateModelTest, undefinedEvent) {
+ // Verify that the failure explanation is empty and work is not done.
+ EXPECT_TRUE(getFailureExplanation().empty());
+ EXPECT_FALSE(getWorkCompleted());
+
+ // First, lets execute the state model to a known valid point, by
+ // calling startModel with a start state of READY_ST.
+ // This should run the model through to DO_WORK_ST.
+ ASSERT_NO_THROW(startModel(READY_ST));
+
+ // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
+ EXPECT_EQ(DO_WORK_ST, getCurrState());
+ EXPECT_EQ(NOP_EVT, getNextEvent());
+
+ // Attempting to post an undefined event within runModel should cause it
+ // to return without throwing and yield a failed model.
+ EXPECT_NO_THROW(runModel(9999));
+
+ // Verify that status methods are correct: model is done but failed.
+ EXPECT_FALSE(isModelNew());
+ EXPECT_FALSE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_TRUE(isModelDone());
+ EXPECT_TRUE(didModelFail());
+
+ // Verify that failure explanation is not empty.
+ EXPECT_FALSE(getFailureExplanation().empty());
+
+ // Verify that work completed flag is still false.
+ EXPECT_FALSE(getWorkCompleted());
+}
+
+/// @brief Test the basic mechanics of state model execution.
+/// This test exercises the ability to execute state model from start to
+/// finish, including the handling of a asynchronous IO operation.
+TEST_F(StateModelTest, stateModelTest) {
+ // Verify that status methods are correct: model is new.
+ EXPECT_TRUE(isModelNew());
+ EXPECT_FALSE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_FALSE(isModelDone());
+
+ // Verify that the failure explanation is empty and work is not done.
+ EXPECT_TRUE(getFailureExplanation().empty());
+ EXPECT_FALSE(getWorkCompleted());
+
+ // Launch the transaction by calling startModel. The state model
+ // should run up until the simulated async work operation is initiated
+ // in DO_WORK_ST.
+ ASSERT_NO_THROW(startModel(READY_ST));
+
+ // Verify that we are now in state of DO_WORK_ST, the last event was
+ // WORK_START_EVT, the next event is NOP_EVT.
+ EXPECT_EQ(DO_WORK_ST, getCurrState());
+ EXPECT_EQ(WORK_START_EVT, getLastEvent());
+ EXPECT_EQ(NOP_EVT, getNextEvent());
+
+ // Simulate completion of async work completion by resuming runModel with
+ // an event of WORK_DONE_EVT.
+ ASSERT_NO_THROW(runModel(WORK_DONE_EVT));
+
+ // Verify that the state model has progressed through to completion:
+ // it is in the DONE_ST, the status is ST_COMPLETED, and the next event
+ // is NOP_EVT.
+ EXPECT_EQ(END_ST, getCurrState());
+ EXPECT_EQ(END_EVT, getNextEvent());
+
+ // Verify that status methods are correct: model done.
+ EXPECT_FALSE(isModelNew());
+ EXPECT_FALSE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_TRUE(isModelDone());
+
+ // Verify that failure explanation is empty.
+ EXPECT_TRUE(getFailureExplanation().empty());
+
+ // Verify that work completed flag is true.
+ EXPECT_TRUE(getWorkCompleted());
+}
+
+// This test verifies the pausing and un-pausing capabilities of the state
+// model.
+TEST_F(StateModelTest, stateModelPause) {
+ // Verify that status methods are correct: model is new.
+ EXPECT_TRUE(isModelNew());
+ EXPECT_FALSE(isModelRunning());
+ EXPECT_FALSE(isModelWaiting());
+ EXPECT_FALSE(isModelDone());
+ EXPECT_FALSE(isModelPaused());
+
+ // Verify that the failure explanation is empty and work is not done.
+ EXPECT_TRUE(getFailureExplanation().empty());
+ EXPECT_FALSE(getWorkCompleted());
+
+ // Transition straight to the state in which the model should always
+ // pause.
+ ASSERT_NO_THROW(startModel(PAUSE_ALWAYS_ST));
+
+ // Verify it was successful and that the model is paused.
+ EXPECT_EQ(PAUSE_ALWAYS_ST, getCurrState());
+ EXPECT_TRUE(isModelPaused());
+
+ // Run the model again. It should still be paused.
+ ASSERT_NO_THROW(runModel(NOP_EVT));
+ EXPECT_TRUE(isModelPaused());
+
+ // Unpause the model and transition to the state in which the model
+ // should be paused at most once.
+ unpauseModel();
+ transition(PAUSE_ONCE_ST, NOP_EVT);
+ EXPECT_EQ(PAUSE_ONCE_ST, getCurrState());
+ EXPECT_TRUE(isModelPaused());
+
+ // The model should still be paused until explicitly unpaused.
+ ASSERT_NO_THROW(runModel(NOP_EVT));
+ EXPECT_EQ(PAUSE_ONCE_ST, getCurrState());
+ EXPECT_TRUE(isModelPaused());
+
+ unpauseModel();
+
+ // Transition back to the first state. The model should pause again.
+ transition(PAUSE_ALWAYS_ST, NOP_EVT);
+ EXPECT_EQ(PAUSE_ALWAYS_ST, getCurrState());
+ EXPECT_TRUE(isModelPaused());
+
+ ASSERT_NO_THROW(runModel(NOP_EVT));
+ EXPECT_EQ(PAUSE_ALWAYS_ST, getCurrState());
+ EXPECT_TRUE(isModelPaused());
+
+ // Unpause the model and transition to the state in which the model
+ // should pause only once. This time it should not pause.
+ unpauseModel();
+ transition(PAUSE_ONCE_ST, NOP_EVT);
+ EXPECT_EQ(PAUSE_ONCE_ST, getCurrState());
+ EXPECT_FALSE(isModelPaused());
+}
+
+}
diff --git a/src/lib/util/tests/stopwatch_unittest.cc b/src/lib/util/tests/stopwatch_unittest.cc
new file mode 100644
index 0000000..a506b6b
--- /dev/null
+++ b/src/lib/util/tests/stopwatch_unittest.cc
@@ -0,0 +1,307 @@
+// Copyright (C) 2015-2023 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 <util/stopwatch.h>
+#include <util/stopwatch_impl.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+namespace {
+
+using namespace isc;
+using namespace isc::util;
+using namespace boost::posix_time;
+
+/// @brief @c StopwatchImpl mock object.
+///
+/// This class derives from the @c StopwatchImpl to override the
+/// @c StopwatchImpl::getCurrentTime. This method is internally called by
+/// the @c StopwatchImpl to determine the current time. By providing the
+/// implementation of this method which returns the fixed (well known)
+/// timestamp value we can obtain the deterministic values from the accessors
+/// of this class.
+///
+/// This class also includes some convenience methods to return the time
+/// durations in milliseconds.
+class StopwatchMock : public StopwatchImpl {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param ref_time Reference time, i.e. the arbitrary time value from
+ /// which time is measured. The @c current_time_ value returned by the
+ /// @c StopwatchMock::getCurrentTime is initialized to this value.
+ /// Subsequent calls to the @c StopwatchMock::ffwd move the value of
+ /// the @c current_time_ forward.
+ StopwatchMock(const ptime& ref_time);
+
+ /// @brief Fast forward time.
+ ///
+ /// Moves the value of the @c current_time_ forward by the specified
+ /// number of milliseconds (microseconds). As a result the timestamp
+ /// returned by the @c StopwatchMock::getCurrentTime moves by this value.
+ /// This simulates the time progress.
+ ///
+ /// @param ms Specifies the number of milliseconds to move current time.
+ /// @param us Specifies the number of fractional microseconds to move
+ /// current time.
+ void ffwd(const uint32_t ms, const uint32_t us = 0);
+
+ /// @brief Returns the last duration in milliseconds.
+ uint32_t getLastDurationInMs() const;
+
+ /// @brief Returns the total duration in milliseconds.
+ uint32_t getTotalDurationInMs() const;
+
+protected:
+
+ /// @brief Returns the current time.
+ ///
+ /// This method returns the fixed @c current_time_ timestamp.
+ virtual ptime getCurrentTime() const;
+
+private:
+
+ /// @brief Holds the current time to be returned by the
+ /// @c StopwatchMock::getCurrentTime.
+ ptime current_time_;
+
+};
+
+StopwatchMock::StopwatchMock(const ptime& ref_time)
+ : StopwatchImpl(), current_time_(ref_time) {
+}
+
+void
+StopwatchMock::ffwd(const uint32_t ms, const uint32_t us) {
+ current_time_ += milliseconds(ms);
+ current_time_ += microseconds(us);
+}
+
+uint32_t
+StopwatchMock::getLastDurationInMs() const {
+ return (getLastDuration().total_milliseconds());
+}
+
+uint32_t
+StopwatchMock::getTotalDurationInMs() const {
+ return (getTotalDuration().total_milliseconds());
+}
+
+ptime
+StopwatchMock::getCurrentTime() const {
+ return (current_time_);
+}
+
+/// @brief Test fixture class for testing @c StopwatchImpl.
+class StopwatchTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor
+ StopwatchTest() = default;
+
+ /// @brief Destructor
+ virtual ~StopwatchTest() = default;
+
+protected:
+
+ /// @brief Set up the test.
+ ///
+ /// Initializes the reference time to be used to create the instances
+ /// of the @c StopwatchMock objects.
+ virtual void SetUp();
+
+ /// @brief Holds the reference time to be used to create the instances
+ /// of the @c StopwatchMock objects.
+ ptime ref_time_;
+};
+
+void
+StopwatchTest::SetUp() {
+ ref_time_ = microsec_clock::universal_time();
+}
+
+/// This test checks the behavior of the stopwatch when it is started
+/// and stopped multiple times. It uses the StopwatchMock object to
+/// control the "time flow" by setting the current time to arbitrary
+/// values using the StopwatchMock::ffwd. In addition, this test
+/// checks that the stopwatch can be reset.
+TEST_F(StopwatchTest, multipleMeasurements) {
+ StopwatchMock stopwatch(ref_time_);
+ // The stopwatch shouldn't automatically start. The initial
+ // durations should be set to 0.
+ EXPECT_EQ(0, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(0, stopwatch.getTotalDurationInMs());
+
+ stopwatch.start();
+
+ // Even though the stopwatch is started, the time is still set to
+ // the initial value. The durations should not be affected.
+ EXPECT_EQ(0, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(0, stopwatch.getTotalDurationInMs());
+
+ // Move the time by 10 ms.
+ stopwatch.ffwd(10);
+
+ // It should be possible to retrieve the durations even when the
+ // stopwatch is running.
+ EXPECT_EQ(10, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(10, stopwatch.getTotalDurationInMs());
+
+ // Now stop it and make sure that the same values are returned.
+ stopwatch.stop();
+
+ EXPECT_EQ(10, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(10, stopwatch.getTotalDurationInMs());
+
+ // Start it again, but don't move the time forward yet.
+ stopwatch.start();
+
+ // The new duration should be 0, but the total should be equal to
+ // the previously measured duration.
+ EXPECT_EQ(0, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(10, stopwatch.getTotalDurationInMs());
+
+ // Move time by 5 ms.
+ stopwatch.ffwd(5);
+
+ // New measured duration should be 5 ms. The total should be 15 ms.
+ EXPECT_EQ(5, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
+
+ // Stop it again and make sure the values returned are the same.
+ stopwatch.stop();
+
+ EXPECT_EQ(5, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
+
+ // Move the time forward while the stopwatch is stopped.
+ stopwatch.ffwd(8);
+
+ // The measured values should not be affected.
+ EXPECT_EQ(5, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
+
+ // Stop should be no-op in this case.
+ stopwatch.stop();
+
+ EXPECT_EQ(5, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
+
+ // Start the stopwatch again.
+ stopwatch.start();
+
+ // Move time by 3 ms.
+ stopwatch.ffwd(3);
+
+ // Since the stopwatch is running, the measured duration should
+ // get updated again.
+ EXPECT_EQ(3, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(18, stopwatch.getTotalDurationInMs());
+
+ // Move the time by 2 ms.
+ stopwatch.ffwd(2);
+
+ // Start should be no-op in this case.
+ stopwatch.start();
+
+ // But the durations should be updated.
+ EXPECT_EQ(5, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(20, stopwatch.getTotalDurationInMs());
+
+ // Make sure we can reset.
+ stopwatch.reset();
+
+ EXPECT_EQ(0, stopwatch.getLastDurationInMs());
+ EXPECT_EQ(0, stopwatch.getTotalDurationInMs());
+}
+
+// This test checks that the stopwatch works when the real clock is in use.
+TEST_F(StopwatchTest, realTime) {
+ // Initially, the measured time should be 0.
+ Stopwatch stopwatch;
+ EXPECT_EQ(0, stopwatch.getLastMilliseconds());
+ EXPECT_EQ(0, stopwatch.getTotalMilliseconds());
+
+ // Start the stopwatch.
+ stopwatch.start();
+
+ // Sleep for 1 ms. The stopwatch should measure this duration.
+ usleep(1000);
+
+ stopwatch.stop();
+
+ // The measured duration should be greater or equal 1 ms.
+ long current_duration = stopwatch.getLastMilliseconds();
+ EXPECT_GE(current_duration, 1);
+ EXPECT_EQ(current_duration, stopwatch.getTotalMilliseconds());
+
+ // Sleep for another 2 ms while the stopwatch is in the stopped state.
+ usleep(2000);
+
+ // In the stopped state, we should still have old durations measured.
+ EXPECT_EQ(current_duration, stopwatch.getLastMilliseconds());
+ EXPECT_EQ(current_duration, stopwatch.getTotalMilliseconds());
+
+ // Start it again.
+ stopwatch.start();
+
+ // Sleep for 1 ms.
+ usleep(1000);
+
+ // The durations should get updated as appropriate.
+ EXPECT_GE(stopwatch.getLastMilliseconds(), 1);
+ EXPECT_GE(stopwatch.getTotalMilliseconds(), 2);
+}
+
+// Make sure that we can obtain the durations as microseconds.
+TEST_F(StopwatchTest, getLastMicroseconds) {
+ Stopwatch stopwatch;
+ stopwatch.start();
+
+ usleep(1000);
+
+ stopwatch.stop();
+
+ long current_duration = stopwatch.getLastMicroseconds();
+ EXPECT_GE(current_duration, 1000);
+ EXPECT_EQ(current_duration, stopwatch.getTotalMicroseconds());
+}
+
+// Make sure that we can use the "autostart" option to start the time
+// measurement in the constructor.
+TEST_F(StopwatchTest, autostart) {
+ Stopwatch stopwatch(true);
+ usleep(1000);
+
+ stopwatch.stop();
+
+ EXPECT_GE(stopwatch.getLastMilliseconds(), 1);
+ EXPECT_EQ(stopwatch.getLastMilliseconds(), stopwatch.getTotalMilliseconds());
+}
+
+// Make sure that the conversion to the loggable string works as expected.
+TEST_F(StopwatchTest, logFormat) {
+ time_duration duration = microseconds(223043);
+ EXPECT_EQ("223.043 ms", StopwatchImpl::logFormat(duration));
+
+ duration = microseconds(1234);
+ EXPECT_EQ("1.234 ms", StopwatchImpl::logFormat(duration));
+
+ duration = microseconds(2000);
+ EXPECT_EQ("2.000 ms", StopwatchImpl::logFormat(duration));
+
+ duration = milliseconds(2100);
+ EXPECT_EQ("2.10 s", StopwatchImpl::logFormat(duration));
+
+ duration = milliseconds(3123);
+ EXPECT_EQ("3.12 s", StopwatchImpl::logFormat(duration));
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/util/tests/str_unittests.cc b/src/lib/util/tests/str_unittests.cc
new file mode 100644
index 0000000..78d28ce
--- /dev/null
+++ b/src/lib/util/tests/str_unittests.cc
@@ -0,0 +1,529 @@
+// Copyright (C) 2011-2024 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/gtest_utils.h>
+#include <util/encode/encode.h>
+#include <util/str.h>
+
+#include <cstdint>
+#include <exception>
+#include <sstream>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::util::str;
+using namespace std;
+
+namespace {
+
+/// @brief Fixture used to test StringSanitizer.
+struct StringUtilTest : ::testing::Test {
+ /// @brief Pass string through scrub and check the result.
+ ///
+ /// @param original The string to sanitize.
+ /// @param char_set The regular expression string describing invalid characters.
+ /// @param char_replacement - character(s) which replace invalid
+ /// characters
+ /// @param expected - expected sanitized string
+ void checkScrub(const string& original,
+ const string& char_set,
+ const string& char_replacement,
+ const string& expected) {
+ StringSanitizerPtr ss;
+ string sanitized;
+
+ try {
+ ss.reset(new StringSanitizer(char_set, char_replacement));
+ } catch (const exception& ex) {
+ ADD_FAILURE() << "Could not construct sanitizer:" << ex.what();
+ return;
+ }
+
+ try {
+ sanitized = ss->scrub(original);
+ } catch (const exception& ex) {
+ ADD_FAILURE() << "Could not scrub string:" << ex.what();
+ return;
+ }
+
+ EXPECT_EQ(sanitized, expected);
+ }
+
+ /// @brief Check that hex strings with colons can be decoded.
+ ///
+ /// @param input Input string to be decoded.
+ /// @param reference The expected result.
+ void checkColonSeparated(const string& input, const string& reference) {
+ // Create a reference vector.
+ vector<uint8_t> reference_vector;
+ ASSERT_NO_THROW_LOG(decodeHex(reference, reference_vector));
+
+ // Fill the output vector with some garbage to make sure that
+ // the data is erased when a string is decoded successfully.
+ vector<uint8_t> decoded(1, 10);
+ ASSERT_NO_THROW_LOG(decodeColonSeparatedHexString(input, decoded));
+
+ // Get the string representation of the decoded data for logging
+ // purposes.
+ string encoded;
+ ASSERT_NO_THROW_LOG(encoded = encodeHex(decoded));
+
+ // Check if the decoded data matches the reference.
+ EXPECT_EQ(decoded, reference_vector) << "decoded data don't match the reference, input='"
+ << input << "', reference='" << reference
+ << "'"
+ ", decoded='"
+ << encoded << "'";
+ }
+
+ /// @brief Check that formatted hex strings can be decoded.
+ ///
+ /// @param input Input string to be decoded.
+ /// @param reference The expected result.
+ void checkFormatted(const string& input, const string& reference) {
+ // Create a reference vector.
+ vector<uint8_t> reference_vector;
+ ASSERT_NO_THROW_LOG(decodeHex(reference, reference_vector));
+
+ // Fill the output vector with some garbage to make sure that
+ // the data is erased when a string is decoded successfully.
+ vector<uint8_t> decoded(1, 10);
+ ASSERT_NO_THROW_LOG(decodeFormattedHexString(input, decoded));
+
+ // Get the string representation of the decoded data for logging
+ // purposes.
+ string encoded;
+ ASSERT_NO_THROW_LOG(encoded = encodeHex(decoded));
+
+ // Check if the decoded data matches the reference.
+ EXPECT_EQ(decoded, reference_vector)
+ << "decoded data don't match the reference, input='" << input << "', reference='"
+ << reference << "', decoded='" << encoded << "'";
+ }
+
+ /// @brief Convenience function which calls quotedStringToBinary
+ /// and converts returned vector back to string.
+ ///
+ /// @param s Input string.
+ ///
+ /// @return String holding a copy of a vector returned by the
+ /// quotedStringToBinary.
+ string checkQuoted(const string& s) {
+ vector<uint8_t> vec = quotedStringToBinary(s);
+ string s2(vec.begin(), vec.end());
+ return (s2);
+ }
+};
+
+// Check that leading and trailing space trimming works.
+TEST_F(StringUtilTest, Trim) {
+ // Empty and full string.
+ EXPECT_EQ("", trim(""));
+ EXPECT_EQ("abcxyz", trim("abcxyz"));
+
+ // Trim right-most blanks
+ EXPECT_EQ("ABC", trim("ABC "));
+ EXPECT_EQ("ABC", trim("ABC\t\t \n\t"));
+
+ // Left-most blank trimming
+ EXPECT_EQ("XYZ", trim(" XYZ"));
+ EXPECT_EQ("XYZ", trim("\t\t \tXYZ"));
+
+ // Right and left, with embedded spaces
+ EXPECT_EQ("MN \t OP", trim("\t\tMN \t OP \t"));
+}
+
+// Check tokenization.
+TEST_F(StringUtilTest, Tokens) {
+ vector<string> result;
+
+ // Default delimiters
+
+ // Degenerate cases
+ result = tokens(""); // Empty string
+ EXPECT_EQ(0, result.size());
+
+ result = tokens(" \n "); // String is all delimiters
+ EXPECT_EQ(0, result.size());
+
+ result = tokens("abc"); // String has no delimiters
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+
+ // String containing leading and/or trailing delimiters, no embedded ones.
+ result = tokens("\txyz"); // One leading delimiter
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = tokens("\t \nxyz"); // Multiple leading delimiters
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = tokens("xyz\n"); // One trailing delimiter
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = tokens("xyz \t"); // Multiple trailing
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = tokens("\t xyz \n"); // Leading and trailing
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ // Embedded delimiters
+ result = tokens("abc\ndef"); // 2 tokens, one separator
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+
+ result = tokens("abc\t\t\ndef"); // 2 tokens, 3 separators
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+
+ result = tokens("abc\n \tdef\t\tghi");
+ ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+ EXPECT_EQ(string("ghi"), result[2]);
+
+ // Embedded and non-embedded delimiters
+
+ result = tokens("\t\t \nabc\n \tdef\t\tghi \n\n");
+ ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+ EXPECT_EQ(string("ghi"), result[2]);
+
+ // Non-default delimiter
+ result = tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
+ ASSERT_EQ(6, result.size());
+ EXPECT_EQ(string("alpha"), result[0]);
+ EXPECT_EQ(string("beta"), result[1]);
+ EXPECT_EQ(string(" "), result[2]);
+ EXPECT_EQ(string("gamma"), result[3]);
+ EXPECT_EQ(string("delta"), result[4]);
+ EXPECT_EQ(string("epsilon"), result[5]);
+
+ // Non-default delimiters (plural)
+ result = tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**", "*+-");
+ ASSERT_EQ(6, result.size());
+ EXPECT_EQ(string("alpha"), result[0]);
+ EXPECT_EQ(string("beta"), result[1]);
+ EXPECT_EQ(string(" "), result[2]);
+ EXPECT_EQ(string("gamma"), result[3]);
+ EXPECT_EQ(string("delta"), result[4]);
+ EXPECT_EQ(string("epsilon"), result[5]);
+
+ // Escaped delimiter
+ result = tokens("foo\\,bar", ",", true);
+ EXPECT_EQ(1, result.size());
+ EXPECT_EQ(string("foo,bar"), result[0]);
+
+ // Escaped escape
+ result = tokens("foo\\\\,bar", ",", true);
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("foo\\"), result[0]);
+ EXPECT_EQ(string("bar"), result[1]);
+
+ // Double escapes
+ result = tokens("foo\\\\\\\\,\\bar", ",", true);
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("foo\\\\"), result[0]);
+ EXPECT_EQ(string("\\bar"), result[1]);
+
+ // Escaped standard character
+ result = tokens("fo\\o,bar", ",", true);
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("fo\\o"), result[0]);
+ EXPECT_EQ(string("bar"), result[1]);
+
+ // Escape at the end
+ result = tokens("foo,bar\\", ",", true);
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("foo"), result[0]);
+ EXPECT_EQ(string("bar\\"), result[1]);
+
+ // Escape opening a token
+ result = tokens("foo,\\,,bar", ",", true);
+ ASSERT_EQ(3, result.size());
+ EXPECT_EQ(string("foo"), result[0]);
+ EXPECT_EQ(string(","), result[1]);
+ EXPECT_EQ(string("bar"), result[2]);
+}
+
+// Check changing of case.
+TEST_F(StringUtilTest, ChangeCase) {
+ string mixed("abcDEFghiJKLmno123[]{=+--+]}");
+ string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
+ string lower("abcdefghijklmno123[]{=+--+]}");
+
+ string test = mixed;
+ lowercase(test);
+ EXPECT_EQ(lower, test);
+
+ test = mixed;
+ uppercase(test);
+ EXPECT_EQ(upper, test);
+}
+
+TEST_F(StringUtilTest, quotedStringToBinary) {
+ // No opening or closing quote should result in empty string.
+ EXPECT_TRUE(quotedStringToBinary("'").empty());
+ EXPECT_TRUE(quotedStringToBinary("").empty());
+ EXPECT_TRUE(quotedStringToBinary(" ").empty());
+ EXPECT_TRUE(quotedStringToBinary("'circuit id").empty());
+ EXPECT_TRUE(quotedStringToBinary("circuit id'").empty());
+
+ // If there is only opening and closing quote, an empty
+ // vector should be returned.
+ EXPECT_TRUE(quotedStringToBinary("''").empty());
+
+ // Both opening and ending quote is present.
+ EXPECT_EQ("circuit id", checkQuoted("'circuit id'"));
+ EXPECT_EQ("remote id", checkQuoted(" ' remote id'"));
+ EXPECT_EQ("duid", checkQuoted(" ' duid'"));
+ EXPECT_EQ("duid", checkQuoted("'duid ' "));
+ EXPECT_EQ("remote'id", checkQuoted(" ' remote'id '"));
+ EXPECT_EQ("remote id'", checkQuoted("'remote id''"));
+ EXPECT_EQ("'remote id", checkQuoted("''remote id'"));
+
+ // Multiple quotes.
+ EXPECT_EQ("'", checkQuoted("'''"));
+ EXPECT_EQ("''", checkQuoted("''''"));
+}
+
+TEST_F(StringUtilTest, decodeColonSeparatedHexString) {
+ // Test valid strings.
+ checkColonSeparated("A1:02:C3:d4:e5:F6", "A102C3D4E5F6");
+ checkColonSeparated("A:02:3:d:E5:F6", "0A02030DE5F6");
+ checkColonSeparated("A:B:C:D", "0A0B0C0D");
+ checkColonSeparated("1", "01");
+ checkColonSeparated("1e", "1E");
+ checkColonSeparated("", "");
+
+ // Test invalid strings.
+ vector<uint8_t> decoded;
+ // Whitespaces.
+ EXPECT_THROW_MSG(decodeColonSeparatedHexString(" ", decoded), BadValue,
+ "invalid format of the decoded string ' '");
+ // Whitespace before digits.
+ EXPECT_THROW_MSG(decodeColonSeparatedHexString(" A1", decoded), BadValue,
+ "invalid format of the decoded string ' A1'");
+ // Two consecutive colons.
+ EXPECT_THROW_MSG(decodeColonSeparatedHexString("A::01", decoded), BadValue,
+ "two consecutive separators (':') specified in a decoded string 'A::01'");
+ // Three consecutive colons.
+ EXPECT_THROW_MSG(decodeColonSeparatedHexString("A:::01", decoded), BadValue,
+ "two consecutive separators (':') specified in a decoded string 'A:::01'");
+ // Whitespace within a string.
+ EXPECT_THROW_MSG(decodeColonSeparatedHexString("A :01", decoded), BadValue,
+ "' ' is not a valid hexadecimal digit in decoded string 'A :01'");
+ // Terminating colon.
+ EXPECT_THROW_MSG(decodeColonSeparatedHexString("0A:01:", decoded), BadValue,
+ "two consecutive separators (':') specified in a decoded string '0A:01:'");
+ // Opening colon.
+ EXPECT_THROW_MSG(decodeColonSeparatedHexString(":0A:01", decoded), BadValue,
+ "two consecutive separators (':') specified in a decoded string ':0A:01'");
+ // Three digits before the colon.
+ EXPECT_THROW_MSG(decodeColonSeparatedHexString("0A1:B1", decoded), BadValue,
+ "invalid format of the decoded string '0A1:B1'");
+}
+
+TEST_F(StringUtilTest, decodeFormattedHexString) {
+ // Colon separated.
+ checkFormatted("1:A7:B5:4:23", "01A7B50423");
+ // Space separated.
+ checkFormatted("1 A7 B5 4 23", "01A7B50423");
+ // No colons, even number of digits.
+ checkFormatted("17a534", "17A534");
+ // Odd number of digits.
+ checkFormatted("A3A6f78", "0A3A6F78");
+ // '0x' prefix.
+ checkFormatted("0xA3A6f78", "0A3A6F78");
+ // '0x' prefix with a special value of 0.
+ checkFormatted("0x0", "00");
+ // Empty string.
+ checkFormatted("", "");
+
+ vector<uint8_t> decoded;
+ // Dangling colon.
+ EXPECT_THROW_MSG(decodeFormattedHexString("0a:", decoded), BadValue,
+ "two consecutive separators (':') specified in a decoded string '0a:'");
+ // Dangling space.
+ EXPECT_THROW_MSG(decodeFormattedHexString("0a ", decoded), BadValue,
+ "two consecutive separators (' ') specified in a decoded string '0a '");
+ // '0x' prefix and spaces.
+ EXPECT_THROW_MSG(decodeFormattedHexString("0x01 02", decoded), BadValue,
+ "invalid format of the decoded string '0x01 02'");
+ // '0x' prefix and colons.
+ EXPECT_THROW_MSG(decodeFormattedHexString("0x01:02", decoded), BadValue,
+ "invalid format of the decoded string '0x01:02'");
+ // colon and spaces mixed
+ EXPECT_THROW_MSG(decodeFormattedHexString("01:02 03", decoded), BadValue,
+ "invalid format of the decoded string '01:02 03'");
+ // Missing colon.
+ EXPECT_THROW_MSG(decodeFormattedHexString("01:0203", decoded), BadValue,
+ "invalid format of the decoded string '01:0203'");
+ // Missing space.
+ EXPECT_THROW_MSG(decodeFormattedHexString("01 0203", decoded), BadValue,
+ "invalid format of the decoded string '01 0203'");
+ // Invalid prefix.
+ EXPECT_THROW_MSG(decodeFormattedHexString("x0102", decoded), BadValue,
+ "'x0102' is not a valid string of hexadecimal digits");
+ // Invalid prefix again.
+ EXPECT_THROW_MSG(decodeFormattedHexString("1x0102", decoded), BadValue,
+ "'1x0102' is not a valid string of hexadecimal digits");
+}
+
+// Verifies StringSantizer class
+TEST_F(StringUtilTest, stringSanitizer) {
+ // Bad regular expression should throw.
+ StringSanitizerPtr ss;
+
+ try {
+ ss.reset(new StringSanitizer("[bogus-regex", ""));
+ } catch (BadValue const& ex) {
+ unordered_set<string> expected{
+ // BSD
+ "invalid regex: '[bogus-regex', The expression contained mismatched [ and ].",
+ // Linux
+ "invalid regex: '[bogus-regex', Invalid range in bracket expression.",
+ };
+ if (!expected.count(ex.what())) {
+ FAIL() << "unexpected BadValue exception message: " << ex.what();
+ }
+ } catch (exception const& ex) {
+ FAIL() << "unexpected exception: " << ex.what();
+ }
+
+ string good_data(StringSanitizer::MAX_DATA_SIZE, '0');
+ string bad_data(StringSanitizer::MAX_DATA_SIZE + 1, '0');
+
+ ASSERT_NO_THROW_LOG(ss.reset(new StringSanitizer(good_data, good_data)));
+
+ ASSERT_THROW_MSG(ss.reset(new StringSanitizer(bad_data, "")), BadValue,
+ "char set size: '4097' exceeds max size: '4096'");
+ ASSERT_THROW_MSG(ss.reset(new StringSanitizer("", bad_data)), BadValue,
+ "char replacement size: '4097' exceeds max size: '4096'");
+
+ // List of invalid chars should work: (b,c,2 are invalid)
+ checkScrub("abc.123", "[b-c2]", "*", "a**.1*3");
+ // Inverted list of valid chars should work: (b,c,2 are valid)
+ checkScrub("abc.123", "[^b-c2]", "*", "*bc**2*");
+
+ // A string of all valid chars should return an identical string.
+ checkScrub("-_A--B__Cabc34567_-", "[^A-Ca-c3-7_-]", "x", "-_A--B__Cabc34567_-");
+
+ // Replacing with a character should work.
+ checkScrub("A[b]c\12JoE3-_x!B$Y#e", "[^A-Za-z0-9_]", "*", "A*b*c*JoE3*_x*B*Y*e");
+
+ // Removing (i.e.replacing with an "empty" string) should work.
+ checkScrub("A[b]c\12JoE3-_x!B$Y#e", "[^A-Za-z0-9_]", "", "AbcJoE3_xBYe");
+
+ // More than one non-matching in a row should work.
+ checkScrub("%%A%%B%%C%%", "[^A-Za-z0-9_]", "x", "xxAxxBxxCxx");
+
+ // Removing more than one non-matching in a row should work.
+ checkScrub("%%A%%B%%C%%", "[^A-Za-z0-9_]", "", "ABC");
+
+ // Replacing with a string should work.
+ checkScrub("%%A%%B%%C%%", "[^A-Za-z0-9_]", "xyz", "xyzxyzAxyzxyzBxyzxyzCxyzxyz");
+
+ // Dots as valid chars work.
+ checkScrub("abc.123", "[^A-Za-z0-9_.]", "*", "abc.123");
+
+ string withNulls("\000ab\000c.12\0003", 10);
+ checkScrub(withNulls, "[^A-Za-z0-9_.]", "*", "*ab*c.12*3");
+}
+
+// Verifies templated buffer iterator seekTrimmed() function
+TEST_F(StringUtilTest, seekTrimmed) {
+ // Empty buffer should be fine.
+ vector<uint8_t> buffer;
+ auto begin = buffer.end();
+ auto end = buffer.end();
+ ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
+ EXPECT_EQ(0, distance(begin, end));
+
+ // Buffer of only trim values, should be fine.
+ buffer = {1, 1};
+ begin = buffer.begin();
+ end = buffer.end();
+ ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 1));
+ EXPECT_EQ(0, distance(begin, end));
+
+ // One trailing null should trim off.
+ buffer = {'o', 'n', 'e', 0};
+ begin = buffer.begin();
+ end = buffer.end();
+ ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
+ EXPECT_EQ(3, distance(begin, end));
+
+ // More than one trailing null should trim off.
+ buffer = {'t', 'h', 'r', 'e', 'e', 0, 0, 0};
+ begin = buffer.begin();
+ end = buffer.end();
+ ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
+ EXPECT_EQ(5, distance(begin, end));
+
+ // Embedded null should be left in place.
+ buffer = {'e', 'm', 0, 'b', 'e', 'd'};
+ begin = buffer.begin();
+ end = buffer.end();
+ ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
+ EXPECT_EQ(6, distance(begin, end));
+
+ // Leading null should be left in place.
+ buffer = {0, 'l', 'e', 'a', 'd', 'i', 'n', 'g'};
+ begin = buffer.begin();
+ end = buffer.end();
+ ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
+ EXPECT_EQ(8, distance(begin, end));
+}
+
+// Verifies isPrintable predicate on strings.
+TEST_F(StringUtilTest, stringIsPrintable) {
+ string content;
+
+ // Empty is printable.
+ EXPECT_TRUE(isPrintable(content));
+
+ // Check Abcd.
+ content = "Abcd";
+ EXPECT_TRUE(isPrintable(content));
+
+ // Add a control character (not printable).
+ content += "\a";
+ EXPECT_FALSE(isPrintable(content));
+}
+
+// Verifies isPrintable predicate on byte vectors.
+TEST_F(StringUtilTest, vectorIsPrintable) {
+ vector<uint8_t> content;
+
+ // Empty is printable.
+ EXPECT_TRUE(isPrintable(content));
+
+ // Check Abcd.
+ content = {0x41, 0x62, 0x63, 0x64};
+ EXPECT_TRUE(isPrintable(content));
+
+ // Add a control character (not printable).
+ content.push_back('\a');
+ EXPECT_FALSE(isPrintable(content));
+}
+
+} // namespace
diff --git a/src/lib/util/tests/thread_pool_unittest.cc b/src/lib/util/tests/thread_pool_unittest.cc
new file mode 100644
index 0000000..976960a
--- /dev/null
+++ b/src/lib/util/tests/thread_pool_unittest.cc
@@ -0,0 +1,804 @@
+// Copyright (C) 2018-2024 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 <exceptions/exceptions.h>
+#include <util/thread_pool.h>
+
+#include <signal.h>
+
+using namespace isc;
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+/// @brief define CallBack type
+typedef function<void()> CallBack;
+
+/// @brief Test Fixture for testing isc::dhcp::ThreadPool
+class ThreadPoolTest : public ::testing::Test {
+public:
+ /// @brief Constructor
+ ThreadPoolTest() : thread_count_(0), count_(0), wait_(false) {
+ }
+
+ /// @brief task function which registers the thread id and signals main
+ /// thread to stop waiting and then waits for main thread to signal to exit
+ void runAndWait() {
+ // run task
+ run();
+ // wait for main thread signal to exit
+ unique_lock<mutex> lk(wait_mutex_);
+ wait_cv_.wait(lk, [&]() {return (wait() == false);});
+ }
+
+ /// @brief task function which registers the thread id and signals main
+ /// thread to stop waiting
+ void run() {
+ {
+ // make sure this thread has started and it is accounted for
+ lock_guard<mutex> lk(mutex_);
+ auto id = this_thread::get_id();
+ // register this thread as doing work on items
+ ids_.emplace(id);
+ // finish task
+ ++count_;
+ // register this task on the history of this thread
+ history_[id].push_back(count_);
+ }
+ // wake main thread if it is waiting for this thread to process
+ cv_.notify_all();
+ }
+
+ /// @brief task function which tries to stop the thread pool and then calls
+ /// @ref runAndWait
+ ///
+ /// @param thread_pool the thread pool
+ void runStopResetAndWait(ThreadPool<CallBack>* thread_pool) {
+ EXPECT_THROW(thread_pool->stop(), MultiThreadingInvalidOperation);
+ EXPECT_THROW(thread_pool->reset(), MultiThreadingInvalidOperation);
+ EXPECT_THROW(thread_pool->wait(), MultiThreadingInvalidOperation);
+ EXPECT_THROW(thread_pool->wait(0), MultiThreadingInvalidOperation);
+ sigset_t nsset;
+ pthread_sigmask(SIG_SETMASK, 0, &nsset);
+ EXPECT_EQ(1, sigismember(&nsset, SIGCHLD));
+ EXPECT_EQ(1, sigismember(&nsset, SIGINT));
+ EXPECT_EQ(1, sigismember(&nsset, SIGHUP));
+ EXPECT_EQ(1, sigismember(&nsset, SIGTERM));
+ EXPECT_NO_THROW(runAndWait());
+ }
+
+ /// @brief reset all counters and internal test state
+ ///
+ /// @param thread_count number of threads
+ void reset(uint32_t thread_count) {
+ // stop test threads
+ stopThreads();
+ // reset the internal state of the test
+ thread_count_ = thread_count;
+ count_ = 0;
+ wait_ = true;
+ ids_.clear();
+ history_.clear();
+ }
+
+ /// @brief start test threads and block main thread until all tasks have
+ /// been processed
+ ///
+ /// @param thread_count number of threads to be started
+ /// @param items_count number of items to wait for being processed
+ /// @param start create processing threads
+ /// @param signal give main thread control over the threads exit
+ void waitTasks(uint32_t thread_count, uint32_t items_count,
+ bool start = false, bool signal = true) {
+ // make sure we have all threads running when performing all the checks
+ unique_lock<mutex> lck(mutex_);
+ if (start) {
+ // start test threads if explicitly specified
+ startThreads(thread_count, signal);
+ }
+ // wait for the threads to process all the items
+ cv_.wait(lck, [&]() {return (count() == items_count);});
+ }
+
+ /// @brief start test threads
+ ///
+ /// @param thread_count number of threads to be started
+ /// @param signal give main thread control over the threads exit
+ void startThreads(uint32_t thread_count, bool signal = true) {
+ // set default task function to wait for main thread signal
+ auto runFunction = &ThreadPoolTest::runAndWait;
+ if (!signal) {
+ // set the task function to finish immediately
+ runFunction = &ThreadPoolTest::run;
+ }
+ // start test threads
+ for (uint32_t i = 0; i < thread_count; ++i) {
+ threads_.push_back(boost::make_shared<std::thread>(runFunction, this));
+ }
+ }
+
+ /// @brief stop test threads
+ void stopThreads() {
+ // signal threads that are waiting
+ signalThreads();
+ // wait for all test threads to exit
+ for (auto const& thread : threads_) {
+ thread->join();
+ }
+ // reset all threads
+ threads_.clear();
+ }
+
+ /// @brief function used by main thread to unblock processing threads
+ void signalThreads() {
+ {
+ lock_guard<mutex> lk(wait_mutex_);
+ // clear the wait flag so that threads will no longer wait for the main
+ // thread signal
+ wait_ = false;
+ }
+ // wake all threads if waiting for main thread signal
+ wait_cv_.notify_all();
+ }
+
+ /// @brief number of completed tasks
+ ///
+ /// @return the number of completed tasks
+ uint32_t count() {
+ return (count_);
+ }
+
+ /// @brief flag which indicates if working thread should wait for main
+ /// thread signal
+ ///
+ /// @return the wait flag
+ bool wait() {
+ return (wait_);
+ }
+
+ /// @brief check the total number of tasks that have been processed
+ /// Some of the tasks may have been run on the same thread and none may have
+ /// been processed by other threads
+ ///
+ /// @param items_count the number of tasks
+ void checkRunHistory(uint32_t items_count) {
+ uint32_t count = 0;
+ // iterate over all threads history and count all the processed tasks
+ for (auto const& element : history_) {
+ count += element.second.size();
+ }
+ ASSERT_EQ(count, items_count);
+ }
+
+ /// @brief check the total number of threads that have processed tasks
+ ///
+ /// @param number of threads processing tasks
+ void checkIds(uint32_t count) {
+ ASSERT_EQ(ids_.size(), count);
+ }
+
+ /// @brief check the thread pool state
+ ///
+ /// @param thread_pool the thread pool
+ /// @param count the number of tasks
+ /// @param size the thread pool size
+ void checkState(ThreadPool<CallBack>& thread_pool, uint32_t count, uint32_t size) {
+ // the item count should match
+ ASSERT_EQ(thread_pool.count(), count);
+ // the thread count should match
+ ASSERT_EQ(thread_pool.size(), size);
+ }
+
+private:
+ /// @brief thread count used by the test
+ uint32_t thread_count_;
+
+ /// @brief mutex used to keep the internal state consistent
+ std::mutex mutex_;
+
+ /// @brief condition variable used to signal main thread that all threads
+ /// have started processing
+ condition_variable cv_;
+
+ /// @brief mutex used to keep the internal state consistent
+ /// related to the control of the main thread over the working threads exit
+ std::mutex wait_mutex_;
+
+ /// @brief condition variable used to signal working threads to exit
+ condition_variable wait_cv_;
+
+ /// @brief number of completed tasks
+ uint32_t count_;
+
+ /// @brief flag which indicates if working thread should wait for main
+ /// thread signal
+ bool wait_;
+
+ /// @brief the set of thread ids which have completed tasks
+ set<std::thread::id> ids_;
+
+ /// @brief the list of completed tasks run by each thread
+ map<std::thread::id, list<uint32_t>> history_;
+
+ /// @brief the list of test threads
+ list<boost::shared_ptr<std::thread>> threads_;
+};
+
+/// @brief test ThreadPool add and count
+TEST_F(ThreadPoolTest, addAndCount) {
+ uint32_t items_count;
+ CallBack call_back;
+ ThreadPool<CallBack> thread_pool;
+ checkState(thread_pool, 0, 0);
+
+ items_count = 4;
+
+ call_back = std::bind(&ThreadPoolTest::run, this);
+
+ // add items to stopped thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ // the item count should match
+ ASSERT_EQ(thread_pool.count(), items_count);
+
+ // calling reset should clear all threads and should remove all queued items
+ EXPECT_NO_THROW(thread_pool.reset());
+ checkState(thread_pool, 0, 0);
+}
+
+/// @brief test ThreadPool start and stop
+TEST_F(ThreadPoolTest, startAndStop) {
+ uint32_t items_count;
+ uint32_t thread_count;
+ CallBack call_back;
+ ThreadPool<CallBack> thread_pool;
+ checkState(thread_pool, 0, 0);
+
+ items_count = 4;
+ thread_count = 4;
+ // prepare setup
+ reset(thread_count);
+
+ // create tasks which block thread pool threads until signaled by main
+ // thread to force all threads of the thread pool to run exactly one task
+ call_back = std::bind(&ThreadPoolTest::runAndWait, this);
+
+ // calling start should create the threads and should keep the queued items
+ EXPECT_THROW(thread_pool.start(0), InvalidParameter);
+ // calling start should create the threads and should keep the queued items
+ EXPECT_NO_THROW(thread_pool.start(thread_count));
+ checkState(thread_pool, 0, thread_count);
+
+ // do it once again to check if it works
+ EXPECT_THROW(thread_pool.start(thread_count), InvalidOperation);
+ checkState(thread_pool, 0, thread_count);
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_NO_THROW(thread_pool.stop());
+ checkState(thread_pool, 0, 0);
+
+ // do it once again to check if it works
+ EXPECT_THROW(thread_pool.stop(), InvalidOperation);
+ checkState(thread_pool, 0, 0);
+
+ // add items to stopped thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ checkState(thread_pool, items_count, 0);
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_THROW(thread_pool.stop(), InvalidOperation);
+ checkState(thread_pool, items_count, 0);
+
+ // calling reset should clear all threads and should remove all queued items
+ EXPECT_NO_THROW(thread_pool.reset());
+ checkState(thread_pool, 0, 0);
+
+ // do it once again to check if it works
+ EXPECT_NO_THROW(thread_pool.reset());
+ checkState(thread_pool, 0, 0);
+
+ // calling start should create the threads and should keep the queued items
+ EXPECT_NO_THROW(thread_pool.start(thread_count));
+ checkState(thread_pool, 0, thread_count);
+
+ // add items to running thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ // wait for all items to be processed
+ waitTasks(thread_count, items_count);
+ checkState(thread_pool, 0, thread_count);
+ // as each thread pool thread is still waiting on main to unblock, each
+ // thread should have been registered in ids list
+ checkIds(items_count);
+ // all items should have been processed
+ ASSERT_EQ(count(), items_count);
+
+ // check that the number of processed tasks matches the number of items
+ checkRunHistory(items_count);
+
+ // signal thread pool tasks to continue
+ signalThreads();
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_NO_THROW(thread_pool.stop());
+ checkState(thread_pool, 0, 0);
+
+ items_count = 64;
+ thread_count = 16;
+ // prepare setup
+ reset(thread_count);
+
+ // create tasks which do not block the thread pool threads so that several
+ // tasks can be run on the same thread and some of the threads never even
+ // having a chance to run
+ call_back = std::bind(&ThreadPoolTest::run, this);
+
+ // add items to stopped thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ checkState(thread_pool, items_count, 0);
+
+ // calling start should create the threads and should keep the queued items
+ EXPECT_NO_THROW(thread_pool.start(thread_count));
+ // the thread count should match
+ ASSERT_EQ(thread_pool.size(), thread_count);
+
+ // wait for all items to be processed
+ waitTasks(thread_count, items_count);
+ checkState(thread_pool, 0, thread_count);
+ // all items should have been processed
+ ASSERT_EQ(count(), items_count);
+
+ // check that the number of processed tasks matches the number of items
+ checkRunHistory(items_count);
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_NO_THROW(thread_pool.stop());
+ checkState(thread_pool, 0, 0);
+
+ items_count = 16;
+ thread_count = 16;
+ // prepare setup
+ reset(thread_count);
+
+ // create tasks which try to stop the thread pool and then block thread pool
+ // threads until signaled by main thread to force all threads of the thread
+ // pool to run exactly one task
+ call_back = std::bind(&ThreadPoolTest::runStopResetAndWait, this, &thread_pool);
+
+ // calling start should create the threads and should keep the queued items
+ EXPECT_NO_THROW(thread_pool.start(thread_count));
+ checkState(thread_pool, 0, thread_count);
+
+ // add items to running thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ // wait for all items to be processed
+ waitTasks(thread_count, items_count);
+ checkState(thread_pool, 0, thread_count);
+ // as each thread pool thread is still waiting on main to unblock, each
+ // thread should have been registered in ids list
+ checkIds(items_count);
+ // all items should have been processed
+ ASSERT_EQ(count(), items_count);
+
+ // check that the number of processed tasks matches the number of items
+ checkRunHistory(items_count);
+
+ // signal thread pool tasks to continue
+ signalThreads();
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_NO_THROW(thread_pool.stop());
+ checkState(thread_pool, 0, 0);
+
+ /// statistics
+ std::cout << "stat10: " << thread_pool.getQueueStat(10) << std::endl;
+ std::cout << "stat100: " << thread_pool.getQueueStat(100) << std::endl;
+ std::cout << "stat1000: " << thread_pool.getQueueStat(1000) << std::endl;
+}
+
+/// @brief test ThreadPool wait
+TEST_F(ThreadPoolTest, wait) {
+ uint32_t items_count;
+ uint32_t thread_count;
+ CallBack call_back;
+ ThreadPool<CallBack> thread_pool;
+ checkState(thread_pool, 0, 0);
+
+ // calling wait should do nothing if not started
+ EXPECT_NO_THROW(thread_pool.wait());
+ checkState(thread_pool, 0, 0);
+
+ // do it once again to check if it works
+ EXPECT_NO_THROW(thread_pool.wait());
+ checkState(thread_pool, 0, 0);
+
+ items_count = 16;
+ thread_count = 16;
+ // prepare setup
+ reset(thread_count);
+
+ // create tasks which block thread pool threads until signaled by main
+ // thread to force all threads of the thread pool to run exactly one task
+ call_back = std::bind(&ThreadPoolTest::runAndWait, this);
+
+ // add items to stopped thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ checkState(thread_pool, items_count, 0);
+
+ // calling start should create the threads and should keep the queued items
+ EXPECT_NO_THROW(thread_pool.start(thread_count));
+ // the thread count should match
+ ASSERT_EQ(thread_pool.size(), thread_count);
+
+ // wait for all items to be processed
+ waitTasks(thread_count, items_count);
+ checkState(thread_pool, 0, thread_count);
+ // as each thread pool thread is still waiting on main to unblock, each
+ // thread should have been registered in ids list
+ checkIds(items_count);
+ // all items should have been processed
+ ASSERT_EQ(count(), items_count);
+
+ // check that the number of processed tasks matches the number of items
+ checkRunHistory(items_count);
+
+ // check that waiting on tasks does timeout
+ ASSERT_FALSE(thread_pool.wait(1));
+
+ // signal thread pool tasks to continue
+ signalThreads();
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_NO_THROW(thread_pool.stop());
+ checkState(thread_pool, 0, 0);
+
+ items_count = 64;
+ thread_count = 16;
+ // prepare setup
+ reset(thread_count);
+
+ // create tasks which do not block the thread pool threads so that several
+ // tasks can be run on the same thread and some of the threads never even
+ // having a chance to run
+ call_back = std::bind(&ThreadPoolTest::run, this);
+
+ // add items to stopped thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ checkState(thread_pool, items_count, 0);
+
+ // calling start should create the threads and should keep the queued items
+ EXPECT_NO_THROW(thread_pool.start(thread_count));
+ // the thread count should match
+ ASSERT_EQ(thread_pool.size(), thread_count);
+
+ // wait for all items to be processed
+ thread_pool.wait();
+ checkState(thread_pool, 0, thread_count);
+ // all items should have been processed
+ ASSERT_EQ(count(), items_count);
+
+ // check that the number of processed tasks matches the number of items
+ checkRunHistory(items_count);
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_NO_THROW(thread_pool.stop());
+ checkState(thread_pool, 0, 0);
+
+ items_count = 16;
+ thread_count = 256;
+ // prepare setup
+ reset(thread_count);
+
+ // add items to stopped thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ checkState(thread_pool, items_count, 0);
+
+ // calling start should create the threads and should keep the queued items
+ EXPECT_NO_THROW(thread_pool.start(thread_count));
+ // the thread count should match
+ ASSERT_EQ(thread_pool.size(), thread_count);
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_NO_THROW(thread_pool.stop());
+ checkState(thread_pool, 0, 0);
+
+ // wait for all items to be processed
+ ASSERT_TRUE(thread_pool.wait(1));
+ checkState(thread_pool, 0, 0);
+}
+
+/// @brief test ThreadPool pause and resume
+TEST_F(ThreadPoolTest, pauseAndResume) {
+ uint32_t items_count;
+ uint32_t thread_count;
+ CallBack call_back;
+ ThreadPool<CallBack> thread_pool;
+ checkState(thread_pool, 0, 0);
+
+ items_count = 4;
+ thread_count = 4;
+ // prepare setup
+ reset(thread_count);
+
+ // create tasks which block thread pool threads until signaled by main
+ // thread to force all threads of the thread pool to run exactly one task
+ call_back = std::bind(&ThreadPoolTest::runAndWait, this);
+
+ // calling resume should do nothing if not started
+ EXPECT_NO_THROW(thread_pool.resume());
+ checkState(thread_pool, 0, 0);
+
+ // do it once again to check if it works
+ EXPECT_NO_THROW(thread_pool.resume());
+ checkState(thread_pool, 0, 0);
+
+ // calling pause should do nothing if not started
+ EXPECT_NO_THROW(thread_pool.pause());
+ checkState(thread_pool, 0, 0);
+
+ // do it once again to check if it works
+ EXPECT_NO_THROW(thread_pool.pause());
+ checkState(thread_pool, 0, 0);
+
+ // calling resume should do nothing if not started
+ EXPECT_NO_THROW(thread_pool.resume());
+ checkState(thread_pool, 0, 0);
+
+ // add items to stopped thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ checkState(thread_pool, items_count, 0);
+
+ // calling pause should do nothing if not started
+ EXPECT_NO_THROW(thread_pool.pause());
+ checkState(thread_pool, items_count, 0);
+
+ // calling resume should do nothing if not started
+ EXPECT_NO_THROW(thread_pool.resume());
+ checkState(thread_pool, items_count, 0);
+
+ // calling pause should do nothing if not started
+ EXPECT_NO_THROW(thread_pool.pause());
+ checkState(thread_pool, items_count, 0);
+
+ // calling start should create the threads and should keep the queued items
+ EXPECT_NO_THROW(thread_pool.start(thread_count));
+ checkState(thread_pool, items_count, thread_count);
+
+ // calling resume should resume threads
+ EXPECT_NO_THROW(thread_pool.resume());
+ // the thread count should match
+ ASSERT_EQ(thread_pool.size(), thread_count);
+
+ // wait for all items to be processed
+ waitTasks(thread_count, items_count);
+ checkState(thread_pool, 0, thread_count);
+ // as each thread pool thread is still waiting on main to unblock, each
+ // thread should have been registered in ids list
+ checkIds(items_count);
+ // all items should have been processed
+ ASSERT_EQ(count(), items_count);
+
+ // check that the number of processed tasks matches the number of items
+ checkRunHistory(items_count);
+
+ // signal thread pool tasks to continue
+ signalThreads();
+
+ // do it once again to check if it works
+ EXPECT_NO_THROW(thread_pool.resume());
+ checkState(thread_pool, 0, thread_count);
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_NO_THROW(thread_pool.stop());
+ checkState(thread_pool, 0, 0);
+
+ items_count = 64;
+ thread_count = 16;
+ // prepare setup
+ reset(thread_count);
+
+ // create tasks which do not block the thread pool threads so that several
+ // tasks can be run on the same thread and some of the threads never even
+ // having a chance to run
+ call_back = std::bind(&ThreadPoolTest::run, this);
+
+ // add items to stopped thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ checkState(thread_pool, items_count, 0);
+
+ // calling start should create the threads and should keep the queued items
+ EXPECT_NO_THROW(thread_pool.start(thread_count));
+ // the thread count should match
+ ASSERT_EQ(thread_pool.size(), thread_count);
+
+ // wait for all items to be processed
+ waitTasks(thread_count, items_count);
+ checkState(thread_pool, 0, thread_count);
+ // all items should have been processed
+ ASSERT_EQ(count(), items_count);
+
+ // check that the number of processed tasks matches the number of items
+ checkRunHistory(items_count);
+
+ // calling pause should pause threads
+ EXPECT_NO_THROW(thread_pool.pause());
+ checkState(thread_pool, 0, thread_count);
+
+ // do it once again to check if it works
+ EXPECT_NO_THROW(thread_pool.pause());
+ checkState(thread_pool, 0, thread_count);
+
+ // prepare setup
+ reset(thread_count);
+
+ // add items to paused thread pool
+ for (uint32_t i = 0; i < items_count; ++i) {
+ bool ret = true;
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ checkState(thread_pool, items_count, thread_count);
+
+ // calling resume should resume threads
+ EXPECT_NO_THROW(thread_pool.resume());
+ // the thread count should match
+ ASSERT_EQ(thread_pool.size(), thread_count);
+
+ // wait for all items to be processed
+ waitTasks(thread_count, items_count);
+ checkState(thread_pool, 0, thread_count);
+ // all items should have been processed
+ ASSERT_EQ(count(), items_count);
+
+ // check that the number of processed tasks matches the number of items
+ checkRunHistory(items_count);
+
+ // calling stop should clear all threads and should keep queued items
+ EXPECT_NO_THROW(thread_pool.stop());
+ checkState(thread_pool, 0, 0);
+}
+
+/// @brief test ThreadPool max queue size
+TEST_F(ThreadPoolTest, maxQueueSize) {
+ uint32_t items_count;
+ CallBack call_back;
+ ThreadPool<CallBack> thread_pool;
+ // the item count should be 0
+ ASSERT_EQ(thread_pool.count(), 0);
+ // the thread count should be 0
+ ASSERT_EQ(thread_pool.size(), 0);
+
+ items_count = 20;
+
+ call_back = std::bind(&ThreadPoolTest::run, this);
+
+ // add items to stopped thread pool
+ bool ret = true;
+ for (uint32_t i = 0; i < items_count; ++i) {
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ // the item count should match
+ ASSERT_EQ(thread_pool.count(), items_count);
+
+ // change the max count
+ ASSERT_EQ(thread_pool.getMaxQueueSize(), 0);
+ size_t max_queue_size = 10;
+ thread_pool.setMaxQueueSize(max_queue_size);
+ EXPECT_EQ(thread_pool.getMaxQueueSize(), max_queue_size);
+
+ // adding an item should squeeze the queue
+ EXPECT_EQ(thread_pool.count(), items_count);
+ EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
+ EXPECT_FALSE(ret);
+ EXPECT_EQ(thread_pool.count(), max_queue_size);
+}
+
+/// @brief test ThreadPool add front.
+TEST_F(ThreadPoolTest, addFront) {
+ uint32_t items_count;
+ CallBack call_back;
+ ThreadPool<CallBack> thread_pool;
+ // the item count should be 0
+ ASSERT_EQ(thread_pool.count(), 0);
+ // the thread count should be 0
+ ASSERT_EQ(thread_pool.size(), 0);
+
+ items_count = 20;
+
+ call_back = std::bind(&ThreadPoolTest::run, this);
+
+ // add items to stopped thread pool
+ bool ret = true;
+ for (uint32_t i = 0; i < items_count; ++i) {
+ EXPECT_NO_THROW(ret = thread_pool.addFront(boost::make_shared<CallBack>(call_back)));
+ EXPECT_TRUE(ret);
+ }
+
+ // the item count should match
+ ASSERT_EQ(thread_pool.count(), items_count);
+
+ // change the max count
+ ASSERT_EQ(thread_pool.getMaxQueueSize(), 0);
+ size_t max_queue_size = 10;
+ thread_pool.setMaxQueueSize(max_queue_size);
+ EXPECT_EQ(thread_pool.getMaxQueueSize(), max_queue_size);
+
+ // adding an item at front should change nothing queue
+ EXPECT_EQ(thread_pool.count(), items_count);
+ EXPECT_NO_THROW(ret = thread_pool.addFront(boost::make_shared<CallBack>(call_back)));
+ EXPECT_FALSE(ret);
+ EXPECT_EQ(thread_pool.count(), items_count);
+}
+
+/// @brief test ThreadPool get queue statistics.
+TEST_F(ThreadPoolTest, getQueueStat) {
+ ThreadPool<CallBack> thread_pool;
+ EXPECT_THROW(thread_pool.getQueueStat(0), InvalidParameter);
+ EXPECT_THROW(thread_pool.getQueueStat(1), InvalidParameter);
+ EXPECT_THROW(thread_pool.getQueueStat(-10), InvalidParameter);
+ EXPECT_THROW(thread_pool.getQueueStat(10000), InvalidParameter);
+ EXPECT_NO_THROW(thread_pool.getQueueStat(10));
+ EXPECT_NO_THROW(thread_pool.getQueueStat(100));
+ EXPECT_NO_THROW(thread_pool.getQueueStat(1000));
+}
+
+} // namespace
diff --git a/src/lib/util/tests/triplet_unittest.cc b/src/lib/util/tests/triplet_unittest.cc
new file mode 100644
index 0000000..9736011
--- /dev/null
+++ b/src/lib/util/tests/triplet_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <util/triplet.h>
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+
+#include <stdint.h>
+
+using namespace isc::util;
+using namespace isc;
+
+namespace {
+
+// constructor validation
+TEST(TripletTest, constructor) {
+
+ const uint32_t min = 10;
+ const uint32_t value = 20;
+ const uint32_t max = 30;
+
+ Triplet<uint32_t> x(min, value, max);
+
+ EXPECT_EQ(min, x.getMin());
+ EXPECT_EQ(value, x.get());
+ EXPECT_EQ(max, x.getMax());
+ EXPECT_FALSE(x.unspecified());
+
+ // requested values below min should return allowed min value
+ EXPECT_EQ(min, x.get(min - 5));
+
+ EXPECT_EQ(min, x.get(min));
+
+ // requesting a value from within the range (min < x < max) should
+ // return the requested value
+ EXPECT_EQ(17, x.get(17));
+
+ EXPECT_EQ(max, x.get(max));
+
+ EXPECT_EQ(max, x.get(max + 5));
+
+ // this will be boring. It is expected to return 42 no matter what
+ Triplet<uint32_t> y(42);
+
+ EXPECT_EQ(42, y.getMin()); // min, default and max are equal to 42
+ EXPECT_EQ(42, y.get()); // it returns ...
+ EXPECT_EQ(42, y.getMax()); // the exact value...
+ EXPECT_FALSE(x.unspecified());
+
+ // requested values below or above are ignore
+ EXPECT_EQ(42, y.get(5)); // all...
+ EXPECT_EQ(42, y.get(42)); // the...
+ EXPECT_EQ(42, y.get(80)); // time!
+}
+
+TEST(TripletTest, unspecified) {
+ Triplet<uint32_t> x;
+ // When using the constructor without parameters, the triplet
+ // value is unspecified.
+ EXPECT_EQ(0, x.getMin());
+ EXPECT_EQ(0, x.get());
+ EXPECT_EQ(0, x.getMax());
+ EXPECT_TRUE(x.unspecified());
+
+ // For the triplet which has unspecified value we can call accessors
+ // without an exception.
+ uint32_t exp_unspec = 0;
+ EXPECT_EQ(exp_unspec, x);
+
+ x = 72;
+ // Check if the new value has been assigned.
+ EXPECT_EQ(72, x.getMin());
+ EXPECT_EQ(72, x.get());
+ EXPECT_EQ(72, x.getMax());
+ // Triplet is now specified.
+ EXPECT_FALSE(x.unspecified());
+}
+
+// Triplets must be easy to use.
+// Simple to/from int conversions must be done on the fly.
+TEST(TripletTest, operator) {
+
+ uint32_t x = 47;
+
+ Triplet<uint32_t> foo(1,2,3);
+ Triplet<uint32_t> bar(4,5,6);
+
+ foo = bar;
+
+ EXPECT_EQ(4, foo.getMin());
+ EXPECT_EQ(5, foo.get());
+ EXPECT_EQ(6, foo.getMax());
+ EXPECT_FALSE(foo.unspecified());
+
+ // assignment operator: uint32_t => triplet
+ Triplet<uint32_t> y(0);
+ y = x;
+
+ EXPECT_EQ(x, y.get());
+
+ // let's try the other way around: triplet => uint32_t
+ uint32_t z = 0;
+ z = y;
+
+ EXPECT_EQ(x, z);
+}
+
+// check if specified values are sane
+TEST(TripletTest, sanityCheck) {
+
+ // min is larger than default
+ EXPECT_THROW(Triplet<uint32_t>(6,5,5), BadValue);
+
+ // max is smaller than default
+ EXPECT_THROW(Triplet<uint32_t>(5,5,4), BadValue);
+
+}
+
+}; // end of anonymous namespace
diff --git a/src/lib/util/tests/unlock_guard_unittests.cc b/src/lib/util/tests/unlock_guard_unittests.cc
new file mode 100644
index 0000000..baaf0e7
--- /dev/null
+++ b/src/lib/util/tests/unlock_guard_unittests.cc
@@ -0,0 +1,236 @@
+// Copyright (C) 2020-2024 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/unlock_guard.h>
+#include <exceptions/exceptions.h>
+
+#include <mutex>
+#include <thread>
+
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+/// @brief test mutex class used to check the internal state of a 'fictional'
+/// mutex so that the functionality of the UnlockGuard can be tested
+/// @note the test mutex can be recursive which means that a lock can be called
+/// on the same thread and not resulting in a dead lock
+class TestMutex {
+public:
+ /// @brief Constructor
+ ///
+ /// @param recursive sets the mutex as recursive mutex
+ TestMutex(bool recursive = false) : lock_(0), dead_lock_(false),
+ lock_count_(0), unlock_count_(0), recursive_(recursive) {
+ }
+
+ /// @brief lock the mutex
+ void lock() {
+ lock_guard<mutex> lk(mutex_);
+ if (lock_ >= 1) {
+ // mutex is already locked
+ if (!recursive_) {
+ // lock on a non-recursive mutex resulting in a dead lock
+ dead_lock_ = true;
+ isc_throw(isc::InvalidOperation,
+ "recursive lock on already locked mutex resulting in "
+ "dead lock");
+ } else {
+ // lock on a recursive mutex
+ if (this_thread::get_id() != id_) {
+ // lock on a recursive mutex on a different thread resulting
+ // in a dead lock
+ dead_lock_ = true;
+ isc_throw(isc::InvalidOperation,
+ "recursive lock on a different thread on already "
+ "locked mutex resulting in dead lock");
+ }
+ }
+ }
+ // increment the total number of locks
+ lock_count_++;
+ // increment the lock state
+ lock_++;
+ // save the thread id
+ id_ = this_thread::get_id();
+ }
+
+ /// @brief unlock the mutex
+ void unlock() {
+ lock_guard<mutex> lk(mutex_);
+ if (lock_ <= 0) {
+ // unlock an unlocked mutex
+ isc_throw(isc::InvalidOperation, "unlock on non locked mutex "
+ "resulting in undefined behavior");
+ }
+ if (lock_ == 1) {
+ // only one thread has the lock
+ // self healing mutex resetting the dead lock flag
+ dead_lock_ = false;
+ // reset the thread id
+ id_ = std::thread::id();
+ }
+ // increment the total number of unlocks
+ unlock_count_++;
+ // decrement the lock state
+ lock_--;
+ }
+
+ /// @brief get the mutex lock state
+ ///
+ /// @return the mutex lock state
+ int32_t getLock() {
+ lock_guard<mutex> lk(mutex_);
+ return lock_;
+ }
+
+ /// @brief get the mutex dead lock state
+ ///
+ /// @return the mutex dead lock state
+ bool getDeadLock() {
+ lock_guard<mutex> lk(mutex_);
+ return dead_lock_;
+ }
+
+ /// @brief get the number of locks performed on mutex
+ ///
+ /// @return the mutex number of locks
+ uint32_t getLockCount() {
+ lock_guard<mutex> lk(mutex_);
+ return lock_count_;
+ }
+
+ /// @brief get the number of unlocks performed on mutex
+ ///
+ /// @return the mutex number of unlocks
+ uint32_t getUnlockCount() {
+ lock_guard<mutex> lk(mutex_);
+ return unlock_count_;
+ }
+
+ /// @brief test the internal state of the mutex
+ ///
+ /// @param expected_lock check equality of this value with lock state
+ /// @param expected_lock_count check equality of this value with lock count
+ /// @param expected_unlock_count check equality of this value with unlock count
+ /// @param expected_dead_lock check equality of this value with dead lock state
+ void testMutexState(int32_t expected_lock,
+ uint32_t expected_lock_count,
+ uint32_t expected_unlock_count,
+ bool expected_dead_lock) {
+ ASSERT_EQ(getLock(), expected_lock);
+ ASSERT_EQ(getLockCount(), expected_lock_count);
+ ASSERT_EQ(getUnlockCount(), expected_unlock_count);
+ ASSERT_EQ(getDeadLock(), expected_dead_lock);
+ }
+
+private:
+ /// @brief internal lock state of the mutex
+ int32_t lock_;
+
+ /// @brief state which indicates that the mutex is in dead lock
+ bool dead_lock_;
+
+ /// @brief total number of locks performed on the mutex
+ uint32_t lock_count_;
+
+ /// @brief total number of unlocks performed on the mutex
+ uint32_t unlock_count_;
+
+ /// @brief flag to indicate if the mutex is recursive or not
+ bool recursive_;
+
+ /// @brief mutex used to keep the internal state consistent
+ mutex mutex_;
+
+ /// @brief the id of the thread holding the mutex
+ std::thread::id id_;
+};
+
+/// @brief Test Fixture for testing isc::util::UnlockGuard
+class UnlockGuardTest : public ::testing::Test {
+};
+
+/// @brief test TestMutex functionality with non-recursive mutex, and recursive
+/// mutex
+TEST_F(UnlockGuardTest, testMutex) {
+ shared_ptr<TestMutex> test_mutex;
+ // test non-recursive lock
+ test_mutex = make_shared<TestMutex>();
+ test_mutex->testMutexState(0, 0, 0, false);
+ {
+ // call lock_guard constructor which locks mutex
+ lock_guard<TestMutex> lock(*test_mutex.get());
+ // expect lock 1 lock_count 1 unlock_count 0 dead_lock false
+ test_mutex->testMutexState(1, 1, 0, false);
+ {
+ // call lock_guard constructor which locks mutex resulting in an
+ // exception as the mutex is already locked (dead lock)
+ EXPECT_THROW(lock_guard<TestMutex> lock2(*test_mutex.get()),
+ isc::InvalidOperation);
+ // expect lock 1 lock_count 1 unlock_count 0 dead_lock true
+ // you should not be able to get here...using a real mutex
+ test_mutex->testMutexState(1, 1, 0, true);
+ }
+ // expect lock 1 lock_count 1 unlock_count 0 dead_lock true
+ // you should not be able to get here...using a real mutex
+ test_mutex->testMutexState(1, 1, 0, true);
+ }
+ // expect lock 0 lock_count 1 unlock_count 1 dead_lock false
+ // the implementation is self healing when completely unlocking the mutex
+ test_mutex->testMutexState(0, 1, 1, false);
+ // test recursive lock
+ test_mutex = make_shared<TestMutex>(true);
+ test_mutex->testMutexState(0, 0, 0, false);
+ {
+ // call lock_guard constructor which locks mutex
+ lock_guard<TestMutex> lock(*test_mutex.get());
+ // expect lock 1 lock_count 1 unlock_count 0 dead_lock false
+ test_mutex->testMutexState(1, 1, 0, false);
+ {
+ // call lock_guard constructor which locks mutex but does not block
+ // as this is done on the same thread and the mutex is recursive
+ EXPECT_NO_THROW(lock_guard<TestMutex> lock2(*test_mutex.get()));
+ // expect lock 1 lock_count 2 unlock_count 1 dead_lock false
+ // the destructor was already called in EXPECT_NO_THROW scope
+ test_mutex->testMutexState(1, 2, 1, false);
+ }
+ // expect lock 1 lock_count 2 unlock_count 1 dead_lock false
+ test_mutex->testMutexState(1, 2, 1, false);
+ }
+ // expect lock 0 lock_count 2 unlock_count 2 dead_lock false
+ test_mutex->testMutexState(0, 2, 2, false);
+}
+
+/// @brief test UnlockGuard functionality with non-recursive mutex
+TEST_F(UnlockGuardTest, testUnlockGuard) {
+ shared_ptr<TestMutex> test_mutex;
+ // test non-recursive lock
+ test_mutex = make_shared<TestMutex>();
+ test_mutex->testMutexState(0, 0, 0, false);
+ {
+ // call lock_guard constructor which locks mutex
+ lock_guard<TestMutex> lock(*test_mutex.get());
+ // expect lock 1 lock_count 1 unlock_count 0 dead_lock false
+ test_mutex->testMutexState(1, 1, 0, false);
+ {
+ UnlockGuard<TestMutex> unlock_guard(*test_mutex.get());
+ // expect lock 0 lock_count 1 unlock_count 1 dead_lock false
+ test_mutex->testMutexState(0, 1, 1, false);
+ }
+ // expect lock 1 lock_count 2 unlock_count 1 dead_lock false
+ test_mutex->testMutexState(1, 2, 1, false);
+ }
+ // expect lock 0 lock_count 2 unlock_count 2 dead_lock false
+ test_mutex->testMutexState(0, 2, 2, false);
+}
+
+} // namespace
diff --git a/src/lib/util/tests/utf8_unittest.cc b/src/lib/util/tests/utf8_unittest.cc
new file mode 100644
index 0000000..168f38b
--- /dev/null
+++ b/src/lib/util/tests/utf8_unittest.cc
@@ -0,0 +1,50 @@
+// Copyright (C) 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 <util/encode/utf8.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace std;
+
+namespace {
+
+// Verify it does nothing for ASCII.
+TEST(Utf8Test, foobar) {
+ string str("foobar");
+ vector<uint8_t> vec8 = encodeUtf8(str);
+ ASSERT_FALSE(vec8.empty());
+ const char* start = reinterpret_cast<const char*>(&vec8[0]);
+ string str8(start, start + vec8.size());
+ EXPECT_EQ(str, str8);
+}
+
+// Verify it encodes not ASCII as expected.
+TEST(Utf8Test, eightc) {
+ string str("-\x8c-");
+ vector<uint8_t> vec8 = encodeUtf8(str);
+ ASSERT_FALSE(vec8.empty());
+ const char* start = reinterpret_cast<const char*>(&vec8[0]);
+ string str8(start, start + vec8.size());
+ string expected("-\xc2\x8c-");
+ EXPECT_EQ(expected, str8);
+}
+
+// Verify it handles correctly control characters.
+TEST(Utf8Test, control) {
+ string str("fo\x00\n\bar");
+ vector<uint8_t> vec8 = encodeUtf8(str);
+ ASSERT_FALSE(vec8.empty());
+ const char* start = reinterpret_cast<const char*>(&vec8[0]);
+ string str8(start, start + vec8.size());
+ EXPECT_EQ(str, str8);
+}
+
+}
diff --git a/src/lib/util/tests/versioned_csv_file_unittest.cc b/src/lib/util/tests/versioned_csv_file_unittest.cc
new file mode 100644
index 0000000..36a1f91
--- /dev/null
+++ b/src/lib/util/tests/versioned_csv_file_unittest.cc
@@ -0,0 +1,501 @@
+// Copyright (C) 2015-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <util/versioned_csv_file.h>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/constants.hpp>
+#include <boost/algorithm/string/split.hpp>
+
+namespace {
+
+using namespace isc::util;
+
+/// @brief Test fixture class for testing operations on VersionedCSVFile.
+///
+/// It implements basic operations on files, such as reading writing
+/// file removal and checking presence of the file. This is used by
+/// unit tests to verify correctness of the file created by the
+/// CSVFile class.
+class VersionedCSVFileTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Sets the path to the CSV file used throughout the tests.
+ /// The name of the file is test.csv and it is located in the
+ /// current build folder.
+ ///
+ /// It also deletes any dangling files after previous tests.
+ VersionedCSVFileTest();
+
+ /// @brief Destructor.
+ ///
+ /// Deletes the test CSV file if any.
+ virtual ~VersionedCSVFileTest();
+
+ /// @brief Prepends the absolute path to the file specified
+ /// as an argument.
+ ///
+ /// @param filename Name of the file.
+ /// @return Absolute path to the test file.
+ static std::string absolutePath(const std::string& filename);
+
+ /// @brief Check if test file exists on disk.
+ bool exists() const;
+
+ /// @brief Reads whole CSV file.
+ ///
+ /// @return Contents of the file.
+ std::string readFile() const;
+
+ /// @brief Removes existing file (if any).
+ int removeFile() const;
+
+ /// @brief Creates file with contents.
+ ///
+ /// @param contents Contents of the file.
+ void writeFile(const std::string& contents) const;
+
+ /// @brief Absolute path to the file used in the tests.
+ std::string testfile_;
+
+};
+
+VersionedCSVFileTest::VersionedCSVFileTest()
+ : testfile_(absolutePath("test.csv")) {
+ static_cast<void>(removeFile());
+}
+
+VersionedCSVFileTest::~VersionedCSVFileTest() {
+ static_cast<void>(removeFile());
+}
+
+std::string
+VersionedCSVFileTest::absolutePath(const std::string& filename) {
+ std::ostringstream s;
+ s << TEST_DATA_BUILDDIR << "/" << filename;
+ return (s.str());
+}
+
+bool
+VersionedCSVFileTest::exists() const {
+ std::ifstream fs(testfile_.c_str());
+ bool ok = fs.good();
+ fs.close();
+ return (ok);
+}
+
+std::string
+VersionedCSVFileTest::readFile() const {
+ std::ifstream fs(testfile_.c_str());
+ if (!fs.is_open()) {
+ return ("");
+ }
+ std::string contents((std::istreambuf_iterator<char>(fs)),
+ std::istreambuf_iterator<char>());
+ fs.close();
+ return (contents);
+}
+
+int
+VersionedCSVFileTest::removeFile() const {
+ return (remove(testfile_.c_str()));
+}
+
+void
+VersionedCSVFileTest::writeFile(const std::string& contents) const {
+ std::ofstream fs(testfile_.c_str(), std::ofstream::out);
+ if (fs.is_open()) {
+ fs << contents;
+ fs.close();
+ }
+}
+
+// This test checks that the function which is used to add columns of the
+// CSV file works as expected.
+TEST_F(VersionedCSVFileTest, addColumn) {
+ boost::scoped_ptr<VersionedCSVFile> csv(new VersionedCSVFile(testfile_));
+
+ // Verify that we're not allowed to open it without the schema
+ ASSERT_THROW(csv->open(), VersionedCSVFileError);
+
+ // Add two columns.
+ ASSERT_NO_THROW(csv->addColumn("animal", "1.0", ""));
+ ASSERT_NO_THROW(csv->addColumn("color", "2.0", "blue"));
+
+ // Make sure we can't add duplicates.
+ EXPECT_THROW(csv->addColumn("animal", "1.0", ""), CSVFileError);
+ EXPECT_THROW(csv->addColumn("color", "2.0", "blue"), CSVFileError);
+
+ // But we should still be able to add unique columns.
+ EXPECT_NO_THROW(csv->addColumn("age", "3.0", "21"));
+
+ // Assert that the file is opened, because the rest of the test relies
+ // on this.
+ ASSERT_NO_THROW(csv->recreate());
+ ASSERT_TRUE(exists());
+
+ // We should have 3 defined columns
+ // Input Header should match defined columns on new files
+ // Valid columns should match defined columns on new files
+ // Minimum valid columns wasn't set. (Remember it's optional)
+ EXPECT_EQ(3, csv->getColumnCount());
+ EXPECT_EQ(3, csv->getInputHeaderCount());
+ EXPECT_EQ(3, csv->getValidColumnCount());
+ EXPECT_EQ(0, csv->getMinimumValidColumns());
+
+ // Schema versions for new files should always match
+ EXPECT_EQ("3.0", csv->getInputSchemaVersion());
+ EXPECT_EQ("3.0", csv->getSchemaVersion());
+
+ // Input Schema State should be current for new files
+ EXPECT_EQ(VersionedCSVFile::CURRENT, csv->getInputSchemaState());
+ EXPECT_FALSE(csv->needsConversion());
+
+ // Make sure we can't add columns (even unique) when the file is open.
+ ASSERT_THROW(csv->addColumn("zoo", "3.0", ""), CSVFileError);
+
+ // Close the file.
+ ASSERT_NO_THROW(csv->close());
+ // And check that now it is possible to add the column.
+ EXPECT_NO_THROW(csv->addColumn("zoo", "3.0", ""));
+}
+
+// Verifies that a current schema version file loads correctly.
+TEST_F(VersionedCSVFileTest, currentSchemaTest) {
+
+ // Create our versioned file, with three columns
+ boost::scoped_ptr<VersionedCSVFile> csv(new VersionedCSVFile(testfile_));
+ ASSERT_NO_THROW(csv->addColumn("animal", "2.0", ""));
+ ASSERT_NO_THROW(csv->addColumn("color", "2.0", "grey"));
+ ASSERT_NO_THROW(csv->addColumn("age", "2.0", "0"));
+
+ // Write a file compliant with the current schema version.
+ writeFile("animal,color,age\n"
+ "cat,black,2\n"
+ "lion,yellow,17\n"
+ "dog,brown,5\n");
+
+ // Header should pass validation and allow the open to succeed.
+ ASSERT_NO_THROW(csv->open());
+
+ // For schema current file We should have:
+ // 3 defined columns
+ // 3 columns total found in the header
+ // 3 valid columns found in the header
+ // Minimum valid columns wasn't set. (Remember it's optional)
+ EXPECT_EQ(3, csv->getColumnCount());
+ EXPECT_EQ(3, csv->getInputHeaderCount());
+ EXPECT_EQ(3, csv->getValidColumnCount());
+ EXPECT_EQ(0, csv->getMinimumValidColumns());
+
+ // Input schema and current schema should both be 2.0
+ EXPECT_EQ("2.0", csv->getInputSchemaVersion());
+ EXPECT_EQ("2.0", csv->getSchemaVersion());
+
+ // Input Schema State should be CURRENT
+ EXPECT_EQ(VersionedCSVFile::CURRENT, csv->getInputSchemaState());
+ EXPECT_FALSE(csv->needsConversion());
+
+ // First row is correct.
+ CSVRow row;
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("cat", row.readAt(0));
+ EXPECT_EQ("black", row.readAt(1));
+ EXPECT_EQ("2", row.readAt(2));
+
+ // Second row is correct.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("lion", row.readAt(0));
+ EXPECT_EQ("yellow", row.readAt(1));
+ EXPECT_EQ("17", row.readAt(2));
+
+ // Third row is correct.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("dog", row.readAt(0));
+ EXPECT_EQ("brown", row.readAt(1));
+ EXPECT_EQ("5", row.readAt(2));
+}
+
+
+// Verifies the basic ability to upgrade valid files.
+// It starts with a version 1.0 file and updates
+// it through two schema evolutions.
+TEST_F(VersionedCSVFileTest, upgradeOlderVersions) {
+
+ // Create version 1.0 schema CSV file
+ writeFile("animal\n"
+ "cat\n"
+ "lion\n"
+ "dog\n");
+
+ // Create our versioned file, with two columns, one for each
+ // schema version
+ boost::scoped_ptr<VersionedCSVFile> csv(new VersionedCSVFile(testfile_));
+ ASSERT_NO_THROW(csv->addColumn("animal", "1.0", ""));
+ ASSERT_NO_THROW(csv->addColumn("color", "2.0", "blue"));
+
+ // Header should pass validation and allow the open to succeed.
+ ASSERT_NO_THROW(csv->open());
+
+ // We should have:
+ // 2 defined columns
+ // 1 column found in the header
+ // 1 valid column in the header
+ // Minimum valid columns wasn't set. (Remember it's optional)
+ EXPECT_EQ(2, csv->getColumnCount());
+ EXPECT_EQ(1, csv->getInputHeaderCount());
+ EXPECT_EQ(1, csv->getValidColumnCount());
+ EXPECT_EQ(0, csv->getMinimumValidColumns());
+
+ // Input schema should be 1.0, while our current schema should be 2.0
+ EXPECT_EQ("1.0", csv->getInputSchemaVersion());
+ EXPECT_EQ("2.0", csv->getSchemaVersion());
+
+ // Input Schema State should be NEEDS_UPGRADE
+ EXPECT_EQ(VersionedCSVFile::NEEDS_UPGRADE, csv->getInputSchemaState());
+ EXPECT_TRUE(csv->needsConversion());
+
+ // First row is correct.
+ CSVRow row;
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("cat", row.readAt(0));
+ EXPECT_EQ("blue", row.readAt(1));
+
+ // Second row is correct.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("lion", row.readAt(0));
+ EXPECT_EQ("blue", row.readAt(1));
+
+ // Third row is correct.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("dog", row.readAt(0));
+ EXPECT_EQ("blue", row.readAt(1));
+
+ // Now, let's try to append something to this file.
+ CSVRow row_write(2);
+ row_write.writeAt(0, "bird");
+ row_write.writeAt(1, "yellow");
+ ASSERT_NO_THROW(csv->append(row_write));
+
+ // Close the file
+ ASSERT_NO_THROW(csv->flush());
+ ASSERT_NO_THROW(csv->close());
+
+
+ // Check the file contents are correct.
+ EXPECT_EQ("animal\n"
+ "cat\n"
+ "lion\n"
+ "dog\n"
+ "bird,yellow\n",
+ readFile());
+
+ // Create a third schema by adding a column
+ ASSERT_NO_THROW(csv->addColumn("age", "3.0", "21"));
+ ASSERT_EQ(3, csv->getColumnCount());
+
+ // Header should pass validation and allow the open to succeed
+ ASSERT_NO_THROW(csv->open());
+
+ // We should have:
+ // 3 defined columns
+ // 1 column found in the header
+ // 1 valid column in the header
+ // Minimum valid columns wasn't set. (Remember it's optional)
+ EXPECT_EQ(3, csv->getColumnCount());
+ EXPECT_EQ(1, csv->getInputHeaderCount());
+ EXPECT_EQ(1, csv->getValidColumnCount());
+ EXPECT_EQ(0, csv->getMinimumValidColumns());
+
+ // Make sure schema versions are accurate
+ EXPECT_EQ("1.0", csv->getInputSchemaVersion());
+ EXPECT_EQ("3.0", csv->getSchemaVersion());
+
+ // Input Schema State should be NEEDS_UPGRADE
+ EXPECT_EQ(VersionedCSVFile::NEEDS_UPGRADE, csv->getInputSchemaState());
+ EXPECT_TRUE(csv->needsConversion());
+
+ // First row is correct.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("cat", row.readAt(0));
+ EXPECT_EQ("blue", row.readAt(1));
+ EXPECT_EQ("21", row.readAt(2));
+
+ // Second row is correct.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("lion", row.readAt(0));
+ EXPECT_EQ("blue", row.readAt(1));
+ EXPECT_EQ("21", row.readAt(2));
+
+ // Third row is correct.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("dog", row.readAt(0));
+ EXPECT_EQ("blue", row.readAt(1));
+ EXPECT_EQ("21", row.readAt(2));
+
+ // Fourth row is correct.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("bird", row.readAt(0));
+ EXPECT_EQ("yellow", row.readAt(1));
+ EXPECT_EQ("21", row.readAt(2));
+}
+
+TEST_F(VersionedCSVFileTest, minimumValidColumn) {
+ // Create version 1.0 schema CSV file
+ writeFile("animal\n"
+ "cat\n"
+ "lion\n"
+ "dog\n");
+
+ // Create our versioned file, with three columns, one for each
+ // schema version
+ boost::scoped_ptr<VersionedCSVFile> csv(new VersionedCSVFile(testfile_));
+ ASSERT_NO_THROW(csv->addColumn("animal", "1.0", ""));
+ ASSERT_NO_THROW(csv->addColumn("color", "2.0", "blue"));
+ ASSERT_NO_THROW(csv->addColumn("age", "3.0", "21"));
+
+ // Verify we can't set minimum columns with a non-existent column
+ EXPECT_THROW(csv->setMinimumValidColumns("bogus"), VersionedCSVFileError);
+
+ // Set the minimum number of columns to "color"
+ csv->setMinimumValidColumns("color");
+ EXPECT_EQ(2, csv->getMinimumValidColumns());
+
+ // Header validation should fail, too few columns
+ ASSERT_THROW(csv->open(), CSVFileError);
+
+ // Set the minimum number of columns to 1. File should parse now.
+ csv->setMinimumValidColumns("animal");
+ EXPECT_EQ(1, csv->getMinimumValidColumns());
+ ASSERT_NO_THROW(csv->open());
+
+ // First row is correct.
+ CSVRow row;
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("cat", row.readAt(0));
+ EXPECT_EQ("blue", row.readAt(1));
+ EXPECT_EQ("21", row.readAt(2));
+
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("lion", row.readAt(0));
+ EXPECT_EQ("blue", row.readAt(1));
+ EXPECT_EQ("21", row.readAt(2));
+
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("dog", row.readAt(0));
+ EXPECT_EQ("blue", row.readAt(1));
+ EXPECT_EQ("21", row.readAt(2));
+}
+
+TEST_F(VersionedCSVFileTest, invalidHeaderColumn) {
+
+ // Create our version 2.0 schema file
+ boost::scoped_ptr<VersionedCSVFile> csv(new VersionedCSVFile(testfile_));
+ ASSERT_NO_THROW(csv->addColumn("animal", "1.0", ""));
+ ASSERT_NO_THROW(csv->addColumn("color", "2.0", "blue"));
+
+ // Create a file with the correct number of columns but a wrong column name
+ writeFile("animal,colour\n"
+ "cat,red\n"
+ "lion,green\n");
+
+ // Header validation should fail, we have an invalid column
+ ASSERT_THROW(csv->open(), CSVFileError);
+}
+
+TEST_F(VersionedCSVFileTest, downGrading) {
+ // Create our version 2.0 schema file
+ boost::scoped_ptr<VersionedCSVFile> csv(new VersionedCSVFile(testfile_));
+ ASSERT_NO_THROW(csv->addColumn("animal", "1.0", ""));
+ ASSERT_NO_THROW(csv->addColumn("color", "2.0", "blue"));
+
+ // Create schema 2.0 file PLUS an extra column
+ writeFile("animal,color,age\n"
+ "cat,red,5\n"
+ "lion,green,8\n");
+
+ // Header should validate and file should open.
+ ASSERT_NO_THROW(csv->open());
+
+ // We should have:
+ // 2 defined columns
+ // 3 columns found in the header
+ // 2 valid columns in the header
+ // Minimum valid columns wasn't set. (Remember it's optional)
+ EXPECT_EQ(2, csv->getColumnCount());
+ EXPECT_EQ(3, csv->getInputHeaderCount());
+ EXPECT_EQ(2, csv->getValidColumnCount());
+ EXPECT_EQ(0, csv->getMinimumValidColumns());
+
+ // Input schema and current schema should both be 2.0
+ EXPECT_EQ("2.0", csv->getInputSchemaVersion());
+ EXPECT_EQ("2.0", csv->getSchemaVersion());
+
+ // Input Schema State should be NEEDS_DOWNGRADE
+ EXPECT_EQ(VersionedCSVFile::NEEDS_DOWNGRADE, csv->getInputSchemaState());
+ EXPECT_TRUE(csv->needsConversion());
+
+ // First row is correct.
+ CSVRow row;
+ EXPECT_TRUE(csv->next(row));
+ EXPECT_EQ("cat", row.readAt(0));
+ EXPECT_EQ("red", row.readAt(1));
+
+ // No data beyond the second column
+ EXPECT_THROW(row.readAt(2), CSVFileError);
+
+ // Second row is correct.
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("lion", row.readAt(0));
+ EXPECT_EQ("green", row.readAt(1));
+
+ // No data beyond the second column
+ EXPECT_THROW(row.readAt(2), CSVFileError);
+}
+
+
+TEST_F(VersionedCSVFileTest, rowChecking) {
+ // Create version 2.0 schema CSV file with a
+ // - valid header
+ // - row 0 has too many values
+ // - row 1 is valid
+ // - row 3 is too few values
+ writeFile("animal,color\n"
+ "cat,red,bogus_row_value\n"
+ "lion,green\n"
+ "too_few\n");
+
+ // Create our versioned file, with two columns, one for each
+ // schema version
+ boost::scoped_ptr<VersionedCSVFile> csv(new VersionedCSVFile(testfile_));
+ csv->addColumn("animal", "1.0", "");
+ csv->addColumn("color", "2.0", "blue");
+
+ // Header validation should pass, so we can open
+ ASSERT_NO_THROW(csv->open());
+
+ CSVRow row;
+ // First row has too many
+ EXPECT_FALSE(csv->next(row));
+
+ // Second row is valid
+ ASSERT_TRUE(csv->next(row));
+ EXPECT_EQ("lion", row.readAt(0));
+ EXPECT_EQ("green", row.readAt(1));
+
+ // Third row has too few
+ EXPECT_FALSE(csv->next(row));
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/util/tests/watch_socket_unittests.cc b/src/lib/util/tests/watch_socket_unittests.cc
new file mode 100644
index 0000000..b503844
--- /dev/null
+++ b/src/lib/util/tests/watch_socket_unittests.cc
@@ -0,0 +1,263 @@
+// Copyright (C) 2014-2018 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 <util/watch_socket.h>
+
+#include <gtest/gtest.h>
+
+#include <sys/select.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+// FIONREAD is here on Solaris
+#include <sys/filio.h>
+#endif
+
+using namespace std;
+using namespace isc;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Returns the result of select() given an fd to check for read status.
+///
+/// @param fd_to_check The file descriptor to test
+///
+/// @return Returns less than one on an error, 0 if the fd is not ready to
+/// read, > 0 if it is ready to read.
+int selectCheck(int fd_to_check) {
+ fd_set read_fds;
+ int maxfd = 0;
+
+ FD_ZERO(&read_fds);
+
+ // Add this socket to listening set
+ FD_SET(fd_to_check, &read_fds);
+ maxfd = fd_to_check;
+
+ struct timeval select_timeout;
+ select_timeout.tv_sec = 0;
+ select_timeout.tv_usec = 0;
+
+ return (select(maxfd + 1, &read_fds, NULL, NULL, &select_timeout));
+}
+
+/// @brief Tests the basic functionality of WatchSocket.
+TEST(WatchSocketTest, basics) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ EXPECT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
+
+ /// Verify that isReady() is false and that a call to select agrees.
+ EXPECT_FALSE(watch->isReady());
+ EXPECT_EQ(0, selectCheck(select_fd));
+
+ /// Verify that the socket can be marked ready.
+ ASSERT_NO_THROW(watch->markReady());
+
+ /// Verify that we have exactly one marker waiting to be read.
+ int count = 0;
+ EXPECT_FALSE(ioctl(select_fd, FIONREAD, &count));
+ EXPECT_EQ(sizeof(WatchSocket::MARKER), count);
+
+ /// Verify that we can call markReady again without error.
+ ASSERT_NO_THROW(watch->markReady());
+
+ /// Verify that we STILL have exactly one marker waiting to be read.
+ EXPECT_FALSE(ioctl(select_fd, FIONREAD, &count));
+ EXPECT_EQ(sizeof(WatchSocket::MARKER), count);
+
+ /// Verify that isReady() is true and that a call to select agrees.
+ EXPECT_TRUE(watch->isReady());
+ EXPECT_EQ(1, selectCheck(select_fd));
+
+ /// Verify that the socket can be cleared.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ /// Verify that isReady() is false and that a call to select agrees.
+ EXPECT_FALSE(watch->isReady());
+ EXPECT_EQ(0, selectCheck(select_fd));
+}
+
+/// @brief Checks behavior when select_fd is closed externally while in the
+/// "cleared" state.
+TEST(WatchSocketTest, closedWhileClear) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
+
+ // Verify that socket does not appear ready.
+ ASSERT_EQ(0, watch->isReady());
+
+ // Interfere by closing the fd.
+ ASSERT_EQ(0, close(select_fd));
+
+ // Verify that socket does not appear ready.
+ ASSERT_EQ(0, watch->isReady());
+
+ // Verify that clear does NOT throw.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ // Verify that trying to mark it fails.
+ ASSERT_THROW(watch->markReady(), WatchSocketError);
+
+ // Verify that clear does NOT throw.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ // Verify that getSelectFd() returns invalid socket.
+ ASSERT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());
+}
+
+/// @brief Checks behavior when select_fd has closed while in the "ready"
+/// state.
+TEST(WatchSocketTest, closedWhileReady) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
+
+ /// Verify that the socket can be marked ready.
+ ASSERT_NO_THROW(watch->markReady());
+ EXPECT_EQ(1, selectCheck(select_fd));
+ EXPECT_TRUE(watch->isReady());
+
+ // Interfere by closing the fd.
+ ASSERT_EQ(0, close(select_fd));
+
+ // Verify that isReady() does not throw.
+ ASSERT_NO_THROW(watch->isReady());
+
+ // and return false.
+ EXPECT_FALSE(watch->isReady());
+
+ // Verify that trying to clear it does not throw.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ // Verify the select_fd fails as socket is invalid/closed.
+ EXPECT_EQ(-1, selectCheck(select_fd));
+
+ // Verify that subsequent attempts to mark it will fail.
+ ASSERT_THROW(watch->markReady(), WatchSocketError);
+}
+
+/// @brief Checks behavior when select_fd has been marked ready but then
+/// emptied by an external read.
+TEST(WatchSocketTest, emptyReadySelectFd) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
+
+ /// Verify that the socket can be marked ready.
+ ASSERT_NO_THROW(watch->markReady());
+ EXPECT_TRUE(watch->isReady());
+ EXPECT_EQ(1, selectCheck(select_fd));
+
+ // Interfere by reading the fd. This should empty the read pipe.
+ uint32_t buf = 0;
+ ASSERT_EQ((read (select_fd, &buf, sizeof(buf))), sizeof(buf));
+ ASSERT_EQ(WatchSocket::MARKER, buf);
+
+ // Really nothing that can be done to protect against this, but let's
+ // make sure we aren't in a weird state.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ // Verify the select_fd does not fail.
+ EXPECT_FALSE(watch->isReady());
+ EXPECT_EQ(0, selectCheck(select_fd));
+
+ // Verify that getSelectFd() returns is still good.
+ ASSERT_EQ(select_fd, watch->getSelectFd());
+}
+
+/// @brief Checks behavior when select_fd has been marked ready but then
+/// contents have been "corrupted" by a partial read.
+TEST(WatchSocketTest, badReadOnClear) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
+
+ /// Verify that the socket can be marked ready.
+ ASSERT_NO_THROW(watch->markReady());
+ EXPECT_TRUE(watch->isReady());
+ EXPECT_EQ(1, selectCheck(select_fd));
+
+ // Interfere by reading the fd. This should empty the read pipe.
+ uint32_t buf = 0;
+ ASSERT_EQ((read (select_fd, &buf, 1)), 1);
+ ASSERT_NE(WatchSocket::MARKER, buf);
+
+ // Really nothing that can be done to protect against this, but let's
+ // make sure we aren't in a weird state.
+ /// @todo maybe clear should never throw, log only
+ ASSERT_THROW(watch->clearReady(), WatchSocketError);
+
+ // Verify the select_fd does not evaluate to ready.
+ EXPECT_FALSE(watch->isReady());
+ EXPECT_NE(1, selectCheck(select_fd));
+
+ // Verify that getSelectFd() returns INVALID.
+ ASSERT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());
+
+ // Verify that subsequent attempt to mark it fails.
+ ASSERT_THROW(watch->markReady(), WatchSocketError);
+}
+
+/// @brief Checks if the socket can be explicitly closed.
+TEST(WatchSocketTest, explicitClose) {
+ WatchSocketPtr watch;
+
+ // Create new instance of the socket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ // Make sure it has been opened by checking that its descriptor
+ // is valid.
+ EXPECT_NE(watch->getSelectFd(), WatchSocket::SOCKET_NOT_VALID);
+
+ // Close the socket.
+ std::string error_string;
+ ASSERT_TRUE(watch->closeSocket(error_string));
+
+ // Make sure that the descriptor is now invalid which indicates
+ // that the socket has been closed.
+ EXPECT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());
+ // No errors should be reported.
+ EXPECT_TRUE(error_string.empty());
+ // Not ready too.
+ ASSERT_NO_THROW(watch->isReady());
+ EXPECT_FALSE(watch->isReady());
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/util/tests/watched_thread_unittest.cc b/src/lib/util/tests/watched_thread_unittest.cc
new file mode 100644
index 0000000..dd01550
--- /dev/null
+++ b/src/lib/util/tests/watched_thread_unittest.cc
@@ -0,0 +1,218 @@
+// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <util/watched_thread.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <functional>
+#include <signal.h>
+#include <unistd.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Test Fixture for testing @c isc::util::WatchedThread
+class WatchedThreadTest : public ::testing::Test {
+public:
+ /// @brief Maximum number of passes allowed in worker event loop
+ static const int WORKER_MAX_PASSES;
+
+ /// @brief Constructor.
+ WatchedThreadTest() {}
+
+ /// @brief Destructor.
+ ~WatchedThreadTest() {
+ }
+
+ /// @brief Sleeps for a given number of event periods sleep
+ /// Each period is 50 ms.
+ void nap(int periods) {
+ usleep(periods * 50 * 1000);
+ };
+
+ /// @brief Worker function to be used by the WatchedThread's thread
+ ///
+ /// The function runs 10 passes through an "event" loop.
+ /// On each pass:
+ /// - check terminate command
+ /// - instigate the desired event (second pass only)
+ /// - naps for 1 period (50ms)
+ ///
+ /// @param watch_type type of event that should occur
+ void worker(WatchedThread::WatchType watch_type) {
+ sigset_t nsset;
+ pthread_sigmask(SIG_SETMASK, 0, &nsset);
+ EXPECT_EQ(1, sigismember(&nsset, SIGCHLD));
+ EXPECT_EQ(1, sigismember(&nsset, SIGINT));
+ EXPECT_EQ(1, sigismember(&nsset, SIGHUP));
+ EXPECT_EQ(1, sigismember(&nsset, SIGTERM));
+ for (passes_ = 1; passes_ < WORKER_MAX_PASSES; ++passes_) {
+
+ // Stop if we're told to do it.
+ if (wthread_->shouldTerminate()) {
+ return;
+ }
+
+ // On the second pass, set the event.
+ if (passes_ == 2) {
+ switch (watch_type) {
+ case WatchedThread::ERROR:
+ wthread_->setError("we have an error");
+ break;
+ case WatchedThread::READY:
+ wthread_->markReady(watch_type);
+ break;
+ case WatchedThread::TERMINATE:
+ default:
+ // Do nothing, we're waiting to be told to stop.
+ break;
+ }
+ }
+
+ // Take a nap.
+ nap(1);
+ }
+
+ // Indicate why we stopped.
+ wthread_->setError("thread expired");
+ }
+
+ /// @brief Current WatchedThread instance.
+ WatchedThreadPtr wthread_;
+
+ /// @brief Counter used to track the number of passes made
+ /// within the thread worker function.
+ std::atomic<int> passes_;
+};
+
+const int WatchedThreadTest::WORKER_MAX_PASSES = 10;
+
+/// Verifies the basic operation of the WatchedThread class.
+/// It checks that a WatchedThread can be created, can be stopped,
+/// and that in set and clear sockets.
+TEST_F(WatchedThreadTest, watchedThreadClassBasics) {
+
+ /// We'll create a WatchedThread and let it run until it expires. (Note this is more
+ /// of a test of WatchedThreadTest itself and ensures that the assumptions made in
+ /// our other tests as to why threads have finished are sound.
+ wthread_.reset(new WatchedThread());
+ ASSERT_FALSE(wthread_->isRunning());
+ wthread_->start(std::bind(&WatchedThreadTest::worker, this, WatchedThread::TERMINATE));
+ ASSERT_TRUE(wthread_->isRunning());
+
+ // Wait more long enough (we hope) for the thread to expire.
+ nap(WORKER_MAX_PASSES * 4);
+
+ // It should have done the maximum number of passes.
+ EXPECT_EQ(passes_, WORKER_MAX_PASSES);
+
+ // Error should be ready and error text should be "thread expired".
+ ASSERT_TRUE(wthread_->isReady(WatchedThread::ERROR));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::READY));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::TERMINATE));
+ EXPECT_EQ("thread expired", wthread_->getLastError());
+
+ // Thread is technically still running, so let's stop it.
+ EXPECT_TRUE(wthread_->isRunning());
+ ASSERT_NO_THROW(wthread_->stop());
+ ASSERT_FALSE(wthread_->isRunning());
+
+ /// Now we'll test stopping a thread.
+ /// Start the WatchedThread, let it run a little and then tell it to stop.
+ wthread_->start(std::bind(&WatchedThreadTest::worker, this, WatchedThread::TERMINATE));
+ ASSERT_TRUE(wthread_->isRunning());
+
+ // No watches should be ready.
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::ERROR));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::READY));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::TERMINATE));
+
+ // Wait a little while.
+ nap(3);
+
+ // Tell it to stop.
+ wthread_->stop();
+ ASSERT_FALSE(wthread_->isRunning());
+
+ // It should have done less than the maximum number of passes.
+ EXPECT_LT(passes_, WORKER_MAX_PASSES);
+
+ // No watches should be ready. Error text should be "thread stopped".
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::ERROR));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::READY));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::TERMINATE));
+ EXPECT_EQ("thread stopped", wthread_->getLastError());
+
+
+ // Next we'll test error notification.
+ // Start the WatchedThread with a thread that sets an error on the second pass.
+ wthread_->start(std::bind(&WatchedThreadTest::worker, this, WatchedThread::ERROR));
+ ASSERT_TRUE(wthread_->isRunning());
+
+ // No watches should be ready.
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::ERROR));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::READY));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::TERMINATE));
+
+ // Wait a little while.
+ nap(6);
+
+ // It should now indicate an error.
+ ASSERT_TRUE(wthread_->isReady(WatchedThread::ERROR));
+ EXPECT_EQ("we have an error", wthread_->getLastError());
+
+ // Tell it to stop.
+ wthread_->stop();
+ ASSERT_FALSE(wthread_->isRunning());
+
+ // It should have done less than the maximum number of passes.
+ EXPECT_LT(passes_, WORKER_MAX_PASSES);
+
+ // No watches should be ready. Error text should be "thread stopped".
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::ERROR));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::READY));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::TERMINATE));
+ EXPECT_EQ("thread stopped", wthread_->getLastError());
+
+
+ // Finally, we'll test data ready notification.
+ // We'll start the WatchedThread with a thread that indicates data ready on its second pass.
+ wthread_->start(std::bind(&WatchedThreadTest::worker, this, WatchedThread::READY));
+ ASSERT_TRUE(wthread_->isRunning());
+
+ // No watches should be ready.
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::ERROR));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::READY));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::TERMINATE));
+
+ // Wait a little while.
+ nap(6);
+
+ // It should now indicate data ready.
+ ASSERT_TRUE(wthread_->isReady(WatchedThread::READY));
+
+ // Tell it to stop.
+ wthread_->stop();
+ ASSERT_FALSE(wthread_->isRunning());
+
+ // It should have done less than the maximum number of passes.
+ EXPECT_LT(passes_, WORKER_MAX_PASSES);
+
+ // No watches should be ready. Error text should be "thread stopped".
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::ERROR));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::READY));
+ ASSERT_FALSE(wthread_->isReady(WatchedThread::TERMINATE));
+ EXPECT_EQ("thread stopped", wthread_->getLastError());
+}
+
+}