name: Tests

on:
  pull_request:
    paths-ignore:
      - README.md
      - CONTRIBUTING.md
      - CITATION.cff
      - LICENSE
      - .readthedocs.yml
      - docs-img/**
      - docs/**
      - awkward-cpp/docs/**
      - studies/**

  schedule:
    - cron: 0 12 1 * *

  workflow_dispatch:

concurrency:
  group: 'test-${{ github.head_ref || github.run_id }}'
  cancel-in-progress: true

env:
  # Leverage reproducible builds by setting a constant SOURCE_DATE_EPOCH
  # This will ensure that the hash of the awkward-cpp directory remains
  # constant for unchanged files, meaning that it can be used for caching
  SOURCE_DATE_EPOCH: "1668811211"

jobs:
  run-tests:
    name: Run Tests
    strategy:
      fail-fast: false
      matrix:
        runs-on:
          - windows-latest
          - ubuntu-latest
          - macos-14
        python-version:
          - '3.14'
          - '3.13'
          - '3.12'
          - '3.11'
          - '3.10'
          - '3.9'
        python-architecture:
          - x64
        dependencies-kind:
          - full
        exclude:
          - runs-on: macos-14
            python-architecture: x64
        include:
          - runs-on: macos-14
            python-architecture: arm64
            dependencies-kind: full
          - python-version: '3.9'
            python-architecture: x86
            runs-on: windows-latest
            dependencies-kind: full
          - python-version: '3.9'
            python-architecture: x86
            runs-on: windows-latest
            dependencies-kind: numpy1
          - python-version: '3.9'
            python-architecture: x64
            runs-on: ubuntu-latest
            dependencies-kind: minimal
          - python-version: 'pypy3.9'
            python-architecture: x64
            runs-on: ubuntu-latest
            dependencies-kind: pypy
          - python-version: '3.11'
            python-architecture: x64
            runs-on: ubuntu-latest
            dependencies-kind: ml
          - python-version: '3.13t'
            python-architecture: x64
            runs-on: ubuntu-latest
            dependencies-kind: nogil
          - python-version: '3.13t'
            python-architecture: x64
            runs-on: windows-latest
            dependencies-kind: nogil

    runs-on: ${{ matrix.runs-on }}

    env:
      PIP_ONLY_BINARY: numpy,pandas,pyarrow,numexpr,numexpr

    steps:
      - uses: actions/checkout@v5
        with:
          submodules: true

      - name: 'Python ${{ matrix.python-version }}'
        uses: actions/setup-python@v6
        with:
          python-version: '${{ matrix.python-version }}'
          architecture: '${{ matrix.python-architecture }}'
          allow-prereleases: true

      - name: Generate build files
        run: pipx run nox -s prepare -- --headers --signatures --tests

      - name: Cache awkward-cpp wheel
        id: cache-awkward-cpp-wheel
        uses: actions/cache@v4
        with:
          path: awkward-cpp/dist
          key: ${{ github.job }}-${{ matrix.runs-on }}-${{ matrix.python-version }}-${{ matrix.python-architecture }}-${{ hashFiles('awkward-cpp/**') }}

      - name: Build awkward-cpp wheel
        if: steps.cache-awkward-cpp-wheel.outputs.cache-hit != 'true'
        run: |
          python -m pip install build
          python -m build -w awkward-cpp

      - name: Find built wheel
        uses: tj-actions/glob@v22
        id: find-wheel
        with:
          files: |
            awkward-cpp/dist/*.whl

      - name: Add workaround for 3.13 + cramjam
        if: matrix.python-version == '3.13'
        run: echo 'PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1' >> $GITHUB_ENV
        shell: bash

      - name: Install awkward, awkward-cpp, and dependencies
        run: >-
          python -m pip install -v . ${{ steps.find-wheel.outputs.paths }} pytest-github-actions-annotate-failures
          -r "requirements-test-${{ matrix.dependencies-kind }}.txt"

      - name: Print versions
        run: python -m pip list

      - name: Check if kernel specification is sorted
        # We don't need to run this all the time
        if: (matrix.python-version == '3.12') && (matrix.runs-on == 'ubuntu-latest')
        run: pipx run nox -s diagnostics -- --check-spec-sorted

      - name: Test specification
        run: python -m pytest -vv -rs awkward-cpp/tests-spec

      - name: Test specification with explicitly defined values
        run: python -m pytest -vv -rs awkward-cpp/tests-spec-explicit

      - name: Test CPU kernels
        run: python -m pytest -vv -rs awkward-cpp/tests-cpu-kernels

      - name: Test CPU kernels with explicitly defined values
        run: python -m pytest -vv -rs awkward-cpp/tests-cpu-kernels-explicit

      - name: Test non-kernels (Python)
        run: >-
          python -m pytest -vv -rs tests --cov=awkward --cov-report=term
          --cov-report=xml
        if: startsWith(matrix.python-version, '3.')

      - name: Test non-kernels (PyPy)
        run: >-
          python -m pytest -vv -rs tests
        if: startsWith(matrix.python-version, 'pypy')

      - name: Upload Codecov results
        if: (matrix.python-version == '3.9') && (matrix.runs-on == 'ubuntu-latest')
        uses: codecov/codecov-action@v5

  run-gpu-tests:
    name: Run GPU Tests

    runs-on: self-hosted

    env:
      PIP_ONLY_BINARY: numpy,pandas,pyarrow,numexpr,numexpr

    # Required for miniconda to activate conda
    defaults:
      run:
        shell: bash -l {0}

    strategy:
      fail-fast: false
      matrix:
        cuda-version:
          - 12

    steps:
      - name: Clean the workspace and mamba
        run: |
          rm -rf * .[!.]* || echo "Nothing to clean"
          rm -rf ~/micromamba* || echo "Nothing to clean"

      - uses: actions/checkout@v5
        with:
          submodules: true

      - name: Get micromamba
        uses: mamba-org/setup-micromamba@v2
        with:
          environment-name: test-env
          init-shell: bash
          create-args: >-
            -c rapidsai
            -c nvidia
            python=3.13
            cudf
            cupy
            cuda-version=${{ matrix.cuda-version }}

      - name: Generate build files
        run: |
          pip install pipx
          pipx run nox -s prepare -- --headers --signatures --tests

      - name: Cache awkward-cpp wheel
        id: cache-awkward-cpp-wheel
        uses: actions/cache@v4
        with:
          path: awkward-cpp/dist
          key: ${{ github.job }}-${{ hashFiles('awkward-cpp/**') }}

      - name: Build awkward-cpp wheel
        if: steps.cache-awkward-cpp-wheel.outputs.cache-hit != 'true'
        run: |
          python -m pip install build
          python -m build -w awkward-cpp

      - name: Find built wheel
        uses: tj-actions/glob@v22
        id: find-wheel
        with:
          files: |
            awkward-cpp/dist/*.whl

      - name: Add workaround for 3.13 + cramjam
        run: echo 'PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1' >> $GITHUB_ENV
        shell: bash

      - name: Install awkward, awkward-cpp, and dependencies
        run: >-
          python -m pip install -v . ${{ steps.find-wheel.outputs.paths }} pytest-github-actions-annotate-failures
          -r "requirements-test-gpu.txt"

      - name: Print versions
        run: python -m pip list

      - name: Test CUDA kernels
        run: python -m pytest -vv -rs tests-cuda-kernels

      - name: Test CUDA kernels with explicitly defined values
        run: python -m pytest -vv -rs tests-cuda-kernels-explicit

      - name: Test CUDA non-kernel (Python)
        run: >-
          python -m pytest -vv -rs tests-cuda

  Linux-ROOT:
    runs-on: ubuntu-latest

    env:
      PIP_ONLY_BINARY: numpy,pandas,pyarrow,numexpr,numexpr

    timeout-minutes: 30

    # Required for miniconda to activate conda
    defaults:
      run:
        shell: "bash -l {0}"

    steps:
      - uses: "actions/checkout@v5"
        with:
          submodules: true

      - name: Setup Python via Conda
        uses: mamba-org/setup-micromamba@v2
        with:
          # Cache invalidates daily by default
          cache-environment: true
          environment-name: awkward
          # Need Python 3.9 for the cached wheels
          create-args: >-
            python=3.9
            numpy
            root

      - name: Generate build files
        run: pipx run nox -s prepare -- --headers --signatures --tests

      - name: Cache awkward-cpp wheel
        id: cache-awkward-cpp-wheel
        uses: actions/cache@v4
        with:
          path: ./awkward-cpp/dist
          key: ${{ github.job }}-${{ hashFiles('awkward-cpp/**') }}

      - name: Build awkward-cpp wheel
        if: steps.cache-awkward-cpp-wheel.outputs.cache-hit != 'true'
        run: |
          python3 -m pip install build
          python3 -m build -w ./awkward-cpp

      - name: Install awkward, awkward-cpp, and dependencies
        run: >-
          python -m pip install --only-binary "numpy,pandas,pyarrow,numexpr"
          -v . ./awkward-cpp/dist/*.whl
          pytest-github-actions-annotate-failures
          -r requirements-test-full.txt

      - name: Print versions
        run: python -m pip list

      - name: Test
        run: python -m pytest -vv -rs tests

  Linux-cppyy:
    # TODO: remove this part of the workflow
    # cppyy is not yet released. Let's load some pre-built wheels via docker (as a binary store)
    runs-on: ubuntu-22.04

    env:
      PIP_ONLY_BINARY: numpy,pandas,pyarrow,numexpr,numexpr

    steps:
      - uses: actions/checkout@v5
        with:
          submodules: true

      - name: 'Python ${{ matrix.python-version }}'
        uses: actions/setup-python@v6
        with:
          python-version: '3.11'

      - name: Generate build files
        run: pipx run nox -s prepare -- --headers --signatures --tests

      - name: Cache awkward-cpp wheel
        id: cache-awkward-cpp-wheel
        uses: actions/cache@v4
        with:
          path: ./awkward-cpp/dist
          key: ${{ github.job }}-${{ hashFiles('awkward-cpp/**') }}

      - name: Build awkward-cpp wheel
        if: steps.cache-awkward-cpp-wheel.outputs.cache-hit != 'true'
        run: |
          python -m pip install build
          python -m build -w ./awkward-cpp

      - name: Extract pre-built manylinux cppyy wheels
        uses: shrink/actions-docker-extract@v3
        with:
          image: "docker.io/agoose77/cppyy-wheels:cp311"
          path: "/wheels/."
          destination: "/tmp/wheelhouse"

      - name: Install awkward, awkward-cpp, and dependencies
        run: >-
          python -m pip install -v --only-binary "numpy,pandas,pyarrow,numexpr"
          ./ ./awkward-cpp/dist/*.whl /tmp/wheelhouse/* pytest-github-actions-annotate-failures

      - name: Print versions
        run: python -m pip list

      - name: Test
        run: python -m pytest -vv -rs tests -k cppyy


  pass-tests:
    if: always()
    needs: [ run-tests, Linux-ROOT, Linux-cppyy ]
    runs-on: ubuntu-latest
    steps:
      - uses: re-actors/alls-green@release/v1
        with:
          jobs: ${{ toJSON(needs) }}
