name: Tests

on:
  push:
    branches:
      - main
      - "maintenance/**"
    paths-ignore:
      - doc/data/messages/**
  pull_request:
    branches:
      - main
      - "maintenance/**"
  workflow_dispatch:

env:
  CACHE_VERSION: 5
  KEY_PREFIX: venv

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  tests:
    name: run / ${{ matrix.python-version }} / ${{ matrix.os }}
    timeout-minutes: 25
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest]
        python-version: &matrix-python-version ["3.10", "3.11", "3.12", "3.13", "3.14"]
        include:
          - os: macos-latest
            python-version: "3.10"
          - os: ubuntu-latest
            python-version: "pypy-3.10"
          - os: ubuntu-latest
            python-version: "pypy-3.11-v7.3.20"
    runs-on: ${{ matrix.os }}
    outputs:
      python-key: ${{ steps.generate-python-key.outputs.key }}
    steps:
      - &checkout
        name: Check out code from GitHub
        uses: actions/checkout@v5.0.0
      - &setup-python
        name: Set up Python ${{ matrix.python-version }}
        id: python
        uses: actions/setup-python@v6.0.0
        with:
          python-version: ${{ matrix.python-version }}
          allow-prereleases: true
          check-latest: true
      - name: Generate partial Python venv restore key
        id: generate-python-key
        run: >-
          echo "key=${{ env.KEY_PREFIX }}-${{ env.CACHE_VERSION }}-${{
            hashFiles('pyproject.toml', 'requirements_test.txt',
          'requirements_test_min.txt', 'requirements_test_pre_commit.txt') }}" >>
          $GITHUB_OUTPUT
      - &cache-python
        name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v4.3.0
        with:
          path: venv
          key: >-
            ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
            steps.generate-python-key.outputs.key }}
      - name: Create Python virtual environment
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          python -m venv venv
          . venv/bin/activate
          python -m pip install --upgrade pip
          pip install --upgrade --requirement requirements_test.txt
      - name: Run pytest
        run: |
          . venv/bin/activate
          pip install . --no-deps
          pip list | grep 'astroid\|pylint'
          python -m pytest --durations=10 --benchmark-disable --cov --cov-report= tests/
      - name: Run functional tests with minimal messages config
        run: |
          . venv/bin/activate
          pip list | grep 'astroid\|pylint'
          python -m pytest -vv --minimal-messages-config tests/test_functional.py --benchmark-disable
      - name: Upload coverage artifact
        if: runner.os == 'Linux'
        uses: actions/upload-artifact@v4.6.2
        with:
          name: coverage-${{ matrix.python-version }}
          include-hidden-files: true
          path: .coverage

  coverage:
    name: process / coverage
    runs-on: ubuntu-latest
    timeout-minutes: 5
    needs: [tests]
    steps:
      - *checkout
      - name: Set up Python 3.13
        id: python
        uses: actions/setup-python@v6.0.0
        with:
          python-version: "3.13"
          check-latest: true
      - &cache-restore-python
        name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache/restore@v4.3.0
        with:
          path: venv
          fail-on-cache-miss: true
          key:
            ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
            needs.tests.outputs.python-key }}
      - name: Download all coverage artifacts
        uses: actions/download-artifact@v5.0.0
      - name: Combine coverage results
        run: |
          . venv/bin/activate
          coverage combine coverage*/.coverage
          coverage xml
      - uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          fail_ci_if_error: true
          verbose: true

  benchmark-linux:
    name: run benchmark / ${{ matrix.python-version }} / Linux
    runs-on: ubuntu-latest
    timeout-minutes: 10
    needs: [tests]
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.13"]
    steps:
      - *checkout
      - *setup-python
      - *cache-restore-python
      - name: Run pytest
        run: |
          . venv/bin/activate
          pip install pygal
          pip install . --no-deps
          pip list | grep 'astroid\|pylint'
          pytest --exitfirst \
            --benchmark-only \
            --benchmark-autosave \
            --benchmark-save-data \
            --benchmark-group-by="group"
      - name: Create partial artifact name suffix
        id: artifact-name-suffix
        run: >-
          echo "datetime="$(date "+%Y%m%d_%H%M") >> $GITHUB_OUTPUT
      - name: Upload benchmark artifact
        uses: actions/upload-artifact@v4.6.2
        with:
          name:
            benchmark-${{ runner.os }}-${{ matrix.python-version }}_${{
            steps.artifact-name-suffix.outputs.datetime }}
          include-hidden-files: true
          path: .benchmarks/

  tests-windows:
    name: run / ${{ matrix.python-version }} / Windows
    runs-on: windows-latest
    timeout-minutes: 25
    needs: [tests]
    strategy:
      fail-fast: false
      matrix:
        python-version: *matrix-python-version
    steps:
      - name: Set temp directory
        run: echo "TEMP=$env:USERPROFILE\AppData\Local\Temp" >> $env:GITHUB_ENV
        # Workaround to set correct temp directory on Windows
        # https://github.com/actions/virtual-environments/issues/712
      - *checkout
      - *setup-python
      - name: Generate partial Python venv restore key
        id: generate-python-key
        run: >-
          echo "key=venv-${{ env.CACHE_VERSION }}-${{
            hashFiles('pyproject.toml', 'requirements_test_min.txt')
          }}" >> $env:GITHUB_OUTPUT
      - *cache-python
      - name: Create Python virtual environment
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          python -m venv venv
          . venv\\Scripts\\activate
          python -m pip install --upgrade pip
          pip install --upgrade --requirement requirements_test_min.txt
      - name: Run pytest
        run: |
          . venv\\Scripts\\activate
          pip install . --no-deps
          pip list | grep 'astroid\|pylint'
          python -m pytest --durations=10 --benchmark-disable tests/
