1# Copyright (c) 2021-2024 Huawei Device Co., Ltd.
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7#
8# Unless required by applicable law or agreed to in writing, software
9# distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
13# Convenience functions for testing Panda.
14
15include(${CMAKE_CURRENT_LIST_DIR}/PandaCmakeFunctions.cmake)
16
17function(common_add_gtest)
18    if(NOT PANDA_WITH_TESTS)
19        return()
20    endif()
21
22    set(prefix ARG)
23    set(noValues CONTAINS_MAIN NO_CORES RAPIDCHECK_ON NO_EXECUTABLE NO_WARNING_FILTER)
24    set(singleValues NAME OUTPUT_DIRECTORY TSAN_EXTRA_OPTIONS PANDA_STD_LIB ARK_BOOTCLASSPATH TEST_RUN_DIR TEST_GROUP STASH_LIST)
25    set(multiValues SOURCES INCLUDE_DIRS LIBRARIES SANITIZERS DEPS_TARGETS CUSTOM_PRERUN_ENVIRONMENT LAUNCHER)
26    set(TIMEOUT_SIGNAL USR1)
27
28    cmake_parse_arguments(${prefix}
29                          "${noValues}"
30                          "${singleValues}"
31                          "${multiValues}"
32                          ${ARGN})
33
34    if (ARG_RAPIDCHECK_ON AND DEFINED DONT_USE_RAPIDCHECK)
35      return()
36    endif()
37
38    if (NOT DEFINED ARG_OUTPUT_DIRECTORY)
39        message(FATAL_ERROR "OUTPUT_DIRECTORY is not defined")
40    endif()
41
42    if (NOT ARG_CONTAINS_MAIN)
43        if (NOT CMAKE_CROSSCOMPILING)
44            set(ARG_SOURCES ${ARG_SOURCES} ${PANDA_ROOT}/tests/gtest_launcher/main.cpp)
45        endif()
46    endif()
47
48    if (ARG_RAPIDCHECK_ON)
49        if(NOT ARG_NO_EXECUTABLE)
50		    panda_add_executable(${ARG_NAME} RAPIDCHECK_ON OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY} EXCLUDE_FROM_ALL ${ARG_SOURCES})
51        endif()
52        set_target_properties(${ARG_NAME} PROPERTIES LINK_FLAGS "-fno-rtti -fexceptions")
53        panda_target_compile_definitions(${ARG_NAME} PRIVATE PANDA_RAPIDCHECK)
54        panda_target_compile_options(${ARG_NAME} PRIVATE "-fno-rtti" "-fexceptions" "-fPIC")
55        panda_target_compile_definitions(${ARG_NAME} PUBLIC PANDA_RAPIDCHECK)
56    else()
57        if(NOT ARG_NO_EXECUTABLE)
58		    panda_add_executable(${ARG_NAME} OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY} EXCLUDE_FROM_ALL ${ARG_SOURCES})
59        endif()
60    endif()
61
62    if(NOT ARG_NO_EXECUTABLE)
63        string(REPLACE "${PANDA_BINARY_ROOT}/" "" bin_path ${ARG_OUTPUT_DIRECTORY})
64        if(NOT DEFINED ARG_STASH_LIST)
65            set(ARG_STASH_LIST stash_list)
66        endif()
67        set_property(GLOBAL APPEND PROPERTY ${ARG_STASH_LIST} "${bin_path}/${ARG_NAME}")
68    endif()
69
70    set(ARG_USE_GTESTS ON)
71
72    if (ARG_USE_GTESTS)
73        panda_target_compile_definitions(${ARG_NAME} PUBLIC PANDA_GTEST)
74        if (NOT ARG_CONTAINS_MAIN AND NOT CMAKE_CROSSCOMPILING)
75            find_program(GDB_PATH gdb REQUIRED)
76            panda_target_compile_definitions(${ARG_NAME} PUBLIC -DTIMEOUT_SIGNAL=SIG${TIMEOUT_SIGNAL} -DGDB_PATH=${GDB_PATH})
77        endif()
78    endif()
79
80    if(PANDA_CI_TESTING_MODE STREQUAL "Nightly")
81        panda_target_compile_definitions(${ARG_NAME} PUBLIC PANDA_NIGHTLY_TEST_ON)
82    endif()
83    # By default tests are just built, running is available either via an
84    # umbrella target or via `ctest -R ...`. But one can always do something
85    # like this if really needed:
86    # add_custom_target(${ARG_NAME}_run
87    #                  COMMENT "Run ${ARG_NAME}"
88    #                  COMMAND ${CMAKE_CTEST_COMMAND}
89    #                  DEPENDS ${ARG_NAME})
90    if (ARG_USE_GTESTS)
91        foreach(include_dir ${ARG_INCLUDE_DIRS})
92            panda_target_include_directories(${ARG_NAME} PUBLIC ${include_dir})
93        endforeach()
94        panda_target_include_directories(${ARG_NAME} SYSTEM PUBLIC
95            ${PANDA_THIRD_PARTY_SOURCES_DIR}/googletest/googlemock/include
96        )
97    endif()
98
99    if (ARG_USE_GTESTS)
100        if (CONTAINS_MAIN OR NOT CMAKE_CROSSCOMPILING)
101            panda_target_link_libraries(${ARG_NAME} gtest)
102        else()
103            panda_target_link_libraries(${ARG_NAME} gtest_main)
104        endif()
105    endif()
106
107    if (NOT (PANDA_TARGET_MOBILE OR PANDA_TARGET_OHOS))
108        panda_target_link_libraries(${ARG_NAME} pthread)
109    endif()
110    panda_target_link_libraries(${ARG_NAME} ${ARG_LIBRARIES})
111
112    add_dependencies(gtests_build ${ARG_NAME} ${ARG_DEPS_TARGETS})
113
114    if (ARG_RAPIDCHECK_ON)
115        panda_target_link_libraries(${ARG_NAME} rapidcheck)
116        panda_target_link_libraries(${ARG_NAME} rapidcheck_gtest)
117        panda_target_include_directories(${ARG_NAME} SYSTEM
118            PRIVATE ${PANDA_THIRD_PARTY_SOURCES_DIR}/rapidcheck/include
119        )
120        add_dependencies(${ARG_NAME} rapidcheck_gtest)
121        add_dependencies(${ARG_NAME} rapidcheck)
122    endif()
123
124    panda_add_sanitizers(TARGET ${ARG_NAME} SANITIZERS ${ARG_SANITIZERS})
125
126    set(prlimit_prefix "")
127    if (ARG_NO_CORES)
128        set(prlimit_prefix prlimit --core=0)
129    endif()
130
131    set(tsan_options "")
132    if ("thread" IN_LIST PANDA_SANITIZERS_LIST)
133        if (DEFINED ENV{TSAN_OPTIONS})
134            set(tsan_options "TSAN_OPTIONS=$ENV{TSAN_OPTIONS},${ARG_TSAN_EXTRA_OPTIONS}")
135        endif()
136    endif()
137
138    # Yes, this is hardcoded. No, do not ask for an option. Go and fix your tests.
139    if (PANDA_CI_TESTING_MODE STREQUAL "Nightly")
140        set(timeout_prefix timeout --preserve-status --signal=${TIMEOUT_SIGNAL} --kill-after=30s 40m)
141    else ()
142        set(timeout_prefix timeout --preserve-status --signal=${TIMEOUT_SIGNAL} --kill-after=30s 20m)
143    endif()
144
145    if (ARG_RAPIDCHECK_ON)
146        add_custom_target(${ARG_NAME}_rapidcheck_tests
147                          COMMAND ${tsan_options} ${prlimit_prefix} ${timeout_prefix}
148                                  ${PANDA_RUN_PREFIX} "${ARG_OUTPUT_DIRECTORY}/${ARG_NAME}"
149                          DEPENDS ${ARG_NAME} ${ARG_DEPS_TARGETS}
150        )
151        add_dependencies(gtests ${ARG_NAME}_rapidcheck_tests)
152    else()
153        set(PANDA_STD_LIB "")
154        if (DEFINED ARG_PANDA_STD_LIB)
155            set(PANDA_STD_LIB "PANDA_STD_LIB=${ARG_PANDA_STD_LIB}")
156        endif()
157
158        set(ARK_BOOTCLASSPATH "")
159        if (DEFINED ARG_ARK_BOOTCLASSPATH)
160            set(ARK_BOOTCLASSPATH "ARK_BOOTCLASSPATH=${ARG_ARK_BOOTCLASSPATH}")
161        endif()
162
163        set(TEST_RUN_DIR ${CMAKE_CURRENT_BINARY_DIR})
164        if(DEFINED ARG_TEST_RUN_DIR)
165            set(TEST_RUN_DIR ${ARG_TEST_RUN_DIR})
166        endif()
167
168        if(NOT DEFINED ARG_LAUNCHER)
169            set(ARG_LAUNCHER "${ARG_OUTPUT_DIRECTORY}/${ARG_NAME}")
170        endif()
171
172        set(output_file "${ARG_OUTPUT_DIRECTORY}/${ARG_NAME}_gtest_output.txt")
173        
174        # Some tests (examples: Logger.FileLogging) can work only with the "fast" mode.
175        # For such tests, it is necessary to disable filter using the NO_WARNING_FILTER flag.
176        # By default, the filter is enabled.
177        if(NOT ARG_NO_WARNING_FILTER)
178            set(FILTER ! grep "-q"
179                "'\\[WARNING] .* Death tests use fork(), which is unsafe particularly in a threaded context.'"
180                "${output_file}"
181            )
182        else()
183            set(FILTER true)
184        endif()
185        set(PRINT_FILTER_TRIGGERING_MESSAGE 
186            echo "Fail: using \\'fast\\' mode in multithreading test detected: please, check warnings below"
187        )
188        
189        add_custom_target(${ARG_NAME}_gtests
190                          COMMAND ${PANDA_STD_LIB} ${ARK_BOOTCLASSPATH} ${ARG_CUSTOM_PRERUN_ENVIRONMENT}
191                                  ${tsan_options} ${prlimit_prefix} ${timeout_prefix}
192                                  ${PANDA_RUN_PREFIX} ${ARG_LAUNCHER}
193                                  --gtest_shuffle --gtest_death_test_style=threadsafe
194                                  --gtest_brief=0
195                                  >${output_file} 2>&1
196                                  && (${FILTER} || (${PRINT_FILTER_TRIGGERING_MESSAGE} && false))
197                                  || (cat ${output_file} && false)
198                          DEPENDS ${ARG_NAME} ${ARG_DEPS_TARGETS}
199                          WORKING_DIRECTORY ${TEST_RUN_DIR}
200        )
201
202        if(NOT DEFINED ARG_TEST_GROUP)
203            set(ARG_TEST_GROUP gtests)
204        endif()
205        add_dependencies(${ARG_TEST_GROUP} ${ARG_NAME}_gtests)
206    endif()
207endfunction()
208