Skip to content

Commit 32c2654

Browse files
committed
Functional tests use GoogleTest now (#26)
This MR makes all LPF functional tests use GoogleTest. The main changes for CMake are: - reorganize CMake to use GTest CMake package for compilation, and gtest_add_tests clause to define Gtests; - remove all use of the run.sh script; - make explicit the list of tests and do not use file(GLOB, ...) clauses to look for all files in test directories, as recommended by CMakesource files: The main changes for source files are: - every single test file needed to be modified to not include internal Test.h but gtest.h, and to use the assert/equal clauses of GoogleTest - all death tests are slightly modified now to expect FAIL() after the expected fail condition - some modernization of C to C++ was needed for a number of files, incl. use of new/delete instead of malloc/free, or string manipulation instead of C char * manipulation A new Python wrapper script test_launcher.py is being employed now for more flexible checking of return codes, which is needed for death tests and normal tests alike. The expected return codes are still parsed from scanning the source files as before. For the complete history, see GitHub PR #26 . This MR also resolves bugs that have appeared on more modern cluster deployments compared to the previous main tag of LPF. This is hence a priority MR to base all further extensions and evolutions of LPF on. This MR was tested successfully for: - x86_64, CentOS Stream 9, GCC 11.5.0, MPICH 4.1.1 (caveat 2) - x86_64, CentOS Stream 9, GCC 11.5.0, OpenMPI 4.1.1 (caveats 1, 2, and 5) - x86_64, Fedora, GCC 9.3.1, MPICH 3.3.2 (caveat 2) - x86_64, Ubuntu 23.04, GCC 13.1.0, OpenMPI 4.1.6 (caveat 5) - x86_64, openEuler 20.03 LTS, GCC 7.3, MPICH 3.2.1 - x86_64, Ubuntu 22.04 LTS, GCC 11.4.0, MPICH 4.3.0b1 (caveat 3, transient) - ARM, Ubuntu 22.04 LTS, GCC 11.4.0, MPICH 4.3.0b1 (caveats 3 & 4) These are under different variations of calls to `bootstrap.sh` to test all the above-described changes. The following caveats apply: 1. with OpenMPI and default arguments to its `mpirun`, oversubscription to larger process counts may be limited, causing some tests to fail. Issue #37 has been raised to pass the appropriate flags to the OpenMPI `mpirun` during testing; 2. `make install` post-install checks fail (as they should) when: 1) tests are disabled, 2) ibverbs dev libraries are present, but 3) no IB card is present. On the machines with IB we tested on, all post-install checks succeed. 3. The hybrid backend on MPICH 4.3.0b1 does not pass the debug layer tests due to a segfault in MPICH's MPI_Abort. Issue #41 has been raised to track this. This error presently only is consistently reproducible on our cluster's ARM nodes and transient on our cluster's x86_64 nodes. They have not been observed outside our cluster. 4. With MPICH 4.3.0b1, we furthermore hit issues #43 and #44. Also these issues are only reproducible on ARM nodes. 5. With OpenMPI 4.1.6 and OpenMPI 4.1.1 on x86_64 we hit issue #45 6. At present, we have no CI flow that can run all build variations relevant to this MR. Issues #40 has been raised to (partially) address as well as to track this issue.
1 parent 67b75ab commit 32c2654

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+659
-442
lines changed

CMakeLists.txt

Lines changed: 131 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}")
5252
set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}")
5353
set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}")
5454
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
55-
"A high performance BSP communications library" )
55+
"A high performance BSP communications library" )
5656

5757
set(CPACK_SOURCE_GENERATOR "TGZ" )
5858
set(CPACK_SOURCE_IGNORE_FILES "/\\\\.git/" "/\\\\.svn/" "\\\\.swp$" "/site/" "/build/" "/pclint/" "/junit/" "/ideas/" )
5959
set(CPACK_SOURCE_PACKAGE_FILE_NAME
60-
"LPF-${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_PACKAGE}")
60+
"LPF-${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_PACKAGE}")
6161

6262
set(CPACK_GENERATOR "RPM")
6363
set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64")
@@ -183,10 +183,29 @@ endif()
183183

