name: Code Coverage

on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]

# Cancel in-progress runs when new commits are pushed
concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: true

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  CARGO_PROFILE_DEV_DEBUG: line-tables-only

jobs:
  coverage:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v6

    - name: Install Rust
      uses: dtolnay/rust-toolchain@stable
      with:
        components: llvm-tools-preview

    - name: Cache Rust dependencies
      uses: Swatinem/rust-cache@v2
      with:
        cache-bin: "false"

    - name: Install cargo-llvm-cov
      uses: taiki-e/install-action@v2
      with:
        tool: cargo-llvm-cov

    - name: Install cargo-nextest
      uses: taiki-e/install-action@v2
      with:
        tool: cargo-nextest

    - name: Free up disk space
      run: |
        sudo rm -rf /usr/share/dotnet
        sudo rm -rf /usr/local/lib/android
        sudo rm -rf /opt/ghc
        sudo rm -rf /opt/hostedtoolcache/CodeQL
        sudo docker image prune --all --force
        df -h

    - name: Generate code coverage
      run: |
        mkdir -p target/coverage
        cargo llvm-cov clean
        SKIP_INTEGRATION_TESTS=1 cargo llvm-cov nextest --html --output-dir target/coverage \
          --lib --test analyzer_tests --test complexity_tests --test core_metrics_tests \
          --test debt_tests --test entropy_tests \
          --status-level fail --final-status-level slow
        cargo llvm-cov report --json --summary-only --output-path target/coverage/coverage-summary.json
        python3 - <<'PY'
        import json
        import sys

        threshold = 80.0
        with open("target/coverage/coverage-summary.json", encoding="utf-8") as coverage_file:
            totals = json.load(coverage_file)["data"][0]["totals"]["lines"]

        percent = float(totals["percent"])
        covered = int(totals["covered"])
        count = int(totals["count"])
        print(f"Line coverage: {percent:.2f}% ({covered}/{count})")

        if percent < threshold:
            print(
                f"::error::Line coverage {percent:.2f}% is below the required "
                f"{threshold:.2f}% threshold"
            )
            sys.exit(1)
        PY

    - name: Archive code coverage results
      if: always()
      uses: actions/upload-artifact@v7
      with:
        name: code-coverage-report
        path: target/coverage/
