name: C++ Tests

on:
  workflow_call:
    inputs:
      safe-checkout:
        required: true
        description: "The SHA key for the commit we want to run over"
        type: string

permissions:
  contents: read

jobs:
  linux:
    strategy:
      fail-fast: false   # Don't cancel all jobs if one fails.
      matrix:
        config:
          - { name: Optimized, flags: --config=opt }
          - { name: Debug, flags: --config=dbg }
          - { name: ASAN, flags: --config=asan, runner: ubuntu-22-large }
          - { name: MSAN, flags: --config=docker-msan, runner: ubuntu-22-large }
          - { name: TSAN, flags: --config=tsan }
          - { name: UBSAN, flags: --config=ubsan }
          - { name: No-RTTI, flags: --cxxopt=-fno-rtti }
        include:
          # Set defaults
          - image: us-docker.pkg.dev/protobuf-build/containers/test/linux/sanitize@sha256:04cd765285bc52cbbf51d66c8c66d8603579cf0f19cc42df26b09d2c270541fb
          - targets: //pkg/... //src/... @com_google_protobuf_examples//...

          # Override cases with custom images
          - config: { name: "TCMalloc" }
            image: "us-docker.pkg.dev/protobuf-build/containers/test/linux/tcmalloc@sha256:bd39119d74b8a3fad4ae335d4cf5294e70384676331b7e19949459fc7a8d8328"
            targets: "//src/..."
          - config: { name: "aarch64" }
            targets: "//src/... //src/google/protobuf/compiler:protoc_aarch64_test"
            image: "us-docker.pkg.dev/protobuf-build/containers/test/linux/emulation:aarch64-63dd26c0c7a808d92673a3e52e848189d4ab0f17"
    name: Linux ${{ matrix.config.name }}
    runs-on: ${{ matrix.config.runner || 'ubuntu-latest' }}
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}
      - name: Run tests
        uses: protocolbuffers/protobuf-ci/bazel-docker@v2
        with:
          image: ${{ matrix.image }}
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          bazel-cache: cpp_linux/${{ matrix.config.name }}
          bazel: test ${{ matrix.targets }} ${{ matrix.config.flags }}

  linux-gcc:
    strategy:
      fail-fast: false   # Don't cancel all jobs if one fails.
      matrix:
        version: ['9.5', '13.1']
    name: Linux GCC ${{ matrix.version }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}
      - name: Run tests
        uses: protocolbuffers/protobuf-ci/bazel-docker@v2
        with:
          image: us-docker.pkg.dev/protobuf-build/containers/test/linux/gcc:${{ matrix.version }}-6.3.0-63dd26c0c7a808d92673a3e52e848189d4ab0f17
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          bazel-cache: cpp_linux/gcc-${{ matrix.version }}
          bazel: test //pkg/... //src/... @com_google_protobuf_examples//...

  linux-release:
    strategy:
      fail-fast: false   # Don't cancel all jobs if one fails.
      matrix:
        arch: [x86_64, aarch64]
    name: Linux Release ${{ matrix.arch}}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}
          submodules: recursive
      - name: Cross compile protoc for ${{ matrix.arch }}
        id: cross-compile
        uses: protocolbuffers/protobuf-ci/cross-compile-protoc@v2
        with:
          image: us-docker.pkg.dev/protobuf-build/containers/common/linux/bazel:6.3.0-91a0ac83e968068672bc6001a4d474cfd9a50f1d
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          architecture: linux-${{ matrix.arch }}
      - name: Setup sccache
        uses: protocolbuffers/protobuf-ci/sccache@v2
        with:
          cache-prefix: linux-release-${{ matrix.arch }}
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
      - name: Run tests
        uses: protocolbuffers/protobuf-ci/docker@v2
        with:
          image: us-docker.pkg.dev/protobuf-build/containers/test/linux/emulation:${{ matrix.arch }}-63dd26c0c7a808d92673a3e52e848189d4ab0f17
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          entrypoint: bash
          command: >
            -c "set -ex;
            sccache -z;
            cmake . -DWITH_PROTOC=/workspace/${{ steps.cross-compile.outputs.protoc }} -Dprotobuf_BUILD_CONFORMANCE=ON -DCMAKE_CXX_STANDARD=14 -Dprotobuf_WITH_ZLIB=OFF ${{ env.SCCACHE_CMAKE_FLAGS }};
            cmake --build . --parallel 20;
            ctest --parallel 20;
            sccache -s"

  linux-cmake:
    strategy:
      fail-fast: false   # Don't cancel all jobs if one fails.
      matrix:
        include:
          - flags: -Dprotobuf_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=14
          - name: Ninja
            flags: -G Ninja -DCMAKE_CXX_STANDARD=14
          - name: Shared
            flags: -Dprotobuf_BUILD_SHARED_LIBS=ON -Dprotobuf_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=14
          - name: C++17
            flags: -DCMAKE_CXX_STANDARD=17
          # TODO Re-enable this.
          #- name: C++20
          #  flags: -DCMAKE_CXX_STANDARD=20

    name: Linux CMake ${{ matrix.name}}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}

      - name: Setup sccache
        uses: protocolbuffers/protobuf-ci/sccache@v2
        with:
          cache-prefix: linux-cmake
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}

      - name: Run tests
        uses: protocolbuffers/protobuf-ci/docker@v2
        with:
          image: us-docker.pkg.dev/protobuf-build/containers/test/linux/cmake:3.13.3-63dd26c0c7a808d92673a3e52e848189d4ab0f17
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          command: >-
            /test.sh ${{ matrix.flags}} ${{ env.SCCACHE_CMAKE_FLAGS }}
            -Dprotobuf_BUILD_TESTS=ON -Dprotobuf_USE_EXTERNAL_GTEST=ON
            -Dprotobuf_ABSL_PROVIDER=package

  linux-cmake-install:
    name: Linux CMake Install
    runs-on: ubuntu-latest
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}
          submodules: recursive

      - name: Setup sccache
        uses: protocolbuffers/protobuf-ci/sccache@v2
        with:
          cache-prefix: linux-cmake-install
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}

      - name: Run tests
        uses: protocolbuffers/protobuf-ci/docker@v2
        with:
          image: us-docker.pkg.dev/protobuf-build/containers/test/linux/cmake:3.13.3-63dd26c0c7a808d92673a3e52e848189d4ab0f17
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          command: >-
            /install.sh -DCMAKE_CXX_STANDARD=14 ${{ env.SCCACHE_CMAKE_FLAGS }} -Dprotobuf_USE_EXTERNAL_GTEST=ON -Dprotobuf_ABSL_PROVIDER=package \&\&
            /test.sh
            ${{ env.SCCACHE_CMAKE_FLAGS }}
            -Dprotobuf_REMOVE_INSTALLED_HEADERS=ON
            -Dprotobuf_BUILD_PROTOBUF_BINARIES=OFF
            -Dprotobuf_BUILD_CONFORMANCE=ON
            -DCMAKE_CXX_STANDARD=14
            -Dprotobuf_USE_EXTERNAL_GTEST=ON -Dprotobuf_ABSL_PROVIDER=package

  linux-cmake-examples:
    name: Linux CMake Examples
    runs-on: ubuntu-latest
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}

      - name: Setup sccache
        uses: protocolbuffers/protobuf-ci/sccache@v2
        with:
          cache-prefix: linux-cmake-examples
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}

      - name: Run tests
        uses: protocolbuffers/protobuf-ci/docker@v2
        with:
          image: us-docker.pkg.dev/protobuf-build/containers/test/linux/cmake:3.13.3-63dd26c0c7a808d92673a3e52e848189d4ab0f17
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          command: >-
            /install.sh -DCMAKE_CXX_STANDARD=14 ${{ env.SCCACHE_CMAKE_FLAGS }}
            -Dprotobuf_USE_EXTERNAL_GTEST=ON -Dprotobuf_ABSL_PROVIDER=package
            -Dprotobuf_BUILD_EXAMPLES=OFF \&\&
            mkdir examples/build \&\&
            cd examples/build \&\&
            cmake .. -DCMAKE_CXX_STANDARD=14 \&\&
            cmake --build .

  linux-cmake-gcc:
    strategy:
      fail-fast: false   # Don't cancel all jobs if one fails.
      matrix:
        include:
          - name: C++14
            flags: -DCMAKE_CXX_STANDARD=14
          - name: C++17
            flags: -DCMAKE_CXX_STANDARD=17
          - name: C++20
            flags: -DCMAKE_CXX_STANDARD=20

    name: Linux CMake GCC ${{ matrix.name }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}
          submodules: recursive

      - name: Setup sccache
        uses: protocolbuffers/protobuf-ci/sccache@v2
        with:
          cache-prefix: linux-cmake-gcc
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}

      - name: Run tests
        uses: protocolbuffers/protobuf-ci/docker@v2
        with:
          image: us-docker.pkg.dev/protobuf-build/containers/test/linux/gcc:12.2-6.3.0-63dd26c0c7a808d92673a3e52e848189d4ab0f17
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          entrypoint: bash
          command: >-
              -c 'set -ex;
              cd /workspace;
              sccache -z;
              cmake . ${{ matrix.flags }} ${{ env.SCCACHE_CMAKE_FLAGS }};
              cmake --build . --parallel 20;
              ctest --verbose --parallel 20;
              sccache -s'

  linux-cmake-submodules:
    name: Linux CMake Submodules
    runs-on: ubuntu-latest
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}
          submodules: recursive

      - name: Setup sccache
        uses: protocolbuffers/protobuf-ci/sccache@v2
        with:
          cache-prefix: linux-cmake-submodules
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}

      - name: Run tests
        uses: protocolbuffers/protobuf-ci/docker@v2
        with:
          image: us-docker.pkg.dev/protobuf-build/containers/test/linux/cmake:3.13.3-63dd26c0c7a808d92673a3e52e848189d4ab0f17
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          command: >-
            /test.sh ${{ env.SCCACHE_CMAKE_FLAGS }}
            -Dprotobuf_BUILD_CONFORMANCE=ON -Dprotobuf_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=14

  linux-cmake-32-bit:
    name: Linux CMake 32-bit
    runs-on: ubuntu-latest
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}
          submodules: recursive

      - name: Setup sccache
        uses: protocolbuffers/protobuf-ci/sccache@v2
        with:
          cache-prefix: linux-cmake-32-bit
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}

      - name: Run tests
        uses: protocolbuffers/protobuf-ci/docker@v2
        with:
          image: us-docker.pkg.dev/protobuf-build/containers/test/linux/32bit@sha256:8275360dc5d676f3470872d79087901c0e4153453976bea908a92c82e8d209ea
          platform: linux/386
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          command: >-
              /bin/bash -cex '
              cd /workspace;
              sccache -z;
              cmake . -DCMAKE_CXX_STANDARD=14 ${{ env.SCCACHE_CMAKE_FLAGS }};
              cmake --build . --parallel 20;
              ctest --verbose --parallel 20;
              sccache -s'

  non-linux:
    strategy:
      fail-fast: false   # Don't cancel all jobs if one fails.
      matrix:
        include:
          - name: MacOS
            os: macos-12
            cache_key: macos-12
            bazel: test //src/...
          - name: MacOS Apple Silicon (build only)
            os: macos-12
            cache_key: macos-12-arm
            # Current github runners are all Intel based, so just build/compile
            # for Apple Silicon to detect issues there.
            bazel: build --cpu=darwin_arm64 //src/...
          - name: Windows
            os: windows-2022
            cache_key: windows-2022
            bazel: test //src/...  @com_google_protobuf_examples//... --test_tag_filters=-conformance --build_tag_filters=-conformance
    name: ${{ matrix.name }} Bazel
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}
      - name: Run tests
        uses: protocolbuffers/protobuf-ci/bazel@v2
        with:
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          bazel: ${{ matrix.bazel }}
          bazel-cache: cpp_${{ matrix.cache_key }}

  non-linux-cmake:
    strategy:
      fail-fast: false   # Don't cancel all jobs if one fails.
      matrix:
        include:
          - name: MacOS CMake
            os: macos-12
            flags: -DCMAKE_CXX_STANDARD=14
            cache-prefix: macos-cmake
          - name: Windows CMake
            os: windows-2022
            flags: >-
              -G Ninja -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_BUILD_CONFORMANCE=OFF
              -Dprotobuf_BUILD_SHARED_LIBS=OFF
              -Dprotobuf_BUILD_EXAMPLES=ON
            vsversion: '2022'
            cache-prefix: windows-2022-cmake
          - name: Windows CMake 2019
            os: windows-2019
            flags: >-
              -G Ninja -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_BUILD_CONFORMANCE=OFF
              -Dprotobuf_BUILD_SHARED_LIBS=OFF
              -Dprotobuf_BUILD_EXAMPLES=ON
            vsversion: '2019'
            cache-prefix: windows-2019-cmake
            # windows-2019 has python3.7 installed, which is incompatible with the latest gcloud
            python-version: '3.8'
          - name: Windows CMake 32-bit
            os: windows-2022
            flags: >-
              -G Ninja -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_BUILD_CONFORMANCE=OFF
            vsversion: '2022'
            windows-arch: 'win32'
            cache-prefix: windows-2022-win32-cmake
          - name: Windows CMake Shared
            os: windows-2022
            flags: >-
              -G Ninja -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_BUILD_CONFORMANCE=OFF
              -Dprotobuf_BUILD_SHARED_LIBS=ON
            vsversion: '2022'
            cache-prefix: windows-2022-cmake
          - name: Windows CMake Install
            os: windows-2022
            install-flags: -G Ninja -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_BUILD_CONFORMANCE=OFF -Dprotobuf_BUILD_TESTS=OFF
            flags: >-
              -G Ninja -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_BUILD_CONFORMANCE=OFF
              -Dprotobuf_REMOVE_INSTALLED_HEADERS=ON
              -Dprotobuf_BUILD_PROTOBUF_BINARIES=OFF
            vsversion: '2022'
            cache-prefix: windows-2022-cmake
    name: ${{ matrix.name }}
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout pending changes
        uses: protocolbuffers/protobuf-ci/checkout@v2
        with:
          ref: ${{ inputs.safe-checkout }}
          submodules: recursive

      - name: Setup MSVC
        if: ${{ runner.os == 'Windows' }}
        uses: ilammy/msvc-dev-cmd@cec98b9d092141f74527d0afa6feb2af698cfe89 # v1.12.1
        with:
          arch: ${{ matrix.windows-arch || 'x64' }}
          vsversion: ${{ matrix.vsversion }}

      # Workaround for incompatibility between gcloud and windows-2019 runners.
      - name: Install Python
        if: ${{ matrix.python-version }}
        uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
        with:
          python-version: ${{ matrix.python-version }}
      - name: Use custom python for gcloud
        if: ${{ matrix.python-version }}
        run: echo "CLOUDSDK_PYTHON=${Python3_ROOT_DIR}\\python3" >> $GITHUB_ENV
        shell: bash

      - name: Setup sccache
        uses: protocolbuffers/protobuf-ci/sccache@v2
        with:
          cache-prefix: ${{ matrix.cache-prefix }}
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}

      # Install phase.
      - name: Configure CMake for install
        if: matrix.install-flags
        uses: protocolbuffers/protobuf-ci/bash@v2
        with:
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          command: cmake . ${{ matrix.install-flags }} ${{ env.SCCACHE_CMAKE_FLAGS }} -Dprotobuf_ALLOW_CCACHE=ON
      - name: Build for install
        if: matrix.install-flags
        shell: bash
        run: VERBOSE=1 cmake --build . --parallel 20
      - name: Install
        if: matrix.install-flags
        shell: bash
        run: cmake --build . --target install
      - name: Report and clear sccache stats
        if: matrix.install-flags
        shell: bash
        run: sccache -s && sccache -z
      - name: Clear CMake cache
        if: matrix.install-flags
        shell: bash
        run: cmake --build . --target clean && rm CMakeCache.txt

      - name: Configure CMake
        uses: protocolbuffers/protobuf-ci/bash@v2
        with:
          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
          command: cmake . ${{ matrix.flags }} ${{ env.SCCACHE_CMAKE_FLAGS }} -Dprotobuf_ALLOW_CCACHE=ON 

      - name: Build
        shell: bash
        run: VERBOSE=1 cmake --build . --parallel 20

      - name: Test
        shell: bash
        run: ctest --verbose --parallel 20 -C Debug

      - name: Report sccache stats
        shell: bash
        run: sccache -s
