diff --git a/CMakeLists.txt b/CMakeLists.txt index a67a325ad0ff34b2c3241c63d8d7ffaf5dfd2dcc..770c284fb9242a202e1c68da090c5c03cda0adc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,9 +29,14 @@ version_numbers ( option (BUILD_SHARED_LIBS "Request build of shared libraries." OFF) set (GFLAGS_SHARED_LIBS ${BUILD_SHARED_LIBS}) +option (BUILD_NEGATIVE_COMPILATION_TESTS "Request addition of negative compilation tests." OFF) +mark_as_advanced(BUILD_NEGATIVE_COMPILATION_TESTS) + set (GFLAGS_NAMESPACE "gflags" CACHE STRING "C++ namespace identifier of gflags library.") mark_as_advanced (GFLAGS_NAMESPACE) +mark_as_advanced (CLEAR CMAKE_INSTALL_PREFIX) +mark_as_advanced (CMAKE_CONFIGURATION_TYPES) if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS AND NOT CMAKE_C_FLAGS) set ( CMAKE_BUILD_TYPE "Release" @@ -121,7 +126,7 @@ else () set (__ATTRIBUTE__UNUSED) endif () -configure_sources (PUBLIC_HDRS ${PUBLIC_HDRS}) +configure_headers (PUBLIC_HDRS ${PUBLIC_HDRS}) configure_sources (PRIVATE_HDRS ${PRIVATE_HDRS}) configure_sources (GFLAGS_SRCS ${GFLAGS_SRCS}) @@ -133,16 +138,19 @@ set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY "lib") # ---------------------------------------------------------------------------- # add library target -if (WIN32 AND BUILD_SHARED_LIBS) - add_definitions (-DGFLAGS_DLL_EXPORT) -endif () include_directories ("${PROJECT_SOURCE_DIR}/src") include_directories ("${PROJECT_BINARY_DIR}/include") include_directories ("${PROJECT_BINARY_DIR}/include/${GFLAGS_NAMESPACE}") add_library (gflags ${GFLAGS_SRCS} ${PRIVATE_HDRS} ${PUBLIC_HDRS}) add_library (gflags_nothreads ${GFLAGS_SRCS} ${PRIVATE_HDRS} ${PUBLIC_HDRS}) -set_target_properties (gflags_nothreads PROPERTIES COMPILE_DEFINITIONS NO_THREADS) + +if (WIN32 AND BUILD_SHARED_LIBS) + set_target_properties (gflags PROPERTIES COMPILE_DEFINITIONS GFLAGS_DLL_EXPORT) + set_target_properties (gflags_nothreads PROPERTIES COMPILE_DEFINITIONS "GFLAGS_DLL_EXPORT;NO_THREADS") +else () + set_target_properties (gflags_nothreads PROPERTIES COMPILE_DEFINITIONS NO_THREADS) +endif () # ---------------------------------------------------------------------------- # installation @@ -191,14 +199,8 @@ configure_file (cmake/config.cmake.in "${PROJECT_BINARY_DIR}/${PACKAGE_NAME}-con # ---------------------------------------------------------------------------- # testing - MUST follow the generation of the build tree config file - -# TODO(andreas) Replace Bash scripts such that tests can be run on Windows (e.g., Python). -# The gflags_unittest.sh script should best be replaced by multiple -# add_test commands in the test/CMakeLists.txt file. -if (UNIX) - include (CTest) - if (BUILD_TESTING) - enable_testing () - add_subdirectory (test) - endif () -endif () +include (CTest) +if (BUILD_TESTING) + enable_testing () + add_subdirectory (test) +endif () \ No newline at end of file diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 717a5086145ece606cf0af940b6db3b5ed66e084..bb6cb1845208e22907bdc01ee184697c04de92be 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -31,6 +31,22 @@ function (version_numbers VERSION MAJOR MINOR PATCH) set ("${PATCH}" "${VERSION_PATCH}" PARENT_SCOPE) endfunction () +# ---------------------------------------------------------------------------- +## Configure public header files +function (configure_headers out) + set (tmp) + foreach (src IN LISTS ARGN) + if (EXISTS "${PROJECT_SOURCE_DIR}/src/${src}.in") + configure_file ("${PROJECT_SOURCE_DIR}/src/${src}.in" "${PROJECT_BINARY_DIR}/include/${GFLAGS_NAMESPACE}/${src}" @ONLY) + list (APPEND tmp "${PROJECT_BINARY_DIR}/include/${GFLAGS_NAMESPACE}/${src}") + else () + configure_file ("${PROJECT_SOURCE_DIR}/src/${src}" "${PROJECT_BINARY_DIR}/include/${GFLAGS_NAMESPACE}/${src}" COPYONLY) + list (APPEND tmp "${PROJECT_BINARY_DIR}/include/${GFLAGS_NAMESPACE}/${src}") + endif () + endforeach () + set (${out} "${tmp}" PARENT_SCOPE) +endfunction () + # ---------------------------------------------------------------------------- ## Configure source files with .in suffix function (configure_sources out) @@ -45,3 +61,22 @@ function (configure_sources out) endforeach () set (${out} "${tmp}" PARENT_SCOPE) endfunction () + +# ---------------------------------------------------------------------------- +## Add usage test +# +# Using PASS_REGULAR_EXPRESSION and FAIL_REGULAR_EXPRESSION would +# do as well, but CMake/CTest does not allow us to specify an +# expected exist status. Moreover, the execute_test.cmake script +# sets environment variables needed by the --fromenv/--tryfromenv tests. +macro (add_gflags_test name expected_rc expected_output unexpected_output cmd) + add_test ( + NAME ${name} + COMMAND "${CMAKE_COMMAND}" "-DCOMMAND:STRING=$<TARGET_FILE:${cmd}>;${ARGN}" + "-DEXPECTED_RC:STRING=${expected_rc}" + "-DEXPECTED_OUTPUT:STRING=${expected_output}" + "-DUNEXPECTED_OUTPUT:STRING=${unexpected_output}" + -P "${PROJECT_SOURCE_DIR}/test/execute_test.cmake" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/test" + ) +endmacro () diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bc16eba4ce1614ed85de9eed43c971d93fb0aba4..73099be3a061fac9d8cc42e6f8e5865b146920be 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,5 @@ ## gflags tests -find_package (PythonInterp) - # ---------------------------------------------------------------------------- # output directories set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Testing/bin") @@ -16,47 +14,153 @@ include_directories ("${CMAKE_CURRENT_SOURCE_DIR}") link_libraries (gflags_nothreads) # ---------------------------------------------------------------------------- -# test executables +# STRIP_FLAG_HELP: check with "strings" that help text is not in binary +if (UNIX) + add_executable (strip_flags gflags_strip_flags_test.cc) + add_test ( + NAME strip_flags + COMMAND /bin/bash "${CMAKE_CURRENT_SOURCE_DIR}/gflags_strip_flags_test.sh" + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/strip_flags" + ) +endif () + +# ---------------------------------------------------------------------------- +# unit tests configure_file (gflags_unittest.cc gflags_unittest-main.cc COPYONLY) configure_file (gflags_unittest.cc gflags_unittest_main.cc COPYONLY) -set (SRCDIR "${CMAKE_CURRENT_SOURCE_DIR}/nc") -configure_file (gflags_nc.py.in "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nc.py" @ONLY) +add_executable (unittest gflags_unittest.cc) +add_executable (unittest2 gflags_unittest-main.cc) +add_executable (unittest3 gflags_unittest_main.cc) + +# First, just make sure the unittest works as-is +add_gflags_test(unittest 0 "" "" unittest) + +# --help should show all flags, including flags from gflags_reporting +add_gflags_test(help-reporting 1 "gflags_reporting.cc" "" unittest --help) + +# Make sure that --help prints even very long helpstrings. +add_gflags_test(long-helpstring 1 "end of a long helpstring" "" unittest --help) + +# Make sure --help reflects flag changes made before flag-parsing +add_gflags_test(changed_bool1 1 "-changed_bool1 (changed) type: bool default: true" "" unittest --help) +add_gflags_test(changed_bool2 1 "-changed_bool2 (changed) type: bool default: false currently: true" "" unittest --help) +# And on the command-line, too +add_gflags_test(changeable_string_var 1 "-changeable_string_var () type: string default: \"1\" currently: \"2\"" "" unittest --changeable_string_var 2 --help) + +# --nohelp and --help=false should be as if we didn't say anything +add_gflags_test(nohelp 0 "PASS" "" unittest --nohelp) +add_gflags_test(help=false 0 "PASS" "" unittest --help=false) + +# --helpfull is the same as help +add_gflags_test(helpfull 1 "gflags_reporting.cc" "" unittest --helpfull) + +# --helpshort should show only flags from the unittest itself +add_gflags_test(helpshort 1 "gflags_unittest.cc" "gflags_reporting.cc" unittest --helpshort) + +# --helpshort should show the tldflag we created in the unittest dir +add_gflags_test(helpshort-tldflag1 1 "tldflag1" "google.cc" unittest --helpshort) +add_gflags_test(helpshort-tldflag2 1 "tldflag2" "google.cc" unittest --helpshort) + +# --helpshort should work if the main source file is suffixed with [_-]main +add_gflags_test(helpshort-main 1 "gflags_unittest-main.cc" "gflags_reporting.cc" unittest2 --helpshort) +add_gflags_test(helpshort_main 1 "gflags_unittest_main.cc" "gflags_reporting.cc" unittest3 --helpshort) + +# --helpon needs an argument +add_gflags_test(helpon 1 "'--helpon' is missing its argument; flag description: show help on" "" unittest --helpon) + +if (BUILD_SHARED_LIBS) + # --helpon argument indicates what file we'll show args from + # TODO(andreas): This test fails. Why is there no help for the gflags module ? + add_gflags_test(helpon=gflags 1 "gflags.cc" "gflags_unittest.cc" unittest --helpon=gflags) + # another way of specifying the argument + # TODO(andreas): This test fails. Why is there no help for the gflags module ? + add_gflags_test(helpon_gflags 1 "gflags.cc" "gflags_unittest.cc" unittest --helpon gflags) +endif () -add_executable (strip_flags gflags_strip_flags_test.cc) -add_executable (unittest gflags_unittest.cc) -add_executable (unittest2 gflags_unittest-main.cc) -add_executable (unittest3 gflags_unittest_main.cc) +# test another argument +add_gflags_test(helpon=gflags_unittest 1 "gflags_unittest.cc" "gflags.cc" unittest --helpon=gflags_unittest) + +# helpmatch is like helpon but takes substrings +add_gflags_test(helpmatch_reporting 1 "gflags_reporting.cc" "gflags_unittest.cc" unittest -helpmatch reporting) +add_gflags_test(helpmatch=unittest 1 "gflags_unittest.cc" "gflags.cc:" unittest -helpmatch=unittest) + +# if no flags are found with helpmatch or helpon, suggest --help +add_gflags_test(helpmatch=nosuchsubstring 1 "No modules matched" "gflags_unittest.cc" unittest -helpmatch=nosuchsubstring) +add_gflags_test(helpon=nosuchmodule 1 "No modules matched" "gflags_unittest.cc" unittest -helpon=nosuchmodule) + +# helppackage shows all the flags in the same dir as this unittest +# --help should show all flags, including flags from google.cc +add_gflags_test(helppackage 1 "gflags_reporting.cc" "" unittest --helppackage) + +# xml! +add_gflags_test(helpxml 1 "gflags_unittest.cc</file>" "gflags_unittest.cc:" unittest --helpxml) + +# just print the version info and exit +add_gflags_test(version-1 0 "gflags_unittest" "gflags_unittest.cc" unittest --version) +add_gflags_test(version-2 0 "version test_version" "gflags_unittest.cc" unittest --version) + +# --undefok is a fun flag... +add_gflags_test(undefok-1 1 "unknown command line flag 'foo'" "" unittest --undefok= --foo --unused_bool) +add_gflags_test(undefok-2 0 "PASS" "" unittest --undefok=foo --foo --unused_bool) +# If you say foo is ok to be undefined, we'll accept --nofoo as well +add_gflags_test(undefok-3 0 "PASS" "" unittest --undefok=foo --nofoo --unused_bool) +# It's ok if the foo is in the middle +add_gflags_test(undefok-4 0 "PASS" "" unittest --undefok=fee,fi,foo,fum --foo --unused_bool) +# But the spelling has to be just right... +add_gflags_test(undefok-5 1 "unknown command line flag 'foo'" "" unittest --undefok=fo --foo --unused_bool) +add_gflags_test(undefok-6 1 "unknown command line flag 'foo'" "" unittest --undefok=foot --foo --unused_bool) + +# See if we can successfully load our flags from the flagfile +add_gflags_test(flagfile.1 0 "gflags_unittest" "gflags_unittest.cc" unittest "--flagfile=${CMAKE_CURRENT_LIST_DIR}/flagfile.1") +add_gflags_test(flagfile.2 0 "PASS" "" unittest "--flagfile=${CMAKE_CURRENT_LIST_DIR}/flagfile.2") +add_gflags_test(flagfile.3 0 "PASS" "" unittest "--flagfile=${CMAKE_CURRENT_LIST_DIR}/flagfile.3") + +# Also try to load flags from the environment +add_gflags_test(fromenv=version 0 "gflags_unittest" "gflags_unittest.cc" unittest --fromenv=version) +add_gflags_test(tryfromenv=version 0 "gflags_unittest" "gflags_unittest.cc" unittest --tryfromenv=version) +add_gflags_test(fromenv=help 0 "PASS" "" unittest --fromenv=help) +add_gflags_test(tryfromenv=help 0 "PASS" "" unittest --tryfromenv=help) +add_gflags_test(fromenv=helpfull 1 "helpfull not found in environment" "" unittest --fromenv=helpfull) +add_gflags_test(tryfromenv=helpfull 0 "PASS" "" unittest --tryfromenv=helpfull) +add_gflags_test(tryfromenv=undefok 0 "PASS" "" unittest --tryfromenv=undefok --foo) +add_gflags_test(tryfromenv=weirdo 1 "unknown command line flag" "" unittest --tryfromenv=weirdo) +add_gflags_test(tryfromenv-multiple 0 "gflags_unittest" "gflags_unittest.cc" unittest --tryfromenv=test_bool,version,unused_bool) +add_gflags_test(fromenv=test_bool 1 "not found in environment" "" unittest --fromenv=test_bool) +add_gflags_test(fromenv=test_bool-ok 1 "unknown command line flag" "" unittest --fromenv=test_bool,ok) +# Here, the --version overrides the fromenv +add_gflags_test(version-overrides-fromenv 0 "gflags_unittest" "gflags_unittest.cc" unittest --fromenv=test_bool,version,ok) + +# Make sure -- by itself stops argv processing +add_gflags_test(dashdash 0 "PASS" "" unittest -- --help) + +# And we should die if the flag value doesn't pass the validator +add_gflags_test(always_fail 1 "ERROR: failed validation of new value 'true' for flag 'always_fail'" "" unittest --always_fail) + +# And if locking in validators fails +# TODO(andreas): Worked on Windows 7 Release configuration, but causes +# debugger abort() intervention in case of Debug configuration. +#add_gflags_test(deadlock_if_cant_lock 0 "PASS" "" unittest --deadlock_if_cant_lock) # ---------------------------------------------------------------------------- # (negative) compilation tests -if (PYTHON_EXECUTABLE) +if (BUILD_NEGATIVE_COMPILATION_TESTS) + find_package (PythonInterp) + if (NOT PYTHON_EXECUTABLE) + message (FATAL_ERROR "No Python installation found! It is required by the negative compilation tests." + " Either install Python or set NEGATIVE_COMPILATION_TESTS to FALSE and try again.") + endif () + set (SRCDIR "${CMAKE_CURRENT_SOURCE_DIR}/nc") + configure_file (gflags_nc.py.in "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nc.py" @ONLY) macro (add_nc_test name) add_test ( NAME nc_${name} COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/nc.py" ${name} ) endmacro () - add_nc_test (sanity) add_nc_test (swapped_args) add_nc_test (int_instead_of_bool) add_nc_test (bool_in_quotes) add_nc_test (define_string_with_0) -endif () - -# ---------------------------------------------------------------------------- -# test commands -add_test ( - NAME strip_flags - COMMAND /bin/bash "${CMAKE_CURRENT_SOURCE_DIR}/gflags_strip_flags_test.sh" - "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/strip_flags" -) - -add_test ( - NAME unittest - COMMAND /bin/bash "${CMAKE_CURRENT_SOURCE_DIR}/gflags_unittest.sh" - "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest" - "${CMAKE_CURRENT_SOURCE_DIR}" # <srcdir> - "${TEMPDIR}/unittest" # <tempdir> -) +endif () \ No newline at end of file diff --git a/test/flagfile.1 b/test/flagfile.1 new file mode 100644 index 0000000000000000000000000000000000000000..e0f921769cf029b30f9f7efc85267cf7adf07e56 --- /dev/null +++ b/test/flagfile.1 @@ -0,0 +1 @@ +--version \ No newline at end of file diff --git a/test/flagfile.2 b/test/flagfile.2 new file mode 100644 index 0000000000000000000000000000000000000000..864f8e8a138552a95e00b8e4d9af9268347993ed --- /dev/null +++ b/test/flagfile.2 @@ -0,0 +1,2 @@ +--foo=bar +--nounused_bool \ No newline at end of file diff --git a/test/flagfile.3 b/test/flagfile.3 new file mode 100644 index 0000000000000000000000000000000000000000..76d92bb1e0fc77fa547420dc49f7636f936e0122 --- /dev/null +++ b/test/flagfile.3 @@ -0,0 +1 @@ +--flagfile=flagfile.2 \ No newline at end of file diff --git a/test/gflags_unittest.cc b/test/gflags_unittest.cc index 4edf3df0dc0707e8cf0e0bfa93f654d901ff4ed7..a2a033a83d37f5ca30bcfe5f9aeaa3d31fa4c702 100644 --- a/test/gflags_unittest.cc +++ b/test/gflags_unittest.cc @@ -39,8 +39,8 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#ifdef HAVE_UNISTD_H -# include <unistd.h> +#if HAVE_UNISTD_H +# include <unistd.h> #endif // for unlink() #include <vector> #include <string> @@ -414,7 +414,7 @@ TEST(FlagFileTest, FilenamesOurfileFirst) { -1.0); } -#ifdef HAVE_FNMATCH_H // otherwise glob isn't supported +#if HAVE_FNMATCH_H // otherwise glob isn't supported TEST(FlagFileTest, FilenamesOurfileGlob) { FLAGS_test_string = "initial"; FLAGS_test_bool = false; @@ -1493,6 +1493,11 @@ TEST(FlagsValidator, FlagSaver) { } // unnamed namespace int main(int argc, char **argv) { + + // Run unit tests only if called without arguments, otherwise this program + // is used by an "external" usage test + const bool run_tests = (argc == 1); + // We need to call SetArgv before parsing flags, so our "test" argv will // win out over this executable's real argv. That makes running this // test with a real --help flag kinda annoying, unfortunately. @@ -1521,7 +1526,11 @@ int main(int argc, char **argv) { ParseCommandLineFlags(&argc, &argv, true); MakeTmpdir(&FLAGS_test_tmpdir); - const int exit_status = RUN_ALL_TESTS(); + int exit_status = 0; + if (run_tests) { + fprintf(stdout, "Running the unit tests now...\n\n"); fflush(stdout); + exit_status = RUN_ALL_TESTS(); + } else fprintf(stderr, "\n\nPASS\n"); ShutDownCommandLineFlags(); return exit_status; }