name: Check ASan & UBSan

on:
  push:
    branches-ignore:
      - gh-readonly-queue/**
      - master
  pull_request:
  merge_group:

jobs:
  check-clang-san:
    runs-on: ubuntu-latest
    env:
      CARGO_HTTP_MULTIPLEXING: false
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      with:
        submodules: recursive

    - name: Prepare Linux
      run: |
        sudo apt-get update -y
        sudo apt-get install pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev libvulkan-dev glslang-tools spirv-tools libglew-dev -y

    - name: Cache Rust dependencies
      uses: Swatinem/rust-cache@v2

    - name: Build with ASan and UBSan
      run: |
        mkdir clang-sanitizer
        cd clang-sanitizer
        export CC=clang
        export CXX=clang++
        export CXXFLAGS="-fsanitize=address,undefined -fsanitize-recover=address,undefined -fno-omit-frame-pointer"
        export CFLAGS="-fsanitize=address,undefined -fsanitize-recover=address,undefined -fno-omit-frame-pointer"
        cmake -DCMAKE_BUILD_TYPE=Debug -DHEADLESS_CLIENT=ON -Werror=dev -DDOWNLOAD_GTEST=ON -DDEV=ON ..
        make -j"$(nproc)"

    - name: Run server and headless client with ASan and UBSan
      run: |
        cd clang-sanitizer
        export UBSAN_OPTIONS=suppressions=../ubsan.supp:log_path=SAN:print_stacktrace=1:halt_on_errors=0
        export ASAN_OPTIONS=log_path=SAN:print_stacktrace=1:check_initialization_order=1:detect_leaks=1:halt_on_errors=0
        export LSAN_OPTIONS=suppressions=../lsan.supp
        ./DDNet "cl_download_skins 0;quit" || true
        ./DDNet-Server shutdown || true
        if test -n "$(find . -maxdepth 1 -name 'SAN.*' -print -quit)"
        then
          cat SAN.*
          exit 1
        fi

    - name: Run unit tests with ASan and UBSan
      run: |
        cd clang-sanitizer
        cmake --build . --config Debug --target run_cxx_tests
        # Rust tests work locally, but still not in CI, even with the same directory
        if test -n "$(find . -maxdepth 1 -name 'SAN.*' -print -quit)"
        then
          cat SAN.*
          exit 1
        fi

    - name: Build mastersrv
      run: |
        cd src/mastersrv
        cargo build
        cp target/debug/mastersrv ../../clang-sanitizer

    - name: Run integration tests with ASan and UBSan
      run: |
        cd clang-sanitizer
        python ../scripts/integration_test.py --test-mastersrv .
        if test -n "$(find . -maxdepth 1 -name 'SAN.*' -print -quit)"
        then
          cat SAN.*
          exit 1
        fi
