1##============================================================================
2##  Copyright (c) Kitware, Inc.
3##  All rights reserved.
4##  See LICENSE.txt for details.
5##
6##  This software is distributed WITHOUT ANY WARRANTY; without even
7##  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8##  PURPOSE.  See the above copyright notice for more information.
9##============================================================================
10
11include(VTKmWrappers)
12
13function(vtkm_create_test_executable
14  prog_name
15  sources
16  libraries
17  defines
18  is_mpi_test
19  use_mpi
20  enable_all_backends
21  use_job_pool)
22
23  vtkm_diy_use_mpi_push()
24
25  set(prog ${prog_name})
26
27  # for MPI tests, suffix test name and add MPI_Init/MPI_Finalize calls.
28  if (is_mpi_test)
29    set(extraArgs EXTRA_INCLUDE "vtkm/thirdparty/diy/environment.h")
30
31    if (use_mpi)
32      vtkm_diy_use_mpi(ON)
33      set(prog "${prog}_mpi")
34    else()
35      vtkm_diy_use_mpi(OFF)
36      set(prog "${prog}_nompi")
37    endif()
38  else()
39    set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN "")
40  endif()
41
42  #the creation of the test source list needs to occur before the labeling as
43  #cuda. This is so that we get the correctly named entry points generated
44  create_test_sourcelist(test_sources ${prog}.cxx ${sources} ${extraArgs})
45
46  add_executable(${prog} ${prog}.cxx ${sources})
47  vtkm_add_drop_unused_function_flags(${prog})
48  target_compile_definitions(${prog} PRIVATE ${defines})
49
50  #determine if we have a device that requires a separate compiler enabled
51  set(device_lang_enabled FALSE)
52  if( (TARGET vtkm::cuda) OR (TARGET vtkm::kokkos_cuda) OR (TARGET vtkm::kokkos_hip))
53    set(device_lang_enabled TRUE)
54  endif()
55
56  #if all backends are enabled, we can use the device compiler to handle all possible backends.
57  set(device_sources)
58  if(device_lang_enabled AND enable_all_backends)
59    set(device_sources ${sources})
60  endif()
61  vtkm_add_target_information(${prog} DEVICE_SOURCES ${device_sources})
62
63  if(NOT VTKm_USE_DEFAULT_SYMBOL_VISIBILITY)
64    set_property(TARGET ${prog} PROPERTY CUDA_VISIBILITY_PRESET "hidden")
65    set_property(TARGET ${prog} PROPERTY CXX_VISIBILITY_PRESET "hidden")
66  endif()
67  set_property(TARGET ${prog} PROPERTY ARCHIVE_OUTPUT_DIRECTORY ${VTKm_LIBRARY_OUTPUT_PATH})
68  set_property(TARGET ${prog} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${VTKm_LIBRARY_OUTPUT_PATH})
69  set_property(TARGET ${prog} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${VTKm_EXECUTABLE_OUTPUT_PATH})
70
71  target_link_libraries(${prog} PRIVATE vtkm_cont_testing ${libraries})
72
73  if(use_job_pool)
74    vtkm_setup_job_pool()
75    set_property(TARGET ${prog} PROPERTY JOB_POOL_COMPILE vtkm_pool)
76  endif()
77
78  vtkm_diy_use_mpi_pop()
79endfunction()
80
81#-----------------------------------------------------------------------------
82# Declare unit tests, which should be in the same directory as a kit
83# (package, module, whatever you call it).  Usage:
84#
85# vtkm_unit_tests(
86#   NAME
87#   SOURCES <source_list>
88#   LIBRARIES <dependent_library_list>
89#   DEFINES <target_compile_definitions>
90#   TEST_ARGS <argument_list>
91#   MPI
92#   ALL_BACKENDS
93#   USE_VTKM_JOB_POOL
94#   <options>
95#   )
96#
97# [LIBRARIES] : extra libraries that this set of tests need to link too
98#
99# [DEFINES]   : extra defines that need to be set for all unit test sources
100#
101# [LABEL]     : CTest Label to associate to this set of tests
102#
103# [TEST_ARGS] : arguments that should be passed on the command line to the
104#               test executable
105#
106# [MPI]       : when specified, the tests should be run in parallel if
107#               MPI is enabled. The tests should also be able to build and run
108#               When MPI is not available, i.e., they should not make explicit
109#               use of MPI and instead completely rely on DIY.
110# [ALL_BACKENDS] : when specified, the tests would test against all enabled
111#                  backends. Otherwise we expect the tests to manage the
112#                  backends at runtime.
113#
114function(vtkm_unit_tests)
115  if (NOT VTKm_ENABLE_TESTING)
116    return()
117  endif()
118
119  set(options)
120  set(global_options ${options} USE_VTKM_JOB_POOL MPI ALL_BACKENDS)
121  set(oneValueArgs BACKEND NAME LABEL)
122  set(multiValueArgs SOURCES LIBRARIES DEFINES TEST_ARGS)
123  cmake_parse_arguments(VTKm_UT
124    "${global_options}" "${oneValueArgs}" "${multiValueArgs}"
125    ${ARGN}
126    )
127  vtkm_parse_test_options(VTKm_UT_SOURCES "${options}" ${VTKm_UT_SOURCES})
128
129  set(per_device_command_line_arguments "NONE")
130  set(per_device_suffix "")
131  set(per_device_timeout 180)
132  set(per_device_serial FALSE)
133
134  set(enable_all_backends ${VTKm_UT_ALL_BACKENDS})
135  if(enable_all_backends)
136    set(per_device_command_line_arguments --device=serial)
137    set(per_device_suffix "SERIAL")
138    if (VTKm_ENABLE_CUDA)
139      list(APPEND per_device_command_line_arguments --device=cuda)
140      list(APPEND per_device_suffix "CUDA")
141      #CUDA tests generally require more time because of kernel generation.
142      list(APPEND per_device_timeout 1500)
143      list(APPEND per_device_serial FALSE)
144    endif()
145    if (VTKm_ENABLE_TBB)
146      list(APPEND per_device_command_line_arguments --device=tbb)
147      list(APPEND per_device_suffix "TBB")
148      list(APPEND per_device_timeout 180)
149      list(APPEND per_device_serial FALSE)
150    endif()
151    if (VTKm_ENABLE_OPENMP)
152      list(APPEND per_device_command_line_arguments --device=openmp)
153      list(APPEND per_device_suffix "OPENMP")
154      list(APPEND per_device_timeout 180)
155      #We need to have all OpenMP tests run serially as they
156      #will uses all the system cores, and we will cause a N*N thread
157      #explosion which causes the tests to run slower than when run
158      #serially
159      list(APPEND per_device_serial TRUE)
160    endif()
161    if (VTKm_ENABLE_KOKKOS)
162      list(APPEND per_device_command_line_arguments --device=kokkos)
163      list(APPEND per_device_suffix "KOKKOS")
164      #may require more time because of kernel generation.
165      list(APPEND per_device_timeout 1500)
166      list(APPEND per_device_serial FALSE)
167    endif()
168  endif()
169
170  set(test_prog)
171  if(VTKm_UT_NAME)
172    set(test_prog "${VTKm_UT_NAME}")
173  else()
174    vtkm_get_kit_name(kit)
175    set(test_prog "UnitTests_${kit}")
176  endif()
177
178  # For Testing Purposes, we will set the default logging level to INFO
179  list(APPEND vtkm_default_test_log_level "-v" "INFO")
180
181  # Add the path to the data directory so tests can find and use data files for testing
182  list(APPEND VTKm_UT_TEST_ARGS "--data-dir=${VTKm_SOURCE_DIR}/data/data")
183
184  # Add the path to the location where regression test images are to be stored
185  list(APPEND VTKm_UT_TEST_ARGS "--baseline-dir=${VTKm_SOURCE_DIR}/data/baseline")
186
187  # Add the path to the location where generated regression test images should be written
188  list(APPEND VTKm_UT_TEST_ARGS "--write-dir=${VTKm_BINARY_DIR}")
189
190  if(VTKm_UT_MPI)
191    if (VTKm_ENABLE_MPI)
192      vtkm_create_test_executable(
193        ${test_prog}
194        "${VTKm_UT_SOURCES}"
195        "${VTKm_UT_LIBRARIES}"
196        "${VTKm_UT_DEFINES}"
197        ON   # is_mpi_test
198        ON   # use_mpi
199        ${enable_all_backends}
200        ${VTKm_UT_USE_VTKM_JOB_POOL})
201    endif()
202    if ((NOT VTKm_ENABLE_MPI) OR VTKm_ENABLE_DIY_NOMPI)
203      vtkm_create_test_executable(
204        ${test_prog}
205        "${VTKm_UT_SOURCES}"
206        "${VTKm_UT_LIBRARIES}"
207        "${VTKm_UT_DEFINES}"
208        ON   # is_mpi_test
209        OFF  # use_mpi
210        ${enable_all_backends}
211        ${VTKm_UT_USE_VTKM_JOB_POOL})
212    endif()
213  else()
214    vtkm_create_test_executable(
215      ${test_prog}
216      "${VTKm_UT_SOURCES}"
217      "${VTKm_UT_LIBRARIES}"
218      "${VTKm_UT_DEFINES}"
219      OFF   # is_mpi_test
220      OFF   # use_mpi
221      ${enable_all_backends}
222      ${VTKm_UT_USE_VTKM_JOB_POOL})
223  endif()
224
225  list(LENGTH per_device_command_line_arguments number_of_devices)
226  foreach(index RANGE ${number_of_devices})
227    if(index EQUAL number_of_devices)
228      #RANGE is inclusive on both sides, and we want it to be
229      #exclusive on the end ( e.g. for(i=0; i < n; ++i))
230      break()
231    endif()
232    if(per_device_command_line_arguments STREQUAL "NONE")
233      set(device_command_line_argument)
234      set(upper_backend ${per_device_suffix})
235      set(timeout       ${per_device_timeout})
236      set(run_serial    ${per_device_serial})
237    else()
238      list(GET per_device_command_line_arguments ${index} device_command_line_argument)
239      list(GET per_device_suffix  ${index}  upper_backend)
240      list(GET per_device_timeout ${index}  timeout)
241      list(GET per_device_serial  ${index}  run_serial)
242    endif()
243
244    foreach (test ${VTKm_UT_SOURCES})
245      get_filename_component(tname ${test} NAME_WE)
246      if(VTKm_UT_MPI)
247        if (VTKm_ENABLE_MPI)
248          add_test(NAME ${tname}${upper_backend}_mpi
249            COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 3 ${MPIEXEC_PREFLAGS}
250                    $<TARGET_FILE:${test_prog}_mpi> ${tname} ${device_command_line_argument}
251                    ${vtkm_default_test_log_level} ${VTKm_UT_TEST_ARGS} ${MPIEXEC_POSTFLAGS}
252            )
253          set_tests_properties("${tname}${upper_backend}_mpi" PROPERTIES
254            LABELS "${upper_backend};${VTKm_UT_LABEL}"
255            TIMEOUT ${timeout}
256            RUN_SERIAL ${run_serial}
257            FAIL_REGULAR_EXPRESSION "runtime error")
258        endif() # VTKm_ENABLE_MPI
259        if ((NOT VTKm_ENABLE_MPI) OR VTKm_ENABLE_DIY_NOMPI)
260          add_test(NAME ${tname}${upper_backend}_nompi
261            COMMAND ${test_prog}_nompi ${tname} ${device_command_line_argument}
262                    ${vtkm_default_test_log_level} ${VTKm_UT_TEST_ARGS}
263            )
264          set_tests_properties("${tname}${upper_backend}_nompi" PROPERTIES
265            LABELS "${upper_backend};${VTKm_UT_LABEL}"
266            TIMEOUT ${timeout}
267            RUN_SERIAL ${run_serial}
268            FAIL_REGULAR_EXPRESSION "runtime error")
269
270        endif() # VTKm_ENABLE_DIY_NOMPI
271      else() # VTKm_UT_MPI
272        add_test(NAME ${tname}${upper_backend}
273          COMMAND ${test_prog} ${tname} ${device_command_line_argument}
274                  ${vtkm_default_test_log_level} ${VTKm_UT_TEST_ARGS}
275          )
276        set_tests_properties("${tname}${upper_backend}" PROPERTIES
277            LABELS "${upper_backend};${VTKm_UT_LABEL}"
278            TIMEOUT ${timeout}
279            RUN_SERIAL ${run_serial}
280            FAIL_REGULAR_EXPRESSION "runtime error")
281      endif() # VTKm_UT_MPI
282    endforeach()
283  endforeach()
284
285endfunction(vtkm_unit_tests)
286
287# -----------------------------------------------------------------------------
288# vtkm_parse_test_options(varname options)
289#   INTERNAL: Parse options specified for individual tests.
290#
291#   Parses the arguments to separate out options specified after the test name
292#   separated by a comma e.g.
293#
294#   TestName,Option1,Option2
295#
296#   For every option in options, this will set _TestName_Option1,
297#   _TestName_Option2, etc in the parent scope.
298#
299function(vtkm_parse_test_options varname options)
300  set(names)
301  foreach(arg IN LISTS ARGN)
302    set(test_name ${arg})
303    set(test_options)
304    if(test_name AND "x${test_name}" MATCHES "^x([^,]*),(.*)$")
305      set(test_name "${CMAKE_MATCH_1}")
306      string(REPLACE "," ";" test_options "${CMAKE_MATCH_2}")
307    endif()
308    foreach(opt IN LISTS test_options)
309      list(FIND options "${opt}" index)
310      if(index EQUAL -1)
311        message(WARNING "Unknown option '${opt}' specified for test '${test_name}'")
312      else()
313        set(_${test_name}_${opt} TRUE PARENT_SCOPE)
314      endif()
315    endforeach()
316    list(APPEND names ${test_name})
317  endforeach()
318  set(${varname} ${names} PARENT_SCOPE)
319endfunction()
320