184184
#enable the hybrid engine
185185
if ( LIB_POSIX_THREADS AND LIB_MATH AND LIB_DL AND MPI_FOUND
186-
AND MPI_IS_THREAD_COMPAT AND MPI_IS_NOT_OPENMPI1
187-
AND ENABLE_IBVERBS )
188-
list(APPEND ENGINES "hybrid")
189-
set(HYBRID_ENGINE_ENABLED on)
186+
AND MPI_IS_THREAD_COMPAT AND MPI_IS_NOT_OPENMPI1 )
187+
if( ENABLE_IBVERBS )
188+
set(LPFLIB_HYBRID_MPI_ENGINE "ibverbs" CACHE STRING
189+
"Choice of MPI engine to use for inter-process communication")
190+
list(APPEND ENGINES "hybrid")
191+
set(HYBRID_ENGINE_ENABLED on)
192+
elseif( MPI_RMA )
193+
set(LPFLIB_HYBRID_MPI_ENGINE "mpirma" CACHE STRING
194+
"Choice of MPI engine to use for inter-process communication")
195+
list(APPEND ENGINES "hybrid")
196+
set(HYBRID_ENGINE_ENABLED on)
197+
elseif( LIB_MATH AND LIB_DL AND MPI_FOUND )
198+
set(LPFLIB_HYBRID_MPI_ENGINE "mpimsg" CACHE STRING
199+
"Choice of MPI engine to use for inter-process communication")
200+
list(APPEND ENGINES "hybrid")
201+
set(HYBRID_ENGINE_ENABLED on)
202+
endif()
203+
if( HYBRID_ENGINE_ENABLED )
204+
message( "Hybrid engine will be built using the ${LPFLIB_HYBRID_MPI_ENGINE} engine" )
205+
else()
206+
message( "No suitable inter-node communication engine found; "
207+
"hybrid engine will not be built" )
208+
endif()
190209
endif()
191210

192211
message( STATUS "The following engines will be built: ${ENGINES}")
@@ -209,6 +228,7 @@ endif()
209228

210229
# When system is not Linux, enable conditionally compiled blocks
211230
if (APPLE)
231+
message( WARNING "LPF compilation on OS X is not regularly tested" )
212232
add_definitions(-DLPF_ON_MACOS=1)
213233
endif()
214234

@@ -312,14 +332,43 @@ endfunction(target_compile_flags)
312332
# Source
313333
set(lpf_cflags)
314334
set(lpf_lib_link_flags)
315-
set(lpf_exe_link_flags "-rdynamic")
335+
set(lpf_exe_link_flags)
336+
337+
# Populate lpf_cflags, lpf_lib_link_flags, lpf_exe_link_flags according to
338+
# (enabled) engine requirements
339+
# - 0) PThreads engine needs nothing special
340+
# - 1) MPI-based engines:
341+
if ( LIB_MATH AND LIB_DL AND MPI_FOUND )
342+
# -fPIC and -rdynamic are necessary to ensure that symbols can be
343+
# looked up by dlsym which is the mechanism lpf_exec uses to broadcast the
344+
# function that should be executed
345+
set(rdyn_lflag "-rdynamic")
346+
if (APPLE)
347+
# OS X does not support -rdynamic
348+
set(rdyn_lflag "")
349+
endif ()
350+
351+
# include flags:
352+
set( mpi_include_flags )
353+
string( REPLACE ";" " -I" mpi_include_flags "${MPI_C_INCLUDE_PATH}" )
354+
set(lpf_cflags "${lpf_cflags} -I${mpi_include_flags} -fPIC")
355+
356+
# linker flags:
357+
set(lib_lflags "${MPI_C_LINK_FLAGS}") #Note: the core library is already linked with MPI_C_LIBRARIES.
358+
string(REPLACE ";" " " lib_lflags "${lib_lflags}") # So, no need to also link executables with it.
359+
set(lpf_lib_link_flags "${lpf_lib_link_flags} ${lib_lflags} ${rdyn_lflag}")
360+
361+
# executable linker flags:
362+
set(lpf_exe_link_flags "${lpf_exe_link_flags} ${rdyn_lflag}")
363+
endif ()
364+
# ...add requirements from other engines here...
316365

