diff --git a/cmake/GoogleTest.cmake b/cmake/GoogleTest.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..2c9d47fe642f20030529e95cade71e0e735cf641
--- /dev/null
+++ b/cmake/GoogleTest.cmake
@@ -0,0 +1,11 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+
+# This module is already included in new versions of CMake
+if(CMAKE_VERSION VERSION_LESS 3.10)
+  include(${CMAKE_CURRENT_LIST_DIR}/upstream/GoogleTest.cmake)
+else()
+  include(GoogleTest)
+endif()
diff --git a/cmake/upstream/GoogleTest.cmake b/cmake/upstream/GoogleTest.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..c95c481983ce7731e02f461dc93aebeda0d256a1
--- /dev/null
+++ b/cmake/upstream/GoogleTest.cmake
@@ -0,0 +1,203 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+GoogleTest
+----------
+
+This module defines functions to help use the Google Test infrastructure.
+
+.. command:: gtest_add_tests
+
+  Automatically add tests with CTest by scanning source code for Google Test
+  macros::
+
+    gtest_add_tests(TARGET target
+                    [SOURCES src1...]
+                    [EXTRA_ARGS arg1...]
+                    [WORKING_DIRECTORY dir]
+                    [TEST_PREFIX prefix]
+                    [TEST_SUFFIX suffix]
+                    [SKIP_DEPENDENCY]
+                    [TEST_LIST outVar]
+    )
+
+  ``TARGET target``
+    This must be a known CMake target. CMake will substitute the location of
+    the built executable when running the test.
+
+  ``SOURCES src1...``
+    When provided, only the listed files will be scanned for test cases. If
+    this option is not given, the :prop_tgt:`SOURCES` property of the
+    specified ``target`` will be used to obtain the list of sources.
+
+  ``EXTRA_ARGS arg1...``
+    Any extra arguments to pass on the command line to each test case.
+
+  ``WORKING_DIRECTORY dir``
+    Specifies the directory in which to run the discovered test cases. If this
+    option is not provided, the current binary directory is used.
+
+  ``TEST_PREFIX prefix``
+    Allows the specified ``prefix`` to be prepended to the name of each
+    discovered test case. This can be useful when the same source files are
+    being used in multiple calls to ``gtest_add_test()`` but with different
+    ``EXTRA_ARGS``.
+
+  ``TEST_SUFFIX suffix``
+    Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
+    every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` can be
+    specified.
+
+  ``SKIP_DEPENDENCY``
+    Normally, the function creates a dependency which will cause CMake to be
+    re-run if any of the sources being scanned are changed. This is to ensure
+    that the list of discovered tests is updated. If this behavior is not
+    desired (as may be the case while actually writing the test cases), this
+    option can be used to prevent the dependency from being added.
+
+  ``TEST_LIST outVar``
+    The variable named by ``outVar`` will be populated in the calling scope
+    with the list of discovered test cases. This allows the caller to do things
+    like manipulate test properties of the discovered tests.
+
+  .. code-block:: cmake
+
+    include(GoogleTest)
+    add_executable(FooTest FooUnitTest.cxx)
+    gtest_add_tests(TARGET      FooTest
+                    TEST_SUFFIX .noArgs
+                    TEST_LIST   noArgsTests
+    )
+    gtest_add_tests(TARGET      FooTest
+                    EXTRA_ARGS  --someArg someValue
+                    TEST_SUFFIX .withArgs
+                    TEST_LIST   withArgsTests
+    )
+    set_tests_properties(${noArgsTests}   PROPERTIES TIMEOUT 10)
+    set_tests_properties(${withArgsTests} PROPERTIES TIMEOUT 20)
+
+  For backward compatibility reasons, the following form is also supported::
+
+    gtest_add_tests(exe args files...)
+
+  ``exe``
+    The path to the test executable or the name of a CMake target.
+  ``args``
+    A ;-list of extra arguments to be passed to executable.  The entire
+    list must be passed as a single argument.  Enclose it in quotes,
+    or pass ``""`` for no arguments.
+  ``files...``
+    A list of source files to search for tests and test fixtures.
+    Alternatively, use ``AUTO`` to specify that ``exe`` is the name
+    of a CMake executable target whose sources should be scanned.
+
+  .. code-block:: cmake
+
+    include(GoogleTest)
+    set(FooTestArgs --foo 1 --bar 2)
+    add_executable(FooTest FooUnitTest.cxx)
+    gtest_add_tests(FooTest "${FooTestArgs}" AUTO)
+
+#]=======================================================================]
+
+function(gtest_add_tests)
+
+  if (ARGC LESS 1)
+    message(FATAL_ERROR "No arguments supplied to gtest_add_tests()")
+  endif()
+
+  set(options
+      SKIP_DEPENDENCY
+  )
+  set(oneValueArgs
+      TARGET
+      WORKING_DIRECTORY
+      TEST_PREFIX
+      TEST_SUFFIX
+      TEST_LIST
+  )
+  set(multiValueArgs
+      SOURCES
+      EXTRA_ARGS
+  )
+  set(allKeywords ${options} ${oneValueArgs} ${multiValueArgs})
+
+  unset(sources)
+  if("${ARGV0}" IN_LIST allKeywords)
+    cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+    set(autoAddSources YES)
+  else()
+    # Non-keyword syntax, convert to keyword form
+    if (ARGC LESS 3)
+      message(FATAL_ERROR "gtest_add_tests() without keyword options requires at least 3 arguments")
+    endif()
+    set(ARGS_TARGET     "${ARGV0}")
+    set(ARGS_EXTRA_ARGS "${ARGV1}")
+    if(NOT "${ARGV2}" STREQUAL "AUTO")
+      set(ARGS_SOURCES "${ARGV}")
+      list(REMOVE_AT ARGS_SOURCES 0 1)
+    endif()
+  endif()
+
+  # The non-keyword syntax allows the first argument to be an arbitrary
+  # executable rather than a target if source files are also provided. In all
+  # other cases, both forms require a target.
+  if(NOT TARGET "${ARGS_TARGET}" AND NOT ARGS_SOURCES)
+    message(FATAL_ERROR "${ARGS_TARGET} does not define an existing CMake target")
+  endif()
+  if(NOT ARGS_WORKING_DIRECTORY)
+    unset(workDir)
+  else()
+    set(workDir WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}")
+  endif()
+
+  if(NOT ARGS_SOURCES)
+    get_property(ARGS_SOURCES TARGET ${ARGS_TARGET} PROPERTY SOURCES)
+  endif()
+
+  unset(testList)
+
+  set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*")
+  set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)")
+
+  foreach(source IN LISTS ARGS_SOURCES)
+    if(NOT ARGS_SKIP_DEPENDENCY)
+      set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${source})
+    endif()
+    file(READ "${source}" contents)
+    string(REGEX MATCHALL "${gtest_test_type_regex} *\\(([A-Za-z_0-9 ,]+)\\)" found_tests ${contents})
+    foreach(hit ${found_tests})
+      string(REGEX MATCH "${gtest_test_type_regex}" test_type ${hit})
+
+      # Parameterized tests have a different signature for the filter
+      if("x${test_type}" STREQUAL "xTEST_P")
+        string(REGEX REPLACE ${gtest_case_name_regex}  "*/\\1.\\2/*" test_name ${hit})
+      elseif("x${test_type}" STREQUAL "xTEST_F" OR "x${test_type}" STREQUAL "xTEST")
+        string(REGEX REPLACE ${gtest_case_name_regex} "\\1.\\2" test_name ${hit})
+      elseif("x${test_type}" STREQUAL "xTYPED_TEST")
+        string(REGEX REPLACE ${gtest_case_name_regex} "\\1/*.\\2" test_name ${hit})
+      else()
+        message(WARNING "Could not parse GTest ${hit} for adding to CTest.")
+        continue()
+      endif()
+
+      # Ignore the test case if it's disabled in GTest
+      set(gtest_case_name_member "${CMAKE_MATCH_2}")
+      if(gtest_case_name_member MATCHES "^DISABLED_")
+        continue()
+      endif()
+
+      add_test(NAME ${ARGS_TEST_PREFIX}${test_name}${ARGS_TEST_SUFFIX}
+               ${workDir}
+               COMMAND ${ARGS_TARGET} --gtest_filter=${test_name} ${ARGS_EXTRA_ARGS}
+      )
+      list(APPEND testList ${ARGS_TEST_PREFIX}${test_name}${ARGS_TEST_SUFFIX})
+    endforeach()
+  endforeach()
+
+  if(ARGS_TEST_LIST)
+    set(${ARGS_TEST_LIST} ${testList} PARENT_SCOPE)
+  endif()
+
+endfunction()
diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt
index 80385077f8485cf5b0d9ba29e44f1db93c8a8669..7ed0bbbc8764e841205b5b9ef0e33903b47fd433 100644
--- a/testing/CMakeLists.txt
+++ b/testing/CMakeLists.txt
@@ -3,4 +3,6 @@
 # accompanying file Copyright.txt for details.
 #------------------------------------------------------------------------------#
 
+include(GoogleTest)
+
 add_subdirectory(adios2)
diff --git a/testing/adios2/engine/adios1/CMakeLists.txt b/testing/adios2/engine/adios1/CMakeLists.txt
index e71473103c8005ed6fff2b4c2c4963db67662eec..35fd48db97582a48a8c1b3bfc456fc3aeb151c16 100644
--- a/testing/adios2/engine/adios1/CMakeLists.txt
+++ b/testing/adios2/engine/adios1/CMakeLists.txt
@@ -10,5 +10,5 @@ if(NOT ADIOS_USE_MPI)
   add_executable(TestADIOS1WriteRead TestADIOS1WriteRead.cpp)
   target_link_libraries(TestADIOS1WriteRead adios2 gtest adios1::adios)
 
-  add_test(NAME adios2::engine::adios1::write_read COMMAND TestADIOS1WriteRead)
+  gtest_add_tests(TARGET TestADIOS1WriteRead)
 endif()
diff --git a/testing/adios2/engine/bp/CMakeLists.txt b/testing/adios2/engine/bp/CMakeLists.txt
index 87eb336a2faf5cf09dd5fcc4001421f6a5ecb4c3..270f0171ca25554ed044619b4847e2fa11a96a00 100644
--- a/testing/adios2/engine/bp/CMakeLists.txt
+++ b/testing/adios2/engine/bp/CMakeLists.txt
@@ -10,5 +10,5 @@ if(NOT ADIOS_USE_MPI)
   add_executable(TestBPWriteRead TestBPWriteRead.cpp)
   target_link_libraries(TestBPWriteRead adios2 gtest adios1::adios)
 
-  add_test(NAME adios2::engine::bp::write_read COMMAND TestBPWriteRead)
+  gtest_add_tests(TARGET TestBPWriteRead)
 endif()
diff --git a/testing/adios2/engine/hdf5/CMakeLists.txt b/testing/adios2/engine/hdf5/CMakeLists.txt
index 2f69ed7a3a83f751b1a9f756dd350389b9e6db28..9847eaee6b081946ccf2c98db47a23b587daa223 100644
--- a/testing/adios2/engine/hdf5/CMakeLists.txt
+++ b/testing/adios2/engine/hdf5/CMakeLists.txt
@@ -9,4 +9,4 @@ add_executable(TestHDF5WriteRead TestHDF5WriteRead.cpp)
 target_include_directories(TestHDF5WriteRead PRIVATE ${HDF5_C_INCLUDE_DIRS})
 target_link_libraries(TestHDF5WriteRead adios2 gtest ${HDF5_C_LIBRARIES})
 
-add_test(NAME adios2::engine::hdf5::write_read COMMAND TestHDF5WriteRead)
+gtest_add_tests(TARGET TestHDF5WriteRead)
diff --git a/testing/adios2/interface/CMakeLists.txt b/testing/adios2/interface/CMakeLists.txt
index 7924344f3098f0dd61e0a4d3b91b76143c70b314..e229416de28347dbe46931805724e7d05bbdfdb2 100644
--- a/testing/adios2/interface/CMakeLists.txt
+++ b/testing/adios2/interface/CMakeLists.txt
@@ -5,4 +5,5 @@
 
 add_executable(TestADIOSInterfaceWrite TestADIOSInterfaceWrite.cpp)
 target_link_libraries(TestADIOSInterfaceWrite adios2 gtest gtest_main)
-add_test(NAME adios2::interface::write COMMAND TestADIOSInterfaceWrite)
+
+gtest_add_tests(TARGET TestADIOSInterfaceWrite)