diff --git a/cmake/modules/RootNewMacros.cmake b/cmake/modules/RootNewMacros.cmake index dc96a5d693ac4..0544aefaec84f 100644 --- a/cmake/modules/RootNewMacros.cmake +++ b/cmake/modules/RootNewMacros.cmake @@ -1078,6 +1078,67 @@ function(ROOT_ADD_TEST test) endfunction() +#---------------------------------------------------------------------------- +# ROOT_PATH_TO_STRING( path PATH_SEPARATOR_REPLACEMENT replacement ) +# +# Mangle the path to a string. +#---------------------------------------------------------------------------- +function(ROOT_PATH_TO_STRING resultvar path) + # FIXME: Copied and modified from ROOTTEST_TARGETNAME_FROM_FILE. We should find a common place for that code. + # FIXME: ROOTTEST_TARGETNAME_FROM_FILE could be replaced by just a call to string(MAKE_C_IDENTIFIER)... + CMAKE_PARSE_ARGUMENTS(ARG "" "" "PATH_SEPARATOR_REPLACEMENT" ${ARGN}) + + set(sep_replacement "") + if (ARG_PATH_SEPARATOR_REPLACEMENT) + set(sep_replacement ${ARG_PATH_SEPARATOR_REPLACEMENT}) + endif() + + get_filename_component(realfp ${path} ABSOLUTE) + get_filename_component(filename_we ${path} NAME_WE) + + string(REPLACE "${CMAKE_SOURCE_DIR}" "" relativepath ${realfp}) + string(REPLACE "${path}" "" relativepath ${relativepath}) + + string(MAKE_C_IDENTIFIER ${relativepath}${filename_we} mangledname) + string(REPLACE "_" "${sep_replacement}" mangledname ${mangledname}) + + set(${resultvar} "${mangledname}" PARENT_SCOPE) +endfunction(ROOT_PATH_TO_STRING) + +#---------------------------------------------------------------------------- +# ROOT_ADD_UNITTEST_SUBDIRECTORY( LIBRARIES) +#---------------------------------------------------------------------------- +function(ROOT_ADD_UNITTEST_SUBDIRECTORY subdir) + ROOT_GLOB_FILES(test_files ${CMAKE_CURRENT_SOURCE_DIR}/${subdir}/*.cxx) + # Get the component from the path. Eg. core to form coreTests test suite name. + ROOT_PATH_TO_STRING(test_name ${CMAKE_CURRENT_SOURCE_DIR}/Tests/) + ROOT_ADD_GTEST(${test_name} ${test_files} ${ARGN}) + # Override the target output folder for to put the binaries in the ${subdir}. + set_property(TARGET ${test_name} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${subdir}/) +endfunction() + +#---------------------------------------------------------------------------- +# function ROOT_ADD_GTEST( source1 source2... LIBRARIES) +# +function(ROOT_ADD_GTEST test_suite) + CMAKE_PARSE_ARGUMENTS(ARG "" "" "LIBRARIES" ${ARGN}) + include_directories(${CMAKE_CURRENT_BINARY_DIR} ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR}) + + set(source_files ${ARG_UNPARSED_ARGUMENTS}) + # Note we cannot use ROOT_EXECUTABLE without user-specified set of LIBRARIES to link with. + # The test suites should choose this in their specific CMakeLists.txt file. + # FIXME: For better coherence we could restrict the libraries the test suite could link + # against. For example, tests in Core should link only against libCore. This could be tricky + # to implement because some ROOT components create more than one library. + ROOT_EXECUTABLE(${test_suite} ${source_files} LIBRARIES ${ARG_LIBRARIES}) + set_property(TARGET ${test_suite} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(${test_suite} gtest gtest_main gmock gmock_main) + + ROOT_PATH_TO_STRING(mangled_name ${test_suite} PATH_SEPARATOR_REPLACEMENT "-") + ROOT_ADD_TEST(gtest${mangled_name} COMMAND ${test_suite}) +endfunction() + + #---------------------------------------------------------------------------- # ROOT_ADD_TEST_SUBDIRECTORY( ) #---------------------------------------------------------------------------- diff --git a/cmake/modules/SearchInstalledSoftware.cmake b/cmake/modules/SearchInstalledSoftware.cmake index 1fdde70fbbe0e..0461a7628fe36 100644 --- a/cmake/modules/SearchInstalledSoftware.cmake +++ b/cmake/modules/SearchInstalledSoftware.cmake @@ -1371,6 +1371,70 @@ if(tmva AND imt) find_package(BLAS) endif() + +#---Download googletest-------------------------------------------------------------- +if (testing) + # FIXME: Remove our version of gtest in roottest. We can reuse this one. + # Add gtest + # http://stackoverflow.com/questions/9689183/cmake-googletest + + set(_byproduct_binary_dir + ${CMAKE_CURRENT_BINARY_DIR}/googletest-prefix/src/googletest-build/googlemock/) + set(_byproducts + ${_byproduct_binary_dir}/gtest/libgtest.a + ${_byproduct_binary_dir}/gtest/libgtest_main.a + ${_byproduct_binary_dir}/libgmock.a + ${_byproduct_binary_dir}/libgmock_main.a + ) + ExternalProject_Add( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.8.0 + # TIMEOUT 10 + # # Force separate output paths for debug and release builds to allow easy + # # identification of correct lib in subsequent TARGET_LINK_LIBRARIES commands + # CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs + # -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs + # -Dgtest_force_shared_crt=ON + # Disable install step + INSTALL_COMMAND "" + BUILD_BYPRODUCTS ${_byproducts} + # Wrap download, configure and build steps in a script to log output + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON) + + # Specify include dirs for gtest and gmock + ExternalProject_Get_Property(googletest source_dir) + set(GTEST_INCLUDE_DIR ${source_dir}/googletest/include) + set(GMOCK_INCLUDE_DIR ${source_dir}/googlemock/include) + + # Libraries + ExternalProject_Get_Property(googletest binary_dir) + set(_G_LIBRARY_PATH ${binary_dir}/googlemock/) + + # gtest + add_library(gtest IMPORTED STATIC GLOBAL) + set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${_G_LIBRARY_PATH}/gtest/libgtest.a) + add_dependencies(gtest googletest) + + # gtest_main + add_library(gtest_main IMPORTED STATIC GLOBAL) + set_property(TARGET gtest_main PROPERTY IMPORTED_LOCATION ${_G_LIBRARY_PATH}/gtest/libgtest_main.a) + add_dependencies(gtest_main googletest) + + # gmock + add_library(gmock IMPORTED STATIC GLOBAL) + set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${_G_LIBRARY_PATH}/libgmock.a) + add_dependencies(gmock googletest) + + # gmock_main + add_library(gmock_main IMPORTED STATIC GLOBAL) + set_property(TARGET gmock_main PROPERTY IMPORTED_LOCATION ${_G_LIBRARY_PATH}/libgmock_main.a) + add_dependencies(gmock_main googletest) + +endif() + #---Report non implemented options--------------------------------------------------- foreach(opt afs glite sapdb srp) if(${opt}) diff --git a/core/base/CMakeLists.txt b/core/base/CMakeLists.txt index 4a10dda8256d2..55a31dfb36138 100644 --- a/core/base/CMakeLists.txt +++ b/core/base/CMakeLists.txt @@ -2,6 +2,13 @@ # CMakeLists.txt file for building ROOT core/base package ############################################################################ +if(testing) + # FIXME: The tests in core should require only libCore. OTOH, TQObjectTests uses the interpreter to register the class. + # This means that if we run make CoreBaseTests the executable wouldn't be runnable because it requires libCling and + # onepcm targets to be built. + ROOT_ADD_UNITTEST_SUBDIRECTORY(test LIBRARIES Core Cling RIO) +endif() + ROOT_GLOB_HEADERS(Base_dict_headers ${CMAKE_CURRENT_SOURCE_DIR}/inc/T*.h ${CMAKE_CURRENT_SOURCE_DIR}/inc/GuiTypes.h ${CMAKE_CURRENT_SOURCE_DIR}/inc/KeySymbols.h diff --git a/core/base/test/TNamedTests.cxx b/core/base/test/TNamedTests.cxx new file mode 100644 index 0000000000000..3bc800beed3e2 --- /dev/null +++ b/core/base/test/TNamedTests.cxx @@ -0,0 +1,11 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "TNamed.h" + +TEST(TNamed, Sanity) +{ + TNamed n("Name", "Title"); + EXPECT_STREQ("Name", n.GetName()); + EXPECT_STREQ("Title", n.GetTitle()); +} diff --git a/core/base/test/TQObjectTests.cxx b/core/base/test/TQObjectTests.cxx new file mode 100644 index 0000000000000..54cd7f7efa335 --- /dev/null +++ b/core/base/test/TQObjectTests.cxx @@ -0,0 +1,47 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "Rtypes.h" +#include "TQObject.h" +#include "TQConnection.h" + +#include "RQ_OBJECT.h" + +#define Stringify(s) Stringifyx(s) +#define Stringifyx(s) #s + +// The interpreter needs to know about RQ_OBJECTTester and using this trick avoids moving this non-reusable class into +// its own header file. +#define DICT_CLASS \ + class RQ_OBJECTTester : public TQObject { \ + /* This will expand, adding signal/slot support to this class */ \ + RQ_OBJECT("RQ_OBJECTTester"); \ + Int_t fValue = 0; \ + \ + public: \ + void SetValue(Int_t value) \ + { \ + /* to prevent infinite looping in the case of cyclic connections */ \ + if (value != fValue) { \ + fValue = value; \ + Emit("SetValue(Int_t)", fValue); \ + } \ + } \ + void PrintValue() const { printf("value=%d\n", fValue); } \ + Int_t GetValue() const { return fValue; } \ + }; + +DICT_CLASS; + +TEST(TQObject, Emit) +{ + gInterpreter->ProcessLine(Stringify(DICT_CLASS)); + RQ_OBJECTTester a; + RQ_OBJECTTester b; + a.Connect("SetValue(Int_t)", "RQ_OBJECTTester", &b, "SetValue(Int_t)"); + + EXPECT_EQ(0, b.GetValue()); + + a.SetValue(1); + EXPECT_EQ(1, b.GetValue()); +}