name: Tests

on:
  push:
    branches:
      - dev-3.x
    paths:
      - ".github/workflows/tests.yml"
      - "aiogram/**"
      - "tests/**"
      - ".coveragerc"
      - ".flake8"
      - "codecov.yaml"
      - "mypy.ini"
      - "poetry.lock"
      - "pyproject.toml"
  pull_request:
    branches:
      - dev-3.x
    paths:
      - ".github/workflows/tests.yml"
      - "aiogram/**"
      - "tests/**"
      - ".coveragerc"
      - ".flake8"
      - "codecov.yaml"
      - "mypy.ini"
      - "poetry.lock"
      - "pyproject.toml"

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        os:
          - ubuntu-latest
          - macos-latest
          - windows-latest
        python-version:
          - '3.8'
          - '3.9'
          - '3.10'
          - '3.11'
          - 'pypy3.8'
          - 'pypy3.9'

    defaults:
      # Windows sucks. Force use bash instead of PowerShell
      run:
        shell: bash

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

    env:
      # We disable some features for PyPy by this environment variable such as:
      # – Installation of `fast` extras: `uvloop` on PyPy is useless and may be even slower
      # than the default loop;
      # – Coverage reports: code introspection disables any optimizations, so tests with
      # coverage enabled are very slow on PyPy.
      # More: https://www.pypy.org/performance.html
      IS_PYPY: ${{ startswith(matrix.python-version, 'pypy') }}
      # Windows has also some limitations:
      # – Redis is not supported on GitHub Windows runners;
      # – Poetry installer doesn't work on Windows with PyPy.
      IS_WINDOWS: ${{ startswith(matrix.os, 'windows') }}

    steps:
      - name: Checkout code
        uses: actions/checkout@master

      - name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install and configure Poetry
        uses: snok/install-poetry@v1
        if: "env.IS_PYPY == 'false' || env.IS_WINDOWS == 'false'"
        with:
          version: 1.2.1
          virtualenvs-create: true
          virtualenvs-in-project: true
          installer-parallel: true

      - name: Install and configure Poetry (PyPy on Windows)
        if: "env.IS_PYPY == 'true' && env.IS_WINDOWS == 'true'"
        run: |
          set -eu
          pip install "poetry==1.2.1"
          poetry config virtualenvs.create true
          poetry config virtualenvs.in-project true
          poetry config installer.parallel true

      - name: Setup redis
        if: ${{ env.IS_WINDOWS == 'false' }}
        uses: shogo82148/actions-setup-redis@v1
        with:
          redis-version: 6

      - name: Load cached venv
        id: cached-poetry-dependencies
        uses: actions/cache@v2
        with:
          path: .venv
          key: venv-${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}-${{ secrets.CACHE_VERSION }}

      - name: Install project dependencies
        if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
        run: |
          flags=""
          [[ "$IS_PYPY" == "false" ]] && flags="$flags -E fast"
          poetry install --no-interaction -E redis -E proxy -E i18n $flags

      - name: Lint code
        if: "env.IS_PYPY == 'false'"
        run: |
          poetry run flake8 aiogram
          poetry run mypy aiogram
          poetry run black --check --diff aiogram tests

      - name: Run tests
        run: |
          flags=""
          [[ "$IS_PYPY" == "false" ]] && flags="$flags --cov=aiogram --cov-config .coveragerc --cov-report=xml"
          [[ "$IS_WINDOWS" == "false" ]] && flags="$flags --redis redis://localhost:6379/0"
          poetry run pytest $flags

      - name: Upload coverage data
        if: "env.IS_PYPY == 'false'"
        uses: codecov/codecov-action@v1
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          file: coverage.xml
          flags: unittests
          name: py-${{ matrix.python-version }}-${{ matrix.os }}
          fail_ci_if_error: true
