# Added for autogenerated headers and LuaJIT ones.
include_directories(${PROJECT_SOURCE_DIR}/src)
include_directories(${PROJECT_BINARY_DIR}/src)
include_directories(${PROJECT_SOURCE_DIR}/src/box)

# A special target with fuzzer and sanitizer flags.
add_library(fuzzer_config INTERFACE)

set(TEST_SUITE_NAME "fuzzing-c")

target_compile_options(
    fuzzer_config
    INTERFACE
        $<$<NOT:$<BOOL:${OSS_FUZZ}>>:
        -fsanitize=fuzzer
        -fsanitize-coverage=trace-cmp
        -fprofile-instr-generate
        -fcoverage-mapping
        -g
        >
        $<$<BOOL:${OSS_FUZZ}>:
        ${CXX}
        ${CXXFLAGS}
        >
)

# `-stdlib=libstdc++` is required by ICU on OSS Fuzz.
# `-lc++` is used by Centipede, so `-lc++` needs to come after
# centipede's lib.
target_link_libraries(
    fuzzer_config
    INTERFACE
        $<$<NOT:$<BOOL:${OSS_FUZZ}>>:
        -fsanitize=fuzzer
        -fsanitize-coverage=trace-cmp
        -fprofile-instr-generate
        -fcoverage-mapping
        -g
        -stdlib=libstdc++
        >
        $<$<BOOL:${OSS_FUZZ}>:
        $ENV{LIB_FUZZING_ENGINE}
        -lc++
        -stdlib=libstdc++
        >
)

set(FUZZ_TEST_TARGETS "")

set(DEFAULT_RUNS_NUMBER 10)

string(JOIN " " LIBFUZZER_OPTS
  -mutate_depth=20
  -print_final_stats=1
  -print_pcs=1
  -reduce_inputs=1
  -reload=1
  -report_slow_units=5
  -seed=${RANDOM_SEED}
  -use_cmp=1
  -use_counters=1
  -use_memmem=1
  -use_value_profile=1
  -workers=${CMAKE_BUILD_PARALLEL_LEVEL}
  # Shell parameter expansion,
  # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
  -runs=\${RUNS:-${DEFAULT_RUNS_NUMBER}}
)

message(STATUS "Add test suite ${TEST_SUITE_NAME}")
function(create_fuzz_test)
  cmake_parse_arguments(
    FUZZ
    ""
    "PREFIX"
    "SOURCES;LIBRARIES"
    ${ARGN}
  )
  add_executable(${FUZZ_PREFIX}_fuzzer ${FUZZ_SOURCES})
  # XXX: The call produces both test and target
  # <${FUZZ_PREFIX}-deps> as a side effect.
  _add_test_suite_target(${FUZZ_PREFIX}
    LABELS "${TEST_SUITE_NAME};fuzzing;"
    DEPENDS ${FUZZ_LIBRARIES} ${FUZZ_PREFIX}_fuzzer
  )
  # Use PUBLIC to force 'fuzzer_config' for all dependent targets.
  target_link_libraries(${FUZZ_PREFIX}_fuzzer PUBLIC ${FUZZ_LIBRARIES})
  set(FUZZ_TEST_TARGETS ${FUZZ_TEST_TARGETS} ${FUZZ_PREFIX}_fuzzer PARENT_SCOPE)
  set(LIBFUZZER_OPTS_EFFECTIVE ${LIBFUZZER_OPTS})
  set(dict_path ${PROJECT_SOURCE_DIR}/test/static/${FUZZ_PREFIX}.dict)
  if(EXISTS ${dict_path})
    set(LIBFUZZER_OPTS_EFFECTIVE "${LIBFUZZER_OPTS_EFFECTIVE} -dict=${dict_path}")
  endif()
  set(corpus_path ${PROJECT_SOURCE_DIR}/test/static/corpus/${FUZZ_PREFIX})
  if(EXISTS ${corpus_path})
    set(LIBFUZZER_OPTS_EFFECTIVE "${LIBFUZZER_OPTS_EFFECTIVE} ${corpus_path}")
  endif()
  set(test_title test/fuzz/${FUZZ_PREFIX}_test)
  set(test_command "$<TARGET_FILE:${FUZZ_PREFIX}_fuzzer> \
        -artifact_prefix=${FUZZ_PREFIX}_ ${LIBFUZZER_OPTS_EFFECTIVE}")
  add_test(NAME ${test_title}
           COMMAND ${BASH} -c "${test_command}"
  )
  set_tests_properties(${test_title} PROPERTIES
    LABELS "${TEST_SUITE_NAME};fuzzing;"
    DEPENDS ${FUZZ_PREFIX}-deps
  )
endfunction()

create_fuzz_test(PREFIX csv
                 SOURCES csv_fuzzer.c
                 LIBRARIES csv fuzzer_config
)

create_fuzz_test(PREFIX uri
                 SOURCES uri_fuzzer.c
                 LIBRARIES uri fuzzer_config
)

create_fuzz_test(PREFIX http_parser
                 SOURCES http_parser_fuzzer.c
                 LIBRARIES http_parser fuzzer_config
)

create_fuzz_test(PREFIX swim_proto_member
                 SOURCES swim_proto_member_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES swim fuzzer_config
)

create_fuzz_test(PREFIX swim_proto_meta
                 SOURCES swim_proto_meta_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES swim fuzzer_config
)

create_fuzz_test(PREFIX datetime_parse_full
                 SOURCES datetime_parse_full_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES core fuzzer_config
)

create_fuzz_test(PREFIX datetime_strptime
                 SOURCES datetime_strptime_fuzzer.cc
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES core fuzzer_config
)

create_fuzz_test(PREFIX mp_datetime
                 SOURCES mp_datetime_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES core fuzzer_config
)

create_fuzz_test(PREFIX vclock_from_string
                 SOURCES vclock_from_string_fuzzer.c
                 LIBRARIES vclock fuzzer_config)

create_fuzz_test(PREFIX xrow_greeting_decode
                 SOURCES xrow_greeting_decode_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_decode_id
                 SOURCES xrow_decode_id_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_decode_auth
                 SOURCES xrow_decode_auth_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_decode_begin
                 SOURCES xrow_decode_begin_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_decode_call
                 SOURCES xrow_decode_call_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_decode_dml
                 SOURCES xrow_decode_dml_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_decode_raft
                 SOURCES xrow_decode_raft_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_decode_sql
                 SOURCES xrow_decode_sql_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_decode_watch
                 SOURCES xrow_decode_watch_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_header_decode
                 SOURCES xrow_header_decode_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

create_fuzz_test(PREFIX xrow_decode_error
                 SOURCES xrow_decode_error_fuzzer.c
                         ${PROJECT_SOURCE_DIR}/test/unit/core_test_utils.c
                 LIBRARIES xrow fuzzer_config)

# Blocked by https://github.com/tarantool/tarantool/issues/8948.
if (NOT ENABLE_UB_SANITIZER)
  create_fuzz_test(PREFIX decimal_to_int64
                   SOURCES decimal_to_int64_fuzzer.c
                   LIBRARIES core fuzzer_config)
endif ()

include(ProtobufMutator)

add_subdirectory(luaL_loadbuffer)
add_subdirectory(sql_fuzzer)
add_subdirectory(lua)

add_custom_target(fuzzers
                  DEPENDS ${FUZZ_TEST_TARGETS} lua-tests
                  COMMENT "Build fuzzers")
