It is possible to nest function calls in macros and macro calls in functions, but we need to carefully consider the scope of the variables. If a feature can be implemented using a function, then this is probably preferable to a macro since it gives more default control over the parent scope state.
We should also mention the use of CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE in src/CMakeLists.txt:
set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON)
This command adds the current directory to the INTERFACE_INCLUDE_DIRECTORIES property for all targets defined in this CMakeLists.txt file. In other words, we did not have to use target_include_directories to indicate the header file location for cpp_test.