diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index d786061..59bf3cf 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -26,6 +26,11 @@ jobs:
diff-storage: _xml_coverage_reports
coverage-summary-title: "Code Coverage Summary (test-branch.xml)"
exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
- path: test-missing-lines.xml
threshold: 50
fail: false
@@ -35,6 +40,11 @@ jobs:
diff-storage: _xml_coverage_reports_5
coverage-summary-title: "Code Coverage Summary (test-missing-lines.xml)"
exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
- path: test-no-branch.xml
threshold: 90
fail: true
@@ -44,6 +54,11 @@ jobs:
diff-storage: _xml_coverage_reports_1
coverage-summary-title: "Code Coverage Summary (test-no-branch.xml)"
exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
- path: test-python.xml
threshold: 90
fail: false
@@ -53,6 +68,11 @@ jobs:
diff-storage: _xml_coverage_reports_2
coverage-summary-title: "Code Coverage Summary (test-python.xml)"
exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
- path: test-no-branch-2.xml
threshold: 90
fail: true
@@ -62,6 +82,11 @@ jobs:
diff-storage: _xml_coverage_reports_3
coverage-summary-title: "Code Coverage Summary (test-no-branch.xml) without detailed coverage"
exclude-detailed-coverage: true
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
- path: test-missing-lines-2.xml
threshold: 50
fail: false
@@ -71,6 +96,191 @@ jobs:
diff-storage: _xml_coverage_reports_4
coverage-summary-title: "Code Coverage Summary (test-missing-lines.xml) without detailed coverage"
exclude-detailed-coverage: true
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
+ # New test cases for uncovered-statements-increase-failure
+ - path: test-uncovered-statements-increase.xml
+ threshold: 50
+ fail: false
+ publish: false
+ diff: true
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_uncovered_increase
+ coverage-summary-title: "Code Coverage Summary (uncovered statements increase)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: true
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
+ # New test cases for new-uncovered-statements-failure
+ - path: test-new-uncovered-statements.xml
+ threshold: 50
+ fail: false
+ publish: false
+ diff: true
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_new_uncovered
+ coverage-summary-title: "Code Coverage Summary (new uncovered statements)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: true
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
+ # New test cases for coverage-rate-reduction-failure
+ - path: test-coverage-rate-reduction.xml
+ threshold: 50
+ fail: false
+ publish: false
+ diff: true
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_rate_reduction
+ coverage-summary-title: "Code Coverage Summary (coverage rate reduction)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: true
+ pycobertura-exception-failure: true
+ togglable-report: false
+ # New test cases for togglable-report
+ - path: test-togglable-report.xml
+ threshold: 80
+ fail: false
+ publish: true
+ diff: false
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_togglable
+ coverage-summary-title: "Code Coverage Summary (togglable report)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: true
+ # Test case with multiple failure modes enabled
+ - path: test-branch.xml
+ threshold: 80
+ fail: true
+ publish: false
+ diff: true
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_multiple_failures
+ coverage-summary-title: "Code Coverage Summary (multiple failure modes)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: true
+ new-uncovered-statements-failure: true
+ coverage-rate-reduction-failure: true
+ pycobertura-exception-failure: true
+ togglable-report: false
+ # Test case with togglable report and excluded detailed coverage
+ - path: test-togglable-report.xml
+ threshold: 80
+ fail: false
+ publish: true
+ diff: false
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_togglable_excluded
+ coverage-summary-title: "Code Coverage Summary (togglable report, excluded details)"
+ exclude-detailed-coverage: true
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: true
+ # Test case for pycobertura exception (diff scenario)
+ - path: test-pycobertura-exception.xml
+ threshold: 50
+ fail: false
+ publish: false
+ diff: true
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_exception
+ coverage-summary-title: "Code Coverage Summary (pycobertura exception flag enabled)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
+ # Test case for improved coverage (should not fail coverage-rate-reduction-failure)
+ - path: test-coverage-improved.xml
+ threshold: 80
+ fail: false
+ publish: false
+ diff: true
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_improved
+ coverage-summary-title: "Code Coverage Summary (improved coverage)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: true
+ pycobertura-exception-failure: true
+ togglable-report: false
+ # Test case for no uncovered statements (should not fail new-uncovered-statements-failure)
+ - path: test-no-uncovered-statements.xml
+ threshold: 100
+ fail: false
+ publish: false
+ diff: true
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_no_uncovered
+ coverage-summary-title: "Code Coverage Summary (no uncovered statements)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: true
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
+ # Test case for decreased uncovered statements (should not fail uncovered-statements-increase-failure)
+ - path: test-uncovered-statements-decreased.xml
+ threshold: 90
+ fail: false
+ publish: false
+ diff: true
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_decreased_uncovered
+ coverage-summary-title: "Code Coverage Summary (decreased uncovered statements)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: true
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: false
+ # Test case for comprehensive scenario with multiple files
+ - path: test-comprehensive.xml
+ threshold: 70
+ fail: false
+ publish: true
+ diff: true
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_comprehensive
+ coverage-summary-title: "Code Coverage Summary (comprehensive test)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: true
+ togglable-report: true
+ # Test case for exception disabled
+ - path: test-exception-disabled.xml
+ threshold: 80
+ fail: false
+ publish: false
+ diff: false
+ diff-branch: main
+ diff-storage: _xml_coverage_reports_exception_disabled
+ coverage-summary-title: "Code Coverage Summary (exception disabled)"
+ exclude-detailed-coverage: false
+ uncovered-statements-increase-failure: false
+ new-uncovered-statements-failure: false
+ coverage-rate-reduction-failure: false
+ pycobertura-exception-failure: false
+ togglable-report: false
steps:
- name: Checkout Code 🛎
uses: actions/checkout@v4
@@ -87,3 +297,8 @@ jobs:
diff-storage: ${{ matrix.reports.diff-storage }}
coverage-summary-title: ${{ matrix.reports.coverage-summary-title }}
exclude-detailed-coverage: ${{ matrix.reports.exclude-detailed-coverage }}
+ uncovered-statements-increase-failure: ${{ matrix.reports.uncovered-statements-increase-failure }}
+ new-uncovered-statements-failure: ${{ matrix.reports.new-uncovered-statements-failure }}
+ coverage-rate-reduction-failure: ${{ matrix.reports.coverage-rate-reduction-failure }}
+ pycobertura-exception-failure: ${{ matrix.reports.pycobertura-exception-failure }}
+ togglable-report: ${{ matrix.reports.togglable-report }}
diff --git a/README.md b/README.md
index 1e80ffd..1b13df8 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,20 @@
+# Code Coverage Report Action
+
[](https://github.com/insightsengineering/coverage-action/actions/workflows/lint.yaml)
[](https://github.com/insightsengineering/coverage-action/actions/workflows/test.yaml)
-
-# Code Coverage Report Action
-
-### Description
Action that converts a Cobertura XML report into a markdown report.
-### Action Type
+
+## Action Type
+
Composite
-### Author
+## Author
+
Inisghts Engineering
-### Inputs
+## Inputs
+
* `token`:
_Description_: Github token to use to publish the check.
@@ -93,9 +95,9 @@ Inisghts Engineering
_Default_: `Code Coverage Summary`
-* `coverage-reduction-failure`:
+* `uncovered-statements-increase-failure`:
- _Description_: Fail the action if code coverage decreased compared to the `diff-branch` is decreased.
+ _Description_: Fail the action if any changed file has an increase in uncovered lines compared to the `diff-branch`. This corresponds to pycobertura exit code 2, which indicates that at least one changed file has more uncovered lines than before (Miss > 0). Note that this is different from coverage rate reduction - it specifically checks for increases in the absolute number of uncovered lines.
_Required_: `false`
@@ -103,7 +105,15 @@ Inisghts Engineering
* `new-uncovered-statements-failure`:
- _Description_: Fail the action if any new uncovered statements are introduced compared to the `diff-branch`.
+ _Description_: Fail the action if new uncovered statements are introduced AND overall coverage improved (total uncovered lines decreased) compared to the `diff-branch`. This corresponds to pycobertura exit code 3, which only occurs when total uncovered lines decreased (Miss <= 0) but there are still new uncovered statements (Missing != []). To fail on ALL new uncovered statements regardless of overall coverage improvement, use this flag together with `uncovered-statements-increase-failure: true`.
+
+ _Required_: `false`
+
+ _Default_: `False`
+
+* `coverage-rate-reduction-failure`:
+
+ _Description_: Fail the action if the overall coverage percentage (rate) decreases compared to the `diff-branch`. This is different from `uncovered-statements-increase-failure` which checks for absolute increases in uncovered lines. This flag specifically looks at the coverage percentage and fails if it goes down, regardless of whether uncovered lines increased or decreased. This is a more forgiving approach that focuses on the relative coverage rate rather than absolute uncovered line counts.
_Required_: `false`
@@ -132,7 +142,6 @@ The detailed coverage report contains the following information per file:
number of code statements, number of statements not covered by any test,
coverage percentage, and line numbers not covered by any test.
-
_Required_: `false`
_Default_: `False`
@@ -142,12 +151,57 @@ coverage percentage, and line numbers not covered by any test.
* `summary`:
_Description_: A summary of coverage report
-
## How it works
This tool makes use of the [PyCobertura](https://github.com/aconrad/pycobertura) CLI tool to produce the summary outputs. The action also supports `diff`s against a given branch and makes use of a remote branch to store reports, which can be specified via this action.
+## Failure Modes
+
+The action provides three different failure modes for detecting coverage regressions, each with different characteristics:
+
+### 1. `uncovered-statements-increase-failure` (Strict)
+
+* **When it fails**: When any changed file has an increase in the absolute number of uncovered lines
+* **Pycobertura exit code**: 2
+* **Use case**: When you want to ensure that no file gets worse coverage, regardless of overall improvements
+* **Example**: If you add 5 uncovered lines to file A but remove 10 uncovered lines from file B, this will still fail because file A got worse
+
+### 2. `new-uncovered-statements-failure` (Moderate)
+
+* **When it fails**: When new uncovered statements are introduced AND overall coverage improved
+* **Pycobertura exit code**: 3
+* **Use case**: When you want to allow overall improvements but still catch new uncovered code
+* **Example**: If you add 5 uncovered lines to file A but remove 10 uncovered lines from file B, this will fail because there are new uncovered statements, even though overall coverage improved
+
+### 3. `coverage-rate-reduction-failure` (Forgiving)
+
+* **When it fails**: When the overall coverage percentage decreases
+* **Pycobertura exit code**: N/A (custom implementation)
+* **Use case**: When you want to focus on the overall coverage rate rather than absolute line counts
+* **Example**: If you add 5 uncovered lines to file A but remove 10 uncovered lines from file B, this will pass if the overall coverage percentage improved
+
+### Combining Failure Modes
+
+You can combine these failure modes for different levels of strictness:
+
+```yaml
+# Most strict: Fail on any uncovered line increase
+uncovered-statements-increase-failure: true
+new-uncovered-statements-failure: true
+coverage-rate-reduction-failure: true
+
+# Moderate: Allow overall improvements but catch new uncovered code
+uncovered-statements-increase-failure: false
+new-uncovered-statements-failure: true
+coverage-rate-reduction-failure: true
+
+# Most forgiving: Only fail if overall coverage percentage decreases
+uncovered-statements-increase-failure: false
+new-uncovered-statements-failure: false
+coverage-rate-reduction-failure: true
+```
+
## Usage
Example usage:
@@ -206,6 +260,13 @@ jobs:
# A custom title that can be added to the code
# coverage summary in the PR comment.
coverage-summary-title: "Code Coverage Summary"
+ # Failure modes for coverage regression detection:
+ # Fail if any changed file has more uncovered lines (pycobertura exit code 2)
+ uncovered-statements-increase-failure: false
+ # Fail if new uncovered statements are introduced despite overall improvement (pycobertura exit code 3)
+ new-uncovered-statements-failure: false
+ # Fail if the overall coverage percentage decreases (more forgiving approach)
+ coverage-rate-reduction-failure: true
```
An example of the output of the action can be seen below:
diff --git a/action.yml b/action.yml
index e9cc3e1..471600b 100644
--- a/action.yml
+++ b/action.yml
@@ -45,12 +45,32 @@ inputs:
description: Title for the code coverage summary in the Pull Request comment.
required: false
default: "Code Coverage Summary"
- coverage-reduction-failure:
- description: Fail the action if code coverage decreased compared to the `diff-branch` is decreased.
+ uncovered-statements-increase-failure:
+ description: |
+ Fail the action if any changed file has an increase in uncovered lines compared to the `diff-branch`.
+ This corresponds to pycobertura exit code 2, which indicates that at least one changed file has
+ more uncovered lines than before (Miss > 0). Note that this is different from coverage rate
+ reduction - it specifically checks for increases in the absolute number of uncovered lines.
required: false
default: false
new-uncovered-statements-failure:
- description: Fail the action if any new uncovered statements are introduced compared to the `diff-branch`.
+ description: |
+ Fail the action if new uncovered statements are introduced AND overall coverage improved
+ (total uncovered lines decreased) compared to the `diff-branch`.
+ This corresponds to pycobertura exit code 3, which only occurs when total uncovered lines
+ decreased (Miss <= 0) but there are still new uncovered statements (Missing != []).
+ To fail on ALL new uncovered statements regardless of overall coverage improvement,
+ use this flag together with `coverage-reduction-failure: true`.
+ required: false
+ default: false
+ coverage-rate-reduction-failure:
+ description: |
+ Fail the action if the overall coverage percentage (rate) decreases compared to the `diff-branch`.
+ This is different from `uncovered-statements-increase-failure` which checks for absolute
+ increases in uncovered lines. This flag specifically looks at the coverage percentage
+ and fails if it goes down, regardless of whether uncovered lines increased or decreased.
+ This is a more forgiving approach that focuses on the relative coverage rate rather than
+ absolute uncovered line counts.
required: false
default: false
pycobertura-exception-failure:
@@ -75,7 +95,7 @@ outputs:
description: Summary of coverage report
value: ${{ steps.create-output.outputs.summary }}
-branding: # https://feathericons.com/
+branding: # https://feathericons.com/
icon: "umbrella"
color: "red"
@@ -193,6 +213,24 @@ runs:
fi
shell: bash
+ - name: Extract diff-branch coverage percentage
+ if: contains(inputs.diff, 'true') && contains(inputs.coverage-rate-reduction-failure, 'true')
+ run: |
+ if [[ -f "${{ inputs.diff-storage }}/data/${{ inputs.diff-branch }}/${{ inputs.storage-subdirectory }}/coverage.xml" ]]
+ then {
+ # Generate a text report for the diff-branch coverage
+ pycobertura show ${{ inputs.diff-storage }}/data/${{ inputs.diff-branch }}/${{ inputs.storage-subdirectory }}/coverage.xml --output .coverage-output.diff-branch
+ # Extract the total coverage percentage
+ grep -E "^TOTAL " .coverage-output.diff-branch | \
+ awk '{print $NF}' | tr -d '%' > .coverage-total.diff-branch
+ echo "Diff-branch coverage: $(cat .coverage-total.diff-branch)%"
+ } else {
+ echo "Diff-branch coverage file not found, cannot compare coverage rates."
+ echo "0" > .coverage-total.diff-branch
+ }
+ fi
+ shell: bash
+
- name: Get total
run: |
grep -E "^TOTAL " .coverage-output | \
@@ -369,15 +407,15 @@ runs:
- name: Fail if coverage worsened
if: contains(inputs.diff, 'true')
run: |
- if [[ "${{ env.pycobertura_status }}" == "3" && "${{ inputs.coverage-reduction-failure }}" == "true" ]]
+ if [[ "${{ env.pycobertura_status }}" == "2" && "${{ inputs.uncovered-statements-increase-failure }}" == "true" ]]
then {
- echo "Code changes worsened the overall coverage."
+ echo "Code changes increased the number of uncovered lines in at least one file."
exit 1
}
fi
- if [[ "${{ env.pycobertura_status }}" == "2" && "${{ inputs.new-uncovered-statements-failure }}" == "true" ]]
+ if [[ "${{ env.pycobertura_status }}" == "3" && "${{ inputs.new-uncovered-statements-failure }}" == "true" ]]
then {
- echo "Code changes increased the number of uncovered statements."
+ echo "Code changes introduced new uncovered statements despite overall coverage improvement."
exit 1
}
fi
@@ -387,6 +425,18 @@ runs:
exit 1
}
fi
+ if [[ "${{ inputs.coverage-rate-reduction-failure }}" == "true" && -f .coverage-total.diff-branch ]]
+ then {
+ current_coverage=$(cat .coverage-total)
+ diff_branch_coverage=$(cat .coverage-total.diff-branch)
+ if (( $(echo "$current_coverage < $diff_branch_coverage" | bc -l) ))
+ then {
+ echo "Coverage rate decreased from ${diff_branch_coverage}% to ${current_coverage}%."
+ exit 1
+ }
+ fi
+ }
+ fi
shell: bash
- name: Clean up intermediate files
@@ -394,5 +444,6 @@ runs:
run: |
rm -rf coverage-action .coverage-output.final .coverage-output \
.coverage-total .coverage-output.diff \
+ .coverage-output.diff-branch .coverage-total.diff-branch \
${{ inputs.diff-storage }}-not-found
shell: bash
diff --git a/fixtures/README.md b/fixtures/README.md
new file mode 100644
index 0000000..5c82408
--- /dev/null
+++ b/fixtures/README.md
@@ -0,0 +1,127 @@
+# Test Fixtures Documentation
+
+This directory contains test fixtures for the Code Coverage Report Action. Each fixture is designed to test specific scenarios and feature flags.
+
+## Original Test Fixtures
+
+### `test-branch.xml`
+- **Purpose**: Tests basic branch coverage functionality
+- **Coverage**: 90% line coverage, 75% branch coverage
+- **Features**: Contains multiple classes with varying coverage levels
+
+### `test-missing-lines.xml` / `test-missing-lines-2.xml`
+- **Purpose**: Tests scenarios with missing lines in different patterns
+- **Coverage**: 51.25% line coverage
+- **Features**: Multiple files with different missing line patterns (start, end, gaps, etc.)
+
+### `test-no-branch.xml` / `test-no-branch-2.xml`
+- **Purpose**: Tests scenarios without branch coverage data
+- **Coverage**: Varies by file
+- **Features**: Simple line coverage without branch information
+
+### `test-python.xml`
+- **Purpose**: Tests Python-specific coverage format
+- **Coverage**: 90% line coverage
+- **Features**: Generated by coverage.py
+
+## New Test Fixtures for Feature Flags
+
+### Failure Mode Testing
+
+#### `test-uncovered-statements-increase.xml`
+- **Purpose**: Tests `uncovered-statements-increase-failure` flag
+- **Coverage**: 70% line coverage
+- **Scenario**: Represents increased uncovered statements (pycobertura exit code 2)
+- **Expected**: Should fail when flag is enabled
+
+#### `test-uncovered-statements-decreased.xml`
+- **Purpose**: Tests `uncovered-statements-increase-failure` flag (positive case)
+- **Coverage**: 90% line coverage
+- **Scenario**: Represents decreased uncovered statements
+- **Expected**: Should pass when flag is enabled
+
+#### `test-new-uncovered-statements.xml`
+- **Purpose**: Tests `new-uncovered-statements-failure` flag
+- **Coverage**: 80% line coverage
+- **Scenario**: Represents new uncovered statements with overall improvement (pycobertura exit code 3)
+- **Expected**: Should fail when flag is enabled
+
+#### `test-no-uncovered-statements.xml`
+- **Purpose**: Tests `new-uncovered-statements-failure` flag (positive case)
+- **Coverage**: 100% line coverage
+- **Scenario**: Represents no uncovered statements
+- **Expected**: Should pass when flag is enabled
+
+#### `test-coverage-rate-reduction.xml`
+- **Purpose**: Tests `coverage-rate-reduction-failure` flag
+- **Coverage**: 60% line coverage
+- **Scenario**: Represents reduced coverage rate compared to baseline
+- **Expected**: Should fail when flag is enabled
+
+#### `test-coverage-improved.xml`
+- **Purpose**: Tests `coverage-rate-reduction-failure` flag (positive case)
+- **Coverage**: 90% line coverage
+- **Scenario**: Represents improved coverage rate compared to baseline
+- **Expected**: Should pass when flag is enabled
+
+### Exception Testing
+
+#### `test-pycobertura-exception.xml`
+- **Purpose**: Tests `pycobertura-exception-failure` flag in diff scenarios
+- **Coverage**: 80% line coverage
+- **Scenario**: Normal coverage report with diff enabled and exception flag enabled
+- **Expected**: Tests that the flag is properly handled during diff operations
+
+#### `test-exception-disabled.xml`
+- **Purpose**: Tests `pycobertura-exception-failure` flag (disabled case)
+- **Coverage**: 85% line coverage
+- **Scenario**: Normal coverage report
+- **Expected**: Should pass when flag is disabled
+
+### Report Format Testing
+
+#### `test-togglable-report.xml`
+- **Purpose**: Tests `togglable-report` flag
+- **Coverage**: 85% line coverage
+- **Scenario**: Normal coverage report
+- **Expected**: Should generate collapsible report format when flag is enabled
+
+### Comprehensive Testing
+
+#### `test-comprehensive.xml`
+- **Purpose**: Tests multiple feature flags together
+- **Coverage**: 75% line coverage across multiple files
+- **Scenario**: Contains three files with high, medium, and low coverage
+- **Features**: Tests complex scenarios with multiple files and varying coverage levels
+
+### Baseline Testing
+
+#### `test-baseline-main.xml`
+- **Purpose**: Provides baseline coverage for diff testing
+- **Coverage**: 80% line coverage
+- **Scenario**: Represents the "main" branch coverage for comparison
+- **Features**: Used as reference point for diff-based failure mode testing
+
+## Test Matrix Coverage
+
+The test matrix in `.github/workflows/test.yaml` includes test cases for:
+
+1. **Basic functionality**: Original test fixtures with new flags set to defaults
+2. **Individual failure modes**: Each failure flag tested in isolation
+3. **Positive cases**: Scenarios where failure flags should NOT trigger
+4. **Combined scenarios**: Multiple flags enabled together
+5. **Exception handling**: Both enabled and disabled exception failure modes
+6. **Report formatting**: Togglable reports with and without detailed coverage
+7. **Edge cases**: Comprehensive testing with multiple files and varying coverage
+
+## Usage
+
+Each test fixture is used in the GitHub Actions test matrix to verify that:
+
+- Feature flags work correctly in isolation
+- Feature flags work correctly when combined
+- Positive and negative scenarios are handled properly
+- Exception handling works as expected
+- Report formatting options function correctly
+
+The test fixtures are designed to be realistic and cover the various scenarios that users might encounter when using the coverage action with different configurations.
\ No newline at end of file
diff --git a/fixtures/test-baseline-main.xml b/fixtures/test-baseline-main.xml
new file mode 100644
index 0000000..0c8f114
--- /dev/null
+++ b/fixtures/test-baseline-main.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-comprehensive.xml b/fixtures/test-comprehensive.xml
new file mode 100644
index 0000000..5e4e607
--- /dev/null
+++ b/fixtures/test-comprehensive.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-coverage-improved.xml b/fixtures/test-coverage-improved.xml
new file mode 100644
index 0000000..68b6027
--- /dev/null
+++ b/fixtures/test-coverage-improved.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-coverage-rate-reduction.xml b/fixtures/test-coverage-rate-reduction.xml
new file mode 100644
index 0000000..8ffb95c
--- /dev/null
+++ b/fixtures/test-coverage-rate-reduction.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-exception-disabled.xml b/fixtures/test-exception-disabled.xml
new file mode 100644
index 0000000..b21e40d
--- /dev/null
+++ b/fixtures/test-exception-disabled.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-new-uncovered-statements.xml b/fixtures/test-new-uncovered-statements.xml
new file mode 100644
index 0000000..7e5cc3a
--- /dev/null
+++ b/fixtures/test-new-uncovered-statements.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-no-uncovered-statements.xml b/fixtures/test-no-uncovered-statements.xml
new file mode 100644
index 0000000..64b2913
--- /dev/null
+++ b/fixtures/test-no-uncovered-statements.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-pycobertura-exception.xml b/fixtures/test-pycobertura-exception.xml
new file mode 100644
index 0000000..66969ae
--- /dev/null
+++ b/fixtures/test-pycobertura-exception.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-togglable-report.xml b/fixtures/test-togglable-report.xml
new file mode 100644
index 0000000..05dde5d
--- /dev/null
+++ b/fixtures/test-togglable-report.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-uncovered-statements-decreased.xml b/fixtures/test-uncovered-statements-decreased.xml
new file mode 100644
index 0000000..a54c4e6
--- /dev/null
+++ b/fixtures/test-uncovered-statements-decreased.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fixtures/test-uncovered-statements-increase.xml b/fixtures/test-uncovered-statements-increase.xml
new file mode 100644
index 0000000..82f1458
--- /dev/null
+++ b/fixtures/test-uncovered-statements-increase.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file