1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
|
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file LICENSE.rst or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindXCTest
----------
.. versionadded:: 3.3
Finds the XCTest framework for writing unit tests in Xcode projects.
.. note::
Xcode 16 and later includes the Swift Testing framework for writing unit tests
in the Swift programming language, which supersedes XCTest.
An XCTest bundle is a CFBundle (Core Foundation Bundle) with a special
product type and bundle extension. See the Apple Developer Library for more
information in the `Testing with Xcode`_ documentation.
.. _Testing with Xcode: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/
Result Variables
^^^^^^^^^^^^^^^^
This module defines the following variables:
``XCTest_FOUND``
Boolean indicating whether the XCTest framework and executable are found.
``XCTest_INCLUDE_DIRS``
Include directories containing the XCTest framework headers needed to use
XCTest.
``XCTest_LIBRARIES``
Libraries needed to link against to use XCTest framework.
Cache Variables
^^^^^^^^^^^^^^^
The following cache variables may also be set:
``XCTest_EXECUTABLE``
The path to the ``xctest`` command-line tool used to execute XCTest bundles.
Commands
^^^^^^^^
When XCTest is found, this module provides the following commands to help
create and run XCTest bundles:
.. command:: xctest_add_bundle
Creates an XCTest bundle to test a given target:
.. code-block:: cmake
xctest_add_bundle(<bundle> <testee> [<sources>...])
This command creates an XCTest bundle named ``<bundle>`` that will test the
specified ``<testee>`` target.
The arguments are:
``<bundle>``
Name of the XCTest bundle to create. The :prop_tgt:`XCTEST` target
property will be set on this bundle.
``<testee>``
Name of the target to test. Supported types for the testee are Frameworks
and App Bundles.
``<sources>...``
One or more source files to add to the bundle. If not provided, they must
be added later using commands like :command:`target_sources`.
.. note::
The :variable:`CMAKE_OSX_SYSROOT` variable must be set before using this
command.
.. command:: xctest_add_test
Adds an XCTest bundle to the project to be run during the CTest phase:
.. code-block:: cmake
xctest_add_test(<name> <bundle>)
This command registers an XCTest bundle to be executed by :manual:`ctest(1)`.
The test will be named ``<name>`` and will run the specified ``<bundle>``.
The arguments are:
``<name>``
Name of the test as it will appear in CTest.
``<bundle>``
Target name of the XCTest bundle.
Examples
^^^^^^^^
Finding XCTest and adding tests:
.. code-block:: cmake
find_package(XCTest)
add_library(foo SHARED foo.c)
if(XCTest_FOUND)
xctest_add_bundle(TestAppBundle foo source.swift)
xctest_add_test(app.TestAppBundle TestAppBundle)
endif()
#]=======================================================================]
set(_PRESERVED_CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}")
if(CMAKE_EFFECTIVE_SYSTEM_NAME STREQUAL "Apple"
AND NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# Non-macos systems set the CMAKE_FIND_ROOT_PATH_MODE to "ONLY" which
# restricts the search paths too much to find XCTest.framework. In
# contrast to the regular system frameworks which reside within the
# SDK direectory the XCTest framework is located in the respective
# platform directory which is not added to the CMAKE_FIND_ROOT_PATH
# (only to CMAKE_SYSTEM_FRAMEWORK_PATH) and therefore not searched.
#
# Until this is properly addressed, temporarily add the platform
# directory to CMAKE_FIND_ROOT_PATH.
list(APPEND CMAKE_FIND_ROOT_PATH "${_CMAKE_OSX_SYSROOT_PATH}/../..")
endif()
find_path(XCTest_INCLUDE_DIR
NAMES "XCTest/XCTest.h"
DOC "XCTest include directory")
mark_as_advanced(XCTest_INCLUDE_DIR)
find_library(XCTest_LIBRARY
NAMES XCTest
DOC "XCTest Framework library")
mark_as_advanced(XCTest_LIBRARY)
set(CMAKE_FIND_ROOT_PATH "${_PRESERVED_CMAKE_FIND_ROOT_PATH}")
unset(_PRESERVED_CMAKE_FIND_ROOT_PATH)
execute_process(
COMMAND xcrun --find xctest
OUTPUT_VARIABLE _xcrun_out OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_VARIABLE _xcrun_err)
if(_xcrun_out)
set(XCTest_EXECUTABLE "${_xcrun_out}" CACHE FILEPATH "XCTest executable")
mark_as_advanced(XCTest_EXECUTABLE)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(XCTest
REQUIRED_VARS XCTest_LIBRARY XCTest_INCLUDE_DIR XCTest_EXECUTABLE)
if(XCTest_FOUND)
set(XCTest_INCLUDE_DIRS "${XCTest_INCLUDE_DIR}")
set(XCTest_LIBRARIES "${XCTest_LIBRARY}")
endif()
function(xctest_add_bundle target testee)
if(NOT XCTest_FOUND)
message(FATAL_ERROR "XCTest is required to create a XCTest Bundle.")
endif()
if(NOT CMAKE_OSX_SYSROOT)
message(FATAL_ERROR "Adding XCTest bundles requires CMAKE_OSX_SYSROOT to be set.")
endif()
add_library(${target} MODULE ${ARGN})
set_target_properties(${target} PROPERTIES
BUNDLE TRUE
XCTEST TRUE
XCTEST_TESTEE ${testee})
target_link_libraries(${target} PRIVATE "-framework Foundation")
target_link_libraries(${target} PRIVATE ${XCTest_LIBRARIES})
target_include_directories(${target} PRIVATE ${XCTest_INCLUDE_DIRS})
# retrieve testee target type
if(NOT TARGET ${testee})
message(FATAL_ERROR "${testee} is not a target.")
endif()
get_property(_testee_type TARGET ${testee} PROPERTY TYPE)
get_property(_testee_framework TARGET ${testee} PROPERTY FRAMEWORK)
get_property(_testee_macosx_bundle TARGET ${testee} PROPERTY MACOSX_BUNDLE)
if(_testee_type STREQUAL "SHARED_LIBRARY" AND _testee_framework)
# testee is a Framework
target_link_libraries(${target} PRIVATE ${testee})
elseif(_testee_type STREQUAL "STATIC_LIBRARY")
# testee is a static library
target_link_libraries(${target} PRIVATE ${testee})
elseif(_testee_type STREQUAL "EXECUTABLE" AND _testee_macosx_bundle)
# testee is an App Bundle
add_dependencies(${target} ${testee})
if(XCODE)
set_target_properties(${target} PROPERTIES
XCODE_ATTRIBUTE_BUNDLE_LOADER "$(TEST_HOST)"
XCODE_ATTRIBUTE_TEST_HOST "$<TARGET_FILE:${testee}>")
# TEST_HOST overrides ${target}'s artifact path, but the relative
# path from TEST_HOST to ${testee}'s PlugIns folder must not leave
# ${target}'s TARGET_BUILD_DIR. If the project sets an explicit
# RUNTIME_OUTPUT_DIRECTORY for ${testee}, put ${target} there too.
# If not, just suppress the project's CMAKE_LIBRARY_OUTPUT_DIRECTORY.
get_property(testee_RUNTIME_OUTPUT_DIRECTORY TARGET ${testee} PROPERTY RUNTIME_OUTPUT_DIRECTORY)
set_property(TARGET ${target} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${testee_RUNTIME_OUTPUT_DIRECTORY})
else()
target_link_options(${target}
PRIVATE "SHELL:-bundle_loader \"$<TARGET_FILE:${testee}>\"")
endif()
else()
message(FATAL_ERROR "Testee ${testee} is of unsupported type.")
endif()
endfunction()
function(xctest_add_test name bundle)
if(NOT XCTest_EXECUTABLE)
message(FATAL_ERROR "XCTest executable is required to register a test.")
endif()
# check that bundle is an XCTest Bundle
if(NOT TARGET ${bundle})
message(FATAL_ERROR "${bundle} is not a target.")
endif()
get_property(_test_type TARGET ${bundle} PROPERTY TYPE)
get_property(_test_bundle TARGET ${bundle} PROPERTY BUNDLE)
get_property(_test_xctest TARGET ${bundle} PROPERTY XCTEST)
if(NOT _test_type STREQUAL "MODULE_LIBRARY"
OR NOT _test_xctest OR NOT _test_bundle)
message(FATAL_ERROR "Test ${bundle} is not an XCTest Bundle")
endif()
# get and check testee properties
get_property(_testee TARGET ${bundle} PROPERTY XCTEST_TESTEE)
if(NOT TARGET ${_testee})
message(FATAL_ERROR "${_testee} is not a target.")
endif()
get_property(_testee_type TARGET ${_testee} PROPERTY TYPE)
get_property(_testee_framework TARGET ${_testee} PROPERTY FRAMEWORK)
get_property(_testee_macosx_bundle TARGET ${_testee} PROPERTY MACOSX_BUNDLE)
# Determine the path to the test module artifact on disk.
set(_test_bundle_dir "$<TARGET_BUNDLE_DIR:${bundle}>")
if(XCODE AND _testee_type STREQUAL "EXECUTABLE" AND _testee_macosx_bundle)
# Xcode's TEST_HOST setting places the test module inside the testee bundle.
if(XCODE_VERSION VERSION_GREATER_EQUAL 7.3)
# The Xcode "new build system" used a different path until Xcode 12.5.
if(CMAKE_XCODE_BUILD_SYSTEM EQUAL 12 AND
XCODE_VERSION VERSION_LESS 12.5 AND
NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(_test_bundle_dir "$<TARGET_BUNDLE_CONTENT_DIR:${_testee}>")
else()
set(_test_bundle_dir "$<TARGET_BUNDLE_CONTENT_DIR:${_testee}>/PlugIns")
endif()
string(APPEND _test_bundle_dir "/$<TARGET_BUNDLE_DIR_NAME:${bundle}>")
endif()
endif()
# register test
# There's no target used for this command, so we don't need to do anything
# here for CMP0178.
add_test(
NAME ${name}
COMMAND ${XCTest_EXECUTABLE} ${_test_bundle_dir})
# point loader to testee in case rpath is disabled
if(_testee_type STREQUAL "SHARED_LIBRARY" AND _testee_framework)
set_property(TEST ${name} APPEND PROPERTY
ENVIRONMENT DYLD_FRAMEWORK_PATH=$<TARGET_LINKER_FILE_DIR:${_testee}>/..)
endif()
endfunction()
|