317366
# Collating all compile & link flags
318367
set(LPF_CORE_COMPILE_FLAGS "${lpf_cflags}" CACHE STRING "Compilation flags for all user code" )
319368
set(LPF_CORE_LIB_LINK_FLAGS "${lpf_lib_link_flags}" CACHE STRING "Flags to link user libraries" )
320369
set(LPF_CORE_EXE_LINK_FLAGS "${lpf_exe_link_flags}" CACHE STRING "Flags to link user executables" )
321370

322-
# Compiling LPF programmes in the build dir
371+
# Compiling LPF programs in the build dir
323372
function( target_link_exe_with_core target )
324373
set(engine "imp")
325374
if (ARGV1)
@@ -367,64 +416,84 @@ if (LPF_ENABLE_TESTS)
367416
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/junit)
368417
set(test_output "${CMAKE_BINARY_DIR}/junit")
369418

370-
set(MY_TEST_LAUNCHER ${CMAKE_BINARY_DIR}/test_launcher.py)
371-
configure_file( ${CMAKE_SOURCE_DIR}/test_launcher.py ${MY_TEST_LAUNCHER} @ONLY FILE_PERMISSIONS WORLD_EXECUTE OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ)
372-
if( NOT Python3_FOUND )
373-
find_package( Python3 REQUIRED)
374-
endif()
419+
find_package( Python3 REQUIRED COMPONENTS Interpreter)
420+
set(MY_TEST_LAUNCHER ${Python3_EXECUTABLE} ${CMAKE_BINARY_DIR}/test_launcher.py)
421+
configure_file( ${CMAKE_SOURCE_DIR}/test_launcher.py.in ${CMAKE_BINARY_DIR}/test_launcher.py @ONLY FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ)
375422

