# This GitHub workflow will setup and run various kinds of tests with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: test

on:
  schedule:
    # The schedule event always (and only) runs on the master branch.
    - # cron (in UTC): minute hour day_of_month month day_of_week
      cron: '00 22 * * SAT'
  push:
    branches: [ master, stable_* ]
  pull_request:
    branches: [ master, stable_* ]

env:
  # Local Docker image cache directory
  DOCKER_CACHE_DIR: ~/docker-cache

jobs:

  set_matrix:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.select_matrix.outputs.matrix }}
    steps:
    - name: "Select matrix"
      id: select_matrix
      # Select full matrix when scheduled or when releasing, and normal matrix
      # otherwise. The matrix is defined as a JSON string.
      # This technique documented in:
      #    https://stackoverflow.com/questions/65384420/how-to-make-a-github-action-matrix-element-conditional
      # NOTE: Ubuntu 20.04 used because in some tests Ubuntu:latest fails with
      # Python 3.6. See pywbemtools issue 1245 for explanation.
      # TODO: Find a way to define this with fewer escapes.
      run: |
        if [[ "${{ github.event_name }}" == "schedule" || "${{ github.head_ref }}" =~ ^release_ ]]; then \
          echo "matrix={ \
            \"os\": [ \"ubuntu-latest\", \"macos-latest\", \"windows-latest\" ], \
            \"python-version\": [ \"3.6\", \"3.7\", \"3.8\", \"3.9\", \"3.10\", \"3.11\", \"3.12\" ], \
            \"package_level\": [ \"minimum\", \"latest\" ], \
            \"exclude\": [ \
              { \
                \"os\": \"ubuntu-latest\", \
                \"python-version\": \"3.6\", \
                \"package_level\": \"minimum\" \
              }, \
              { \
                \"os\": \"ubuntu-latest\", \
                \"python-version\": \"3.6\", \
                \"package_level\": \"latest\" \
              } \
            ], \
            \"include\": [ \
              { \
                \"os\": \"ubuntu-20.04\", \
                \"python-version\": \"2.7\", \
                \"package_level\": \"minimum\", \
                \"container\": {\"image\": \"python:2.7.18-buster\"} \
              }, \
              { \
                \"os\": \"ubuntu-20.04\", \
                \"python-version\": \"2.7\", \
                \"package_level\": \"latest\", \
                \"container\": {\"image\": \"python:2.7.18-buster\"} \
              }, \
              { \
                \"os\": \"ubuntu-20.04\", \
                \"python-version\": \"3.6\", \
                \"package_level\": \"minimum\" \
              }, \
              { \
                \"os\": \"ubuntu-20.04\", \
                \"python-version\": \"3.6\", \
                \"package_level\": \"latest\" \
              } \
            ] \
          }" >> $GITHUB_OUTPUT; \
        else \
          echo "matrix={ \
            \"os\": [ \"ubuntu-latest\" ], \
            \"python-version\": [ \"3.12\" ], \
            \"package_level\": [ \"minimum\", \"latest\" ], \
            \"include\": [ \
              { \
                \"os\": \"ubuntu-20.04\", \
                \"python-version\": \"2.7\", \
                \"package_level\": \"minimum\", \
                \"container\": {\"image\": \"python:2.7.18-buster\"} \
              }, \
              { \
                \"os\": \"ubuntu-20.04\", \
                \"python-version\": \"2.7\", \
                \"package_level\": \"latest\", \
                \"container\": {\"image\": \"python:2.7.18-buster\"} \
              }, \
              { \
                \"os\": \"ubuntu-20.04\", \
                \"python-version\": \"3.6\", \
                \"package_level\": \"minimum\" \
              }, \
              { \
                \"os\": \"ubuntu-20.04\", \
                \"python-version\": \"3.6\", \
                \"package_level\": \"latest\" \
              }, \
              { \
                \"os\": \"ubuntu-latest\", \
                \"python-version\": \"3.7\", \
                \"package_level\": \"minimum\" \
              }, \
              { \
                \"os\": \"ubuntu-latest\", \
                \"python-version\": \"3.7\", \
                \"package_level\": \"latest\" \
              }, \
              { \
                \"os\": \"ubuntu-latest\", \
                \"python-version\": \"3.8\", \
                \"package_level\": \"latest\" \
              }, \
              { \
                \"os\": \"ubuntu-latest\", \
                \"python-version\": \"3.10\", \
                \"package_level\": \"minimum\" \
              }, \
              { \
                \"os\": \"ubuntu-latest\", \
                \"python-version\": \"3.10\", \
                \"package_level\": \"latest\" \
              }, \
              { \
                \"os\": \"macos-latest\", \
                \"python-version\": \"3.6\", \
                \"package_level\": \"latest\" \
              }, \
              { \
                \"os\": \"macos-latest\", \
                \"python-version\": \"3.11\", \
                \"package_level\": \"latest\" \
              }, \
              { \
                \"os\": \"windows-latest\", \
                \"python-version\": \"3.6\", \
                \"package_level\": \"latest\" \
              }, \
              { \
                \"os\": \"windows-latest\", \
                \"python-version\": \"3.10\", \
                \"package_level\": \"minimum\" \
              }, \
              { \
                \"os\": \"windows-latest\", \
                \"python-version\": \"3.11\", \
                \"package_level\": \"minimum\" \
              }, \
              { \
                \"os\": \"windows-latest\", \
                \"python-version\": \"3.11\", \
                \"package_level\": \"latest\" \
              }, \
              { \
                \"os\": \"windows-latest\", \
                \"python-version\": \"3.12\", \
                \"package_level\": \"minimum\" \
              }, \
              { \
                \"os\": \"windows-latest\", \
                \"python-version\": \"3.12\", \
                \"package_level\": \"latest\" \
              } \
            ] \
          }" >> $GITHUB_OUTPUT; \
        fi
    - name: Show matrix in JSON
      run: echo '${{ steps.select_matrix.outputs.matrix }}'

  test:
    needs: set_matrix
    strategy:
      fail-fast: false
      max-parallel: 20
      matrix: ${{ fromJson(needs.set_matrix.outputs.matrix) }}
    runs-on: ${{ matrix.os }}
    container: ${{ matrix.container }}
    env:
      PIP_DISABLE_PIP_VERSION_CHECK: 1
      PIP_NO_PYTHON_VERSION_WARNING: 1
    steps:
    - name: Set run type (normal, scheduled, release)
      id: set-run-type
      uses: actions/github-script@v7
      with:
        result-encoding: string
        script: |
          var result
          if ("${{ github.event_name }}" == "schedule") {
            result = "scheduled"
          } else if ("${{ github.head_ref }}".match(/^release_/)) {
            result = "release"
          } else {
            result = "normal"
          }
          console.log(result)
          return result
    - name: Select whether to run steps for cythonized archive
      id: run_cythonized
      if: "! startsWith(matrix.os, 'windows-') && (github.event_name == 'schedule' || startsWith(github.head_ref, 'release_'))"
      run: |
        echo "Running steps for cythonized archive"
    - name: Create local cache directory for Docker images
      # GitHub Actions has Docker only on Ubuntu
      if: startsWith(matrix.os, 'ubuntu-')
      run: |
        mkdir -p ${{ env.DOCKER_CACHE_DIR }}
    - name: Set up caching for local Docker image cache directory
      # GitHub Actions has Docker only on Ubuntu
      if: startsWith(matrix.os, 'ubuntu-')
      uses: actions/cache@v4
      with:
        path: ${{ env.DOCKER_CACHE_DIR }}
        key: docker-cache-{hash}
        restore-keys: |
          docker-cache-
    - name: Checkout repo
      uses: actions/checkout@v4
      with:
        fetch-depth: 0
    - name: Set up Python ${{ matrix.python-version }}
      if: ${{ ! ( matrix.python-version == '2.7' ) }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
    - name: Display initial Python packages
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        echo "Installed Python packages:"
        make pip_list
    - name: Display platform and env vars
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make platform env
    - name: Install the package and its dependents
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make install
    - name: Display Python packages
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        echo "Installed Python packages:"
        make pip_list
    - name: Development setup
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make develop
    - name: Display Python packages
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        echo "Installed Python packages:"
        make pip_list
    - name: Show package dependency tree
      run: |
        echo "Package dependency tree of installed Python packages:"
        python -m pipdeptree --all
    - name: Run check_reqs
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make check_reqs
    - name: Run build
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make build
    - name: Run builddoc
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make builddoc
    - name: Run check
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make check
    - name: Run ruff
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make ruff
    - name: Run pylint
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make pylint
    - name: Run test
      # xmllint not in py27 container
      if: matrix.python-version != '2.7'
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
        # TESTCASES: test_cim_obj.py
      run: |
        make test
    - name: Run end2endtest
      # GitHub Actions has Docker only on Ubuntu, but not in py27 container
      if: startsWith(matrix.os, 'ubuntu-') && matrix.python-version != '2.7'
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make end2endtest
    - name: Send coverage result to coveralls.io
      # no coverage in py27 container because no test is run
      if: matrix.python-version != '2.7'
      shell: bash -l {0}
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        COVERALLS_PARALLEL: true
        COVERALLS_FLAG_NAME: "${{ matrix.os }},${{ matrix.python-version }},${{ matrix.package_level }}"
        COVERALLS_SERVICE_NAME: github
        COVERALLS_SERVICE_JOB_ID: "${{ github.run_id }}"
        COVERALLS_SERVICE_NUMBER: "${{ github.workflow }}-${{ github.run_number }}"
      run: |
        coveralls
    - name: Run installtest
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make installtest
    - name: Run leaktest
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make leaktest
    - name: Run resourcetest
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make resourcetest
    - name: Run perftest
      if: "github.event_name == 'schedule' || startsWith(github.head_ref, 'release_')"
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make perftest
    - name: Build cythonized archive
      if: steps.run_cythonized.outcome == 'success'
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make buildc
    - name: Install cythonized archive
      # Excluded py27 because the abitag in the dist file name is hard to predict,
      # and because it does not have a __file__ member in a module.
      if: steps.run_cythonized.outcome == 'success' && matrix.python-version != '2.7'
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make installc
    - name: Test cythonized archive
      # Excluded py27 because it does not have a __file__ member in a module.
      if: steps.run_cythonized.outcome == 'success' && matrix.python-version != '2.7'
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
        # TODO: Remove -x option once tests succeed
        TESTOPTS: "-x"
      run: |
        make testc
    - name: Run safety
      env:
        PACKAGE_LEVEL: ${{ matrix.package_level }}
        RUN_TYPE: ${{ steps.set-run-type.outputs.result }}
      run: |
        make safety

  test_finish:
    needs: test
    runs-on: ubuntu-latest
    container: python:3-slim
    steps:
    - name: Install coveralls
      run: |
        pip3 install --upgrade coveralls
    - name: Send coverage finish to coveralls.io
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        COVERALLS_SERVICE_NUMBER: "${{ github.workflow }}-${{ github.run_number }}"
      run: |
        coveralls --finish
