File: bindings-sysman-python-unit-tests-coverage.yml

package info (click to toggle)
level-zero 1.28.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 15,308 kB
  • sloc: cpp: 149,246; ansic: 16,655; python: 12,807; makefile: 5
file content (307 lines) | stat: -rw-r--r-- 12,532 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
##
# Copyright (C) 2026 Intel Corporation
#
# SPDX-License-Identifier: MIT
#
##

name: Python Bindings - Unit Tests & Coverage

on:
  pull_request:
    branches:
      - '**'
    paths:
      - 'bindings/sysman/python/**'
  push:
    branches:
      - main
      - master
      - python_bindings
    paths:
      - 'bindings/sysman/python/**'
  workflow_dispatch:

env:
  PYTHON_VERSION: '3.10'

jobs:
  unit-tests-linux:
    name: Linux - Unit Tests & Coverage Analysis
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: bindings/sysman/python
    permissions:
      contents: read
      checks: write        # For check results

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Full history for better coverage comparison

      - name: Set up Python ${{ env.PYTHON_VERSION }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: 'pip'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install pytest pytest-cov pytest-html pytest-xdist
          pip install coverage[toml]
          # Linting, formatting, and type checking tools
          pip install flake8 black isort mypy
          
          # Install any project-specific dependencies if requirements.txt exists
          if [ -f requirements.txt ]; then
            pip install -r requirements.txt
          fi

      - name: Code Quality Checks
        run: |
          echo "Running code quality checks..."
          
          # Check code formatting with black
          echo "Checking code formatting..."
          black --check --diff source/ test/ || {
            echo "❌ Code formatting issues found. Run 'black source/ test/' to fix."
            exit 1
          }
          
          # Check import sorting
          echo "Checking import sorting..."
          isort --check-only --diff source/ test/ || {
            echo "❌ Import sorting issues found. Run 'isort source/ test/' to fix."
            exit 1
          }
          
          # Run linting
          echo "Running flake8 linting..."
          flake8 source/ test/ || {
            echo "❌ Linting issues found. Check flake8 output above."
            exit 1
          }
          
          # Run type checking
          echo "Running mypy type checking..."
          mypy source/ --ignore-missing-imports --no-strict-optional || {
            echo "❌ Type checking issues found. Check mypy output above."
            exit 1
          }
          
          echo "✅ All code quality checks passed!"

      - name: Run Unit Tests with Coverage
        run: |
          # Run tests with coverage and generate multiple report formats
          python -m pytest test/unit_tests/ \
            --cov=source \
            --cov-config=pyproject.toml \
            --cov-report=term-missing \
            --cov-report=html:htmlcov \
            --cov-report=xml:coverage.xml \
            --cov-report=json:coverage.json \
            --junit-xml=test-results.xml \
            --html=test-report.html \
            --self-contained-html \
            -v \
            --tb=short \
            --durations=10

      - name: Extract Coverage Percentage
        id: coverage
        run: |
          # Extract coverage percentage from JSON report
          COVERAGE_PCT=$(python -c 'import json; data = json.load(open("coverage.json")); print("{:.1f}".format(data["totals"]["percent_covered"]))')
          echo "coverage_pct=$COVERAGE_PCT" >> $GITHUB_OUTPUT
          echo "Current Coverage: $COVERAGE_PCT%"

      - name: Get Baseline Coverage from Target Branch
        id: baseline
        continue-on-error: true
        run: |
          # ============================================================================
          # TEMPORARY: First Baseline Handling
          # TODO: Remove the special handling below after first PR merge to master
          # Once master has test coverage baseline, this graceful fallback is no longer needed
          # ============================================================================
          SKIP_BASELINE_ON_MISSING=true  # Set to false after first merge
          
          # Get the target branch (base of the PR or parent commit for push events)
          if [ "${{ github.event_name }}" == "pull_request" ]; then
            TARGET_BRANCH="${{ github.event.pull_request.base.ref }}"
            echo "Pull request detected - comparing against base branch: $TARGET_BRANCH"
          else
            # For push events, compare against the parent commit
            TARGET_BRANCH="${{ github.ref_name }}"
            echo "Push event detected - comparing against parent commit on branch: $TARGET_BRANCH"
            # Use HEAD~1 to get the previous commit
            git checkout HEAD~1 2>/dev/null || {
              if [ "$SKIP_BASELINE_ON_MISSING" = true ]; then
                echo "⚠️ No parent commit found (possibly first commit). Skipping baseline comparison."
                echo "baseline_coverage=0" >> $GITHUB_OUTPUT
                exit 0
              else
                echo "❌ No parent commit found"
                exit 1
              fi
            }
          fi
          
          echo "Target branch/commit: $TARGET_BRANCH"
          
          # Checkout target branch/commit to get baseline coverage
          if [ "${{ github.event_name }}" == "pull_request" ]; then
            git fetch origin $TARGET_BRANCH
            git checkout origin/$TARGET_BRANCH 2>/dev/null || {
              if [ "$SKIP_BASELINE_ON_MISSING" = true ]; then
                echo "⚠️ Could not checkout target branch. Skipping baseline comparison."
                echo "baseline_coverage=0" >> $GITHUB_OUTPUT
                exit 0
              else
                echo "❌ Could not checkout target branch: $TARGET_BRANCH"
                exit 1
              fi
            }
          fi
          
          # Check if tests exist in baseline
          if [ ! -d "bindings/sysman/python/test/unit_tests" ]; then
            if [ "$SKIP_BASELINE_ON_MISSING" = true ]; then
              echo "⚠️ No unit tests found in baseline. This is likely the first commit with tests."
              echo "Skipping baseline comparison."
              echo "baseline_coverage=0" >> $GITHUB_OUTPUT
              exit 0
            else
              echo "❌ No unit tests found in baseline branch"
              exit 1
            fi
          fi
          
          # Install dependencies and run tests to get baseline coverage
          python -m pip install --upgrade pip
          pip install pytest pytest-cov coverage[toml]
          
          # Run tests with coverage for baseline
          python -m pytest test/unit_tests/ \
            --cov=source \
            --cov-report=json:baseline-coverage.json \
            -q || {
            if [ "$SKIP_BASELINE_ON_MISSING" = true ]; then
              echo "⚠️ Baseline tests failed or not found. Skipping baseline comparison."
              echo "baseline_coverage=0" >> $GITHUB_OUTPUT
              exit 0
            else
              echo "❌ Baseline tests failed on $TARGET_BRANCH"
              echo "The target branch has broken tests. Fix the baseline before merging."
              exit 1
            fi
          }
          
          # Extract baseline coverage
          BASELINE_COVERAGE=$(python -c 'import json; data = json.load(open("baseline-coverage.json")); print("{:.1f}".format(data["totals"]["percent_covered"]))')
          echo "baseline_coverage=$BASELINE_COVERAGE" >> $GITHUB_OUTPUT
          echo "Baseline Coverage: $BASELINE_COVERAGE%"
          
          # Switch back to PR branch
          git checkout ${{ github.sha }}

      - name: Check Coverage Threshold
        run: |
          CURRENT_COVERAGE="${{ steps.coverage.outputs.coverage_pct }}"
          BASELINE_COVERAGE="${{ steps.baseline.outputs.baseline_coverage }}"
          
          echo "Current Coverage: $CURRENT_COVERAGE%"
          echo "Baseline Coverage: $BASELINE_COVERAGE%"
          
          # ============================================================================
          # TEMPORARY: First Baseline Handling
          # TODO: Remove this section after first PR merge to master
          # After first merge, baseline should always exist and this check is unnecessary
          # ============================================================================
          # If baseline is 0, this is the first commit with tests - always pass
          if [ "$BASELINE_COVERAGE" == "0" ] || [ -z "$BASELINE_COVERAGE" ]; then
            echo "✅ No baseline coverage found (first commit with tests)."
            echo "Current coverage: $CURRENT_COVERAGE%"
            echo "Establishing baseline for future comparisons."
            exit 0
          fi
          # ============================================================================
          # END TEMPORARY SECTION
          # ============================================================================
          
          # Use awk for floating point comparison (more portable than bc)
          if [ $(echo "$CURRENT_COVERAGE >= $BASELINE_COVERAGE" | awk '{print ($1 >= $3)}') -eq 1 ]; then
            DELTA=$(echo "$CURRENT_COVERAGE - $BASELINE_COVERAGE" | awk '{printf "%.1f", $1 - $3}')
            echo "✅ Coverage check passed: $CURRENT_COVERAGE% >= $BASELINE_COVERAGE% (Δ ${DELTA}%)"
          else
            REGRESSION=$(echo "$BASELINE_COVERAGE - $CURRENT_COVERAGE" | awk '{printf "%.1f", $1 - $3}')
            echo "❌ Coverage regression detected!"
            echo "Current coverage ($CURRENT_COVERAGE%) is below baseline coverage ($BASELINE_COVERAGE%)"
            echo "Regression: -${REGRESSION}%"
            echo "This would cause coverage to regress from the baseline."
            echo "Please add tests to maintain or improve coverage."
            exit 1
          fi

      - name: Coverage Summary
        if: always()
        run: |
          echo "## 📊 Test Coverage Report" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY
          echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
          echo "| Current Coverage | ${{ steps.coverage.outputs.coverage_pct }}% |" >> $GITHUB_STEP_SUMMARY
          echo "| Target Branch Coverage | ${{ steps.baseline.outputs.baseline_coverage }}% |" >> $GITHUB_STEP_SUMMARY
          
          CURRENT_COV="${{ steps.coverage.outputs.coverage_pct }}"
          BASELINE_COV="${{ steps.baseline.outputs.baseline_coverage }}"
          
          # Use awk for comparison (handles empty/zero baseline gracefully)
          if [ -z "$BASELINE_COV" ] || [ "$BASELINE_COV" == "0" ]; then
            echo "| Status | ✅ PASSED (Baseline Established) |" >> $GITHUB_STEP_SUMMARY
          elif awk "BEGIN {exit !($CURRENT_COV >= $BASELINE_COV)}"; then
            echo "| Status | ✅ PASSED |" >> $GITHUB_STEP_SUMMARY
          else
            echo "| Status | ❌ FAILED |" >> $GITHUB_STEP_SUMMARY
          fi
          echo "" >> $GITHUB_STEP_SUMMARY
          
          # Add detailed coverage report (only if .coverage file exists)
          if [ -f .coverage ]; then
            echo "### Detailed Coverage Report" >> $GITHUB_STEP_SUMMARY
            echo '```' >> $GITHUB_STEP_SUMMARY
            python -m coverage report >> $GITHUB_STEP_SUMMARY || echo "Coverage report generation failed" >> $GITHUB_STEP_SUMMARY
            echo '```' >> $GITHUB_STEP_SUMMARY
          else
            echo "### Detailed Coverage Report" >> $GITHUB_STEP_SUMMARY
            echo "Coverage data file not found. Report not available." >> $GITHUB_STEP_SUMMARY
          fi

      - name: Upload Test Results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results-${{ github.run_number }}
          path: |
            test-results.xml
            test-report.html
            htmlcov/
            coverage.xml
            coverage.json
          retention-days: 30

      - name: Upload Coverage Reports
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: coverage-report-${{ github.run_number }}
          path: |
            htmlcov/
            coverage.xml
            coverage.json
          retention-days: 30