376423
# Macro for adding a new GoogleTest test
377424
function(add_gtest testName ENGINE debug testSource )
378-
if ("{$ENGINE}" STREQUAL "")
379-
message(FATAL_ERROR "engine cannot be empty, ever!")
380-
endif()
381-
add_executable(${testName} ${testSource} ${ARGN})
382-
target_compile_definitions(${testName} PUBLIC LPF_CORE_IMPL_ID=${ENGINE})
383-
target_compile_definitions(${testName} PUBLIC LPF_CORE_MPI_USES_${ENGINE})
384-
if (debug)
385-
target_include_directories( ${testName} BEFORE PRIVATE ${CMAKE_SOURCE_DIR}/include/debug )
386-
target_link_libraries(${testName} lpf_debug lpf_hl_debug GTest::gtest GTest::gtest_main)
387-
else(debug)
388-
target_link_libraries(${testName} GTest::gtest GTest::gtest_main)
389-
endif(debug)
390-
391-
392-
# Extract test-specific information from comments of tests
393-
file(READ ${testSource} fileContents)
394-
string(REGEX MATCH "Exit code: ([0-9]+)" _ ${fileContents})
395-
set(retCode ${CMAKE_MATCH_1})
396-
string(REGEX MATCH "pre P >= ([0-9]+)" _ ${fileContents})
397-
set(minProcs ${CMAKE_MATCH_1})
398-
string(REGEX MATCH "pre P <= ([0-9]+)" _ ${fileContents})
399-
set(maxProcs ${CMAKE_MATCH_1})
400-
string(REGEX MATCH "-probe ([0-9]+.[0-9]+)" _ ${fileContents})
401-
set(lpfProbeSecs ${CMAKE_MATCH_1})
402-
403-
target_link_exe_with_core(${testName} ${ENGINE})
404-
405-
406-
if ("${minProcs}" STREQUAL "")
407-
set(minProcs "1")
408-
endif()
409-
if ("${maxProcs}" STREQUAL "")
410-
set(maxProcs "5")
411-
endif()
412-
if ("${lpfProbeSecs}" STREQUAL "")
413-
set(lpfProbeSecs "0.0")
414-
endif()
415-
if ("${retCode}" STREQUAL "")
416-
set(retCode "0")
417-
endif()
418-
419-
# Most recent approach to Gtests, recommended!
420-
set_property(TARGET ${testName} PROPERTY TEST_LAUNCHER ${MY_TEST_LAUNCHER};-e;${ENGINE};-L;${CMAKE_BINARY_DIR}/lpfrun_build;-p;${minProcs};-P;${maxProcs};-t;${lpfProbeSecs};-R;${retCode})
421-
gtest_discover_tests(${testName}
422-
TEST_PREFIX ${ENGINE}_
423-
EXTRA_ARGS --gtest_output=xml:${test_output}/${ENGINE}_${testName}
424-
DISCOVERY_MODE POST_BUILD
425-
DISCOVERY_TIMEOUT 15
426-
)
425+
if ("{$ENGINE}" STREQUAL "")
426+
message(FATAL_ERROR "engine cannot be empty, ever!")
427+
endif()
428+
add_executable(${testName} ${testSource} ${ARGN})
429+
target_compile_definitions(${testName} PUBLIC LPF_CORE_IMPL_ID=${ENGINE})
430+
target_compile_definitions(${testName} PUBLIC LPF_CORE_MPI_USES_${ENGINE})
431+
if (debug)
432+
target_include_directories( ${testName} BEFORE PRIVATE ${CMAKE_SOURCE_DIR}/include/debug )
433+
target_link_libraries(${testName} lpf_debug lpf_hl_debug GTest::gtest GTest::gtest_main)
434+
else(debug)
435+
target_link_libraries(${testName} GTest::gtest GTest::gtest_main)
436+
endif(debug)
437+
438+
439+
# Extract test-specific information from comments of tests
440+
file(READ ${testSource} fileContents)
441+
string(REGEX MATCH "Exit code: ([0-9]+)" _ ${fileContents})
442+
set(retCode ${CMAKE_MATCH_1})
443+
string(REGEX MATCH "pre P >= ([0-9]+)" _ ${fileContents})
444+
set(minProcs ${CMAKE_MATCH_1})
445+
string(REGEX MATCH "pre P <= ([0-9]+)" _ ${fileContents})
446+
set(maxProcs ${CMAKE_MATCH_1})
447+
string(REGEX MATCH "-probe ([0-9]+.[0-9]+)" _ ${fileContents})
448+
set(lpfProbeSecs ${CMAKE_MATCH_1})
449+
450+
target_link_exe_with_core(${testName} ${ENGINE})
451+
452+
# The "\pre P <= max" comment in a test indicates the desired number of
453+
# maximum LPF processes. If the test does not define a desired number of
454+
# maximum LPF processes, it will be set to 5.
455+
#
456+
# The "\pre P >= min" comment in a test indicates the desired number of
457+
# minimum LPF processes. If the test does not define a desired minimum
458+
# number of LPF processes, it will be set to 1.
459+
#
460+
# Let 'processorCount' be the detected number of processors by the system.
461+
# If this number is smaller than the desider minimum and/or maximum number
462+
# of processes, it overwrites these
463+
#
464+
# Most tests only define a mininum number of desired processes, such as
465+
# "\pre P >= 1". In those cases, the test will execute for the range 1,..,5
466+
# (including)
467+
468+
if ("${minProcs}" STREQUAL "")
469+
set(minProcs "1")
470+
endif()
471+
if ("${maxProcs}" STREQUAL "")
472+
set(maxProcs "5")
473+
endif()
474+
# cap min with processorCount, if needed
475+
if ("${minProcs}" GREATER "${processorCount}")
476+
set(minProcs ${processorCount})
477+
endif()
478+
# cap max with processorCount, if needed
479+
if ("${maxProcs}" GREATER "${processorCount}")
480+
set(maxProcs ${processorCount})
481+
endif()
482+
if ("${lpfProbeSecs}" STREQUAL "")
483+
set(lpfProbeSecs "0.0")
484+
endif()
485+
if ("${retCode}" STREQUAL "")
486+
set(retCode "0")
487+
endif()
427488

