Commit f36aab92 authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[master] Merge branch 'trac3186' DHCP hook library user_chk

Adds the hooks subdirectory to bind10/dir for storing hooks shared
libraries, and the first such library for DHCP, user_chk.
parents 99db31bc 2c28d84f
......@@ -1288,6 +1288,10 @@ AC_CONFIG_FILES([Makefile
src/bin/usermgr/Makefile
src/bin/usermgr/tests/Makefile
src/bin/tests/Makefile
src/hooks/Makefile
src/hooks/dhcp/Makefile
src/hooks/dhcp/user_chk/Makefile
src/hooks/dhcp/user_chk/tests/Makefile
src/lib/Makefile
src/lib/asiolink/Makefile
src/lib/asiolink/tests/Makefile
......@@ -1468,6 +1472,7 @@ AC_OUTPUT([doc/version.ent
src/bin/d2/spec_config.h.pre
src/bin/d2/tests/test_data_files_config.h
src/bin/tests/process_rename_test.py
src/hooks/dhcp/user_chk/tests/test_data_files_config.h
src/lib/config/tests/data_def_unittests_config.h
src/lib/dhcpsrv/tests/test_libraries.h
src/lib/python/isc/config/tests/config_test
......
SUBDIRS = lib bin
# @todo hooks lib could be a configurable switch
SUBDIRS += hooks
EXTRA_DIST = \
cppcheck-suppress.lst \
......
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
# Some versions of GCC warn about some versions of Boost regarding
# missing initializer for members in its posix_time.
# https://svn.boost.org/trac/boost/ticket/3477
# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
# Define rule to build logging source files from message file
user_chk_messages.h user_chk_messages.cc: s-messages
s-messages: user_chk_messages.mes
$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/hooks/dhcp/user_chk/user_chk_messages.mes
touch $@
# Tell automake that the message files are built as part of the build process
# (so that they are built before the main library is built).
BUILT_SOURCES = user_chk_messages.h user_chk_messages.cc
# Ensure that the message file is included in the distribution
EXTRA_DIST =
# Get rid of generated message files on a clean
CLEANFILES = *.gcno *.gcda user_chk_messages.h user_chk_messages.cc s-messages
lib_LTLIBRARIES = libdhcp_user_chk.la
libdhcp_user_chk_la_SOURCES =
libdhcp_user_chk_la_SOURCES += load_unload.cc
libdhcp_user_chk_la_SOURCES += subnet_select_co.cc
libdhcp_user_chk_la_SOURCES += user.cc user.h
libdhcp_user_chk_la_SOURCES += user_chk_log.cc user_chk_log.h
libdhcp_user_chk_la_SOURCES += user_data_source.h
libdhcp_user_chk_la_SOURCES += user_file.cc user_file.h
libdhcp_user_chk_la_SOURCES += user_registry.cc user_registry.h
libdhcp_user_chk_la_SOURCES += version.cc
# Until logging in dynamically loaded libraries is fixed, exclude these.
#nodist_libdhcp_user_chk_la_SOURCES = user_chk_messages.cc user_chk_messages.h
libdhcp_user_chk_la_CXXFLAGS = $(AM_CXXFLAGS)
libdhcp_user_chk_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
libdhcp_user_chk_la_LDFLAGS = $(AM_LDFLAGS)
libdhcp_user_chk_la_LDFLAGS += -avoid-version -export-dynamic -module
libdhcp_user_chk_la_LIBADD =
libdhcp_user_chk_la_LIBADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
libdhcp_user_chk_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
libdhcp_user_chk_la_LIBADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
libdhcp_user_chk_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
libdhcp_user_chk_la_LIBADD += $(top_builddir)/src/lib/util/threads/libb10-threads.la
if USE_CLANGPP
# Disable unused parameter warning caused by some of the
# Boost headers when compiling with clang.
libdhcp_user_chk_la_CXXFLAGS += -Wno-unused-parameter
endif
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
/// @file This file defines the load and unload hooks library functions.
#include <hooks/hooks.h>
#include <user_registry.h>
#include <user_file.h>
#include <iostream>
#include <fstream>
#include <errno.h>
using namespace isc::hooks;
/// @brief Pointer to the registry instance.
UserRegistryPtr user_registry;
/// @brief Output filestream for recording user check outcomes.
std::fstream user_chk_output;
/// @brief User registry input file name.
/// @todo Hard-coded for now, this should be configurable.
const char* registry_fname = "/tmp/user_chk_registry.txt";
/// @brief User check outcome file name.
/// @todo Hard-coded for now, this should be configurable.
const char* user_chk_output_fname = "/tmp/user_chk_outcome.txt";
// Functions accessed by the hooks framework use C linkage to avoid the name
// mangling that accompanies use of the C++ compiler as well as to avoid
// issues related to namespaces.
extern "C" {
/// @brief Called by the Hooks library manager when the library is loaded.
///
/// Instantiates the UserRegistry and opens the outcome file. Failure in
/// either results in a failed return code.
///
/// @param unused library handle parameter required by Hooks API.
///
/// @return Returns 0 upon success, non-zero upon failure.
int load(LibraryHandle&) {
// non-zero indicates an error.
int ret_val = 0;
try {
// Instantiate the registry.
user_registry.reset(new UserRegistry());
// Create the data source.
UserDataSourcePtr user_file(new UserFile(registry_fname));
// Set the registry's data source
user_registry->setSource(user_file);
// Do an initial load of the registry.
user_registry->refresh();
// Open up the output file for user_chk results.
user_chk_output.open(user_chk_output_fname,
std::fstream::out | std::fstream::app);
if (!user_chk_output) {
// Grab the system error message.
const char* errmsg = strerror(errno);
isc_throw(isc::Unexpected, "Cannot open output file: "
<< user_chk_output_fname
<< " reason: " << errmsg);
}
}
catch (const std::exception& ex) {
// Log the error and return failure.
std::cout << "DHCP UserCheckHook could not be loaded: "
<< ex.what() << std::endl;
ret_val = 1;
}
return (ret_val);
}
/// @brief Called by the Hooks library manager when the library is unloaded.
///
/// Destroys the UserRegistry and closes the outcome file.
///
/// @return Always returns 0.
int unload() {
try {
user_registry.reset();
if (user_chk_output.is_open()) {
user_chk_output.close();
}
} catch (const std::exception& ex) {
// On the off chance something goes awry, catch it and log it.
// @todo Not sure if we should return a non-zero result or not.
std::cout << "DHCP UserCheckHook could not be unloaded: "
<< ex.what() << std::endl;
}
return (0);
}
}
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
/// @file Defines the subnet4_select and subnet6_select callout functions.
#include <hooks/hooks.h>
#include <dhcp/pkt4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/subnet.h>
#include <user_registry.h>
#include <fstream>
#include <string>
using namespace isc::dhcp;
using namespace isc::hooks;
using namespace std;
extern UserRegistryPtr user_registry;
extern std::fstream user_chk_output;
extern const char* registry_fname;
extern const char* user_chk_output_fname;
// Functions accessed by the hooks framework use C linkage to avoid the name
// mangling that accompanies use of the C++ compiler as well as to avoid
// issues related to namespaces.
extern "C" {
/// @brief Adds an entry to the end of the user check outcome file.
///
/// Each user entry is written in an ini-like format, with one name-value pair
/// per line as follows:
///
/// id_type=<id type>
/// client=<id str>
/// subnet=<subnet str>
/// registered=<is registered>"
///
/// where:
/// <id type> text label of the id type: "HW_ADDR" or "DUID"
/// <id str> user's id formatted as either isc::dhcp::Hwaddr.toText() or
/// isc::dhcp::DUID.toText()
/// <subnet str> selected subnet formatted as isc::dhcp::Subnet4::toText() or
/// isc::dhcp::Subnet6::toText() as appropriate.
/// <is registered> "yes" or "no"
///
/// Sample IPv4 entry would like this:
///
/// @code
/// id_type=DUID
/// client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
/// subnet=2001:db8:2::/64
/// registered=yes
/// id_type=duid
/// @endcode
///
/// Sample IPv4 entry would like this:
///
/// @code
/// id_type=DUID
/// id_type=HW_ADDR
/// client=hwtype=1 00:0c:01:02:03:05
/// subnet=152.77.5.0/24
/// registered=no
/// @endcode
///
/// @param id_type_str text label identify the id type
/// @param id_val_str text representation of the user id
/// @param subnet_str text representation of the selected subnet
/// @param registered boolean indicating if the user is registered or not
void generate_output_record(const std::string& id_type_str,
const std::string& id_val_str,
const std::string& subnet_str,
const bool& registered)
{
user_chk_output << "id_type=" << id_type_str << std::endl
<< "client=" << id_val_str << std::endl
<< "subnet=" << subnet_str << std::endl
<< "registered=" << (registered ? "yes" : "no")
<< std::endl;
// @todo Flush is here to ensure output is immediate for demo purposes.
// Performance would generally dictate not using it.
flush(user_chk_output);
}
/// @brief This callout is called at the "subnet4_select" hook.
///
/// This function searches the UserRegistry for the client indicated by the
/// inbound IPv4 DHCP packet. If the client is found in the registry output
/// the generate outcome record and return.
///
/// If the client is not found in the registry replace the selected subnet
/// with the restricted access subnet, then generate the outcome record and
/// return. By convention, it is assumed that last subnet in the list of
/// available subnets is the restricted access subnet.
///
/// @param handle CalloutHandle which provides access to context.
///
/// @return 0 upon success, non-zero otherwise.
int subnet4_select(CalloutHandle& handle) {
if (!user_registry) {
std::cout << "DHCP UserCheckHook : subnet4_select UserRegistry is null"
<< std::endl;
return (1);
}
try {
// Get subnet collection. If it's empty just bail nothing to do.
const isc::dhcp::Subnet4Collection *subnets = NULL;
handle.getArgument("subnet4collection", subnets);
if (subnets->size() == 0) {
return 0;
}
// Refresh the registry.
user_registry->refresh();
// Get the HWAddress as the user identifier.
Pkt4Ptr query;
handle.getArgument("query4", query);
HWAddrPtr hwaddr = query->getHWAddr();
// Look for the user.
UserPtr registered_user = user_registry->findUser(*hwaddr);
if (registered_user) {
// User is in the registry, so leave the pre-selected subnet alone.
Subnet4Ptr subnet;
handle.getArgument("subnet4", subnet);
// Add the outcome entry to the output file.
generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
subnet->toText(), true);
} else {
// User is not in the registry, so assign them to the last subnet
// in the collection. By convention we are assuming this is the
// restricted subnet.
Subnet4Ptr subnet = subnets->back();
handle.setArgument("subnet4", subnet);
// Add the outcome entry to the output file.
generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
subnet->toText(), false);
}
} catch (const std::exception& ex) {
std::cout << "DHCP UserCheckHook : subnet6_select unexpected error: "
<< ex.what() << std::endl;
return (1);
}
return (0);
}
/// @brief This callout is called at the "subnet6_select" hook.
///
/// This function searches the UserRegistry for the client indicated by the
/// inbound IPv6 DHCP packet. If the client is found in the registry generate
/// the outcome record and return.
///
/// If the client is not found in the registry replace the selected subnet
/// with the restricted access subnet, then generate the outcome record and
/// return. By convention, it is assumed that last subnet in the list of
/// available subnets is the restricted access subnet.
///
/// @param handle CalloutHandle which provides access to context.
///
/// @return 0 upon success, non-zero otherwise.
int subnet6_select(CalloutHandle& handle) {
if (!user_registry) {
std::cout << "DHCP UserCheckHook : subnet6_select UserRegistry is null"
<< std::endl;
return (1);
}
try {
// Get subnet collection. If it's empty just bail nothing to do.
const isc::dhcp::Subnet6Collection *subnets = NULL;
handle.getArgument("subnet6collection", subnets);
if (subnets->size() == 0) {
return 0;
}
// Refresh the registry.
user_registry->refresh();
// Get the HWAddress as the user identifier.
Pkt6Ptr query;
handle.getArgument("query6", query);
DuidPtr duid;
OptionPtr opt_duid = query->getOption(D6O_CLIENTID);
if (!opt_duid) {
std::cout << "DHCP6 query is missing DUID" << std::endl;
return (1);
}
duid = DuidPtr(new DUID(opt_duid->getData()));
// Look for the user.
UserPtr registered_user = user_registry->findUser(*duid);
if (registered_user) {
// User is in the registry, so leave the pre-selected subnet alone.
Subnet6Ptr subnet;
handle.getArgument("subnet6", subnet);
// Add the outcome entry to the output file.
generate_output_record(UserId::DUID_STR, duid->toText(),
subnet->toText(), true);
} else {
// User is not in the registry, so assign them to the last subnet
// in the collection. By convention we are assuming this is the
// restricted subnet.
Subnet6Ptr subnet = subnets->back();
handle.setArgument("subnet6", subnet);
// Add the outcome entry to the output file.
generate_output_record(UserId::DUID_STR, duid->toText(),
subnet->toText(), false);
}
} catch (const std::exception& ex) {
std::cout << "DHCP UserCheckHook : subnet6_select unexpected error: "
<< ex.what() << std::endl;
return (1);
}
return (0);
}
}
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/user_chk
AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/hooks/dhcp/user_chk/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
AM_CXXFLAGS = $(B10_CXXFLAGS)
# Some versions of GCC warn about some versions of Boost regarding
# missing initializer for members in its posix_time.
# https://svn.boost.org/trac/boost/ticket/3477
# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
USER_CHK_LIB = $(top_builddir)/src/hooks/dhcp/user_chk/libdhcp_user_chk.la
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
TESTS_ENVIRONMENT = \
$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
TESTS =
if HAVE_GTEST
TESTS += libdhcp_user_chk_unittests
libdhcp_user_chk_unittests_SOURCES =
libdhcp_user_chk_unittests_SOURCES += ../load_unload.cc
libdhcp_user_chk_unittests_SOURCES += ../subnet_select_co.cc
libdhcp_user_chk_unittests_SOURCES += ../version.cc
libdhcp_user_chk_unittests_SOURCES += ../user.cc ../user.h
libdhcp_user_chk_unittests_SOURCES += ../user_chk_log.cc ../user_chk_log.h
# Until logging in dynamically loaded libraries is fixed, exclude these.
#libdhcp_user_chk_unittests_SOURCES += ../user_chk_messages.cc ../user_chk_messages.h
libdhcp_user_chk_unittests_SOURCES += ../user_data_source.h
libdhcp_user_chk_unittests_SOURCES += ../user_file.cc ../user_file.h
libdhcp_user_chk_unittests_SOURCES += ../user_registry.cc ../user_registry.h
libdhcp_user_chk_unittests_SOURCES += run_unittests.cc
libdhcp_user_chk_unittests_SOURCES += userid_unittests.cc
libdhcp_user_chk_unittests_SOURCES += user_unittests.cc
libdhcp_user_chk_unittests_SOURCES += user_registry_unittests.cc
libdhcp_user_chk_unittests_SOURCES += user_file_unittests.cc
libdhcp_user_chk_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
libdhcp_user_chk_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
libdhcp_user_chk_unittests_CXXFLAGS = $(AM_CXXFLAGS)
if USE_CLANGPP
# This is to workaround unused variables tcout and tcerr in
# log4cplus's streams.h and unused parameters from some of the
# Boost headers.
libdhcp_user_chk_unittests_CXXFLAGS += -Wno-unused-parameter
endif
libdhcp_user_chk_unittests_LDADD = $(top_builddir)/src/lib/log/libb10-log.la
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
libdhcp_user_chk_unittests_LDADD += ${BOTAN_LIBS} ${BOTAN_RPATH}
libdhcp_user_chk_unittests_LDADD += $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <log/logger_support.h>
#include <gtest/gtest.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::log::initLogger();
int result = RUN_ALL_TESTS();
return (result);
}
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
//
/// @brief Path to local dir so tests can locate test data files
#define USER_CHK_TEST_DIR "@abs_top_srcdir@/src/hooks/dhcp/user_chk/tests"
{ "type" : "HW_ADDR", "id" : "01AC00F03344", "opt1" : "true" }
{ "type" : "DUID", "id" : "225060de0a0b", "opt1" : "true" }
{ "type" : "DUID", "id" : "777777777777", "opt1" : "true" }
{ "type" : "BOGUS", "id" : "01AC00F03344", "opt1" : "true" }
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <exceptions/exceptions.h>
#include <test_data_files_config.h>
#include <user_file.h>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
using namespace std;
namespace {
/// @brief Convenience method for reliably building test file path names.
///
/// Function prefixes the given file name with a path to unit tests directory
/// so we can reliably find test data files.
///
/// @param name base file name of the test file
std::string testFilePath(const std::string& name) {
return (std::string(USER_CHK_TEST_DIR) + "/" + name);
}
/// @brief Tests the UserFile constructor.
TEST(UserFile, construction) {
// Verify that a UserFile with no file name is rejected.
ASSERT_THROW(UserFile(""), UserFileError);