489+
# Most recent approach to Gtests, recommended!
490+
set_property(TARGET ${testName} PROPERTY TEST_LAUNCHER ${MY_TEST_LAUNCHER};--engine;${ENGINE};--parallel_launcher;${CMAKE_BINARY_DIR}/lpfrun_build;--min_process_count;${minProcs};--max_process_count;${maxProcs};--lpf_probe_timer;${lpfProbeSecs};--expected_return_code;${retCode})
491+
gtest_discover_tests(${testName}
492+
TEST_PREFIX ${ENGINE}_
493+
EXTRA_ARGS --gtest_output=xml:${test_output}/${ENGINE}_${testName}
494+
DISCOVERY_MODE POST_BUILD
495+
DISCOVERY_TIMEOUT 15
496+
)
428497

429498
endfunction(add_gtest)
430499

README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ Optional MPI engine requires
3838
Optional for thread pinning by Pthreads and hybrid engines
3939
- hwloc > 1.11
4040

41+
Optional tests requires
42+
- GNU C++ compiler (C++17 compatible),
43+
- Python 3.
44+
4145
Optional (see --enable-doc) documentation requires
4246
- doxygen > 1.5.6,
4347
- graphviz,

bootstrap.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ EOF
192192

193193
--with-mpiexec=*)
194194
mpiexec="${arg#--with-mpiexec=}"
195-
mpi_cmake_flags="${mpi_cmake_flags} -DMPIEXEC=$mpiexec"
195+
mpi_cmake_flags="${mpi_cmake_flags} -DMPIEXEC=$mpiexec -DMPIEXEC_EXECUTABLE=$mpiexec"
196196
shift;
197197
;;
198198

@@ -288,8 +288,8 @@ ${CMAKE_EXE} -Wno-dev \
288288
-DLPF_HWLOC="${hwloc}" \
289289
$hwloc_found_flag \
290290
$mpi_cmake_flags \
291-
"$extra_flags" \
292-
"$perf_flags" \
291+
${extra_flags+"$extra_flags"} \
292+
${perf_flags+"$perf_flags"} \
293293
"$@" $srcdir \
294294
|| { echo FAIL "Failed to configure LPF; Please check your chosen configuration"; exit 1; }
295295

cmake/mpi.cmake

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,17 @@ try_run( IBVERBS_INIT_RUNS IBVERBS_INIT_COMPILES
169169
)
170170
endif()
171171

172-
set(ENABLE_IBVERBS FALSE)
173-
if (LIB_IBVERBS AND NOT IBVERBS_INIT_RUNS STREQUAL "FAILED_TO_RUN")
174-
set(ENABLE_IBVERBS TRUE)
172+
if (LPF_ENABLE_TESTS)
173+
# The Google Test integration requires that tests successfully compiled are
174+
# also runnable
175+
if (LIB_IBVERBS AND NOT IBVERBS_INIT_RUNS STREQUAL "FAILED_TO_RUN")
176+
set(ENABLE_IBVERBS TRUE)
177+
endif()
178+
else()
179+
# Without the aforementioned Google Test requirement, we can safely build
180+
# it and allow the user to deploy the built binaries on IB-enabled nodes.
181+
if (LIB_IBVERBS)
182+
set(ENABLE_IBVERBS TRUE)
183+
endif()
175184
endif()
176185

177-

doc/lpf_core.cfg.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -742,8 +742,8 @@ INPUT = @PROJECT_SOURCE_DIR@/include/lpf/core.h \
742742
@PROJECT_SOURCE_DIR@/include/bsp/bsp.h \
743743
@PROJECT_SOURCE_DIR@/include/lpf/hybrid.h \
744744
@PROJECT_SOURCE_DIR@/include/lpf/mpirpc-client.h \
745-
@PROJECT_SOURCE_DIR@/include/lpf/rpc-client.h
746-
745+
@PROJECT_SOURCE_DIR@/include/lpf/rpc-client.h \
746+
@PROJECT_SOURCE_DIR@/include/lpf/abort.h
747747

748748
# This tag can be used to specify the character encoding of the source files
749749
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

0 commit comments

Comments
 (0)