Skip to content

Commit 39ef13e

Browse files
authored
Add code coverage reporting to GitHub Actions workflow (#2044)
1 parent 479bbcf commit 39ef13e

File tree

8 files changed

+160
-13
lines changed

8 files changed

+160
-13
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: added
3+
4+
Add code coverage reporting to GitHub Actions PHPUnit workflow with dedicated coverage job using Xdebug

.github/workflows/phpunit.yml

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,133 @@ jobs:
5151
env:
5252
PHP_VERSION: ${{ matrix.php-versions }}
5353
WP_ENVIRONMENT_TYPE: production
54+
55+
coverage:
56+
runs-on: ubuntu-latest
57+
services:
58+
mysql:
59+
image: mariadb:10.4
60+
env:
61+
MYSQL_ROOT_PASSWORD: root
62+
ports:
63+
- 3306:3306
64+
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=10s --health-retries=10
65+
steps:
66+
- name: Install svn
67+
run: |
68+
sudo apt-get update
69+
sudo apt-get install subversion
70+
- name: Checkout
71+
uses: actions/checkout@v5
72+
73+
- name: Setup PHP with Xdebug
74+
uses: shivammathur/setup-php@v2
75+
with:
76+
php-version: '8.4'
77+
coverage: xdebug
78+
tools: composer, phpunit-polyfills, cs2pr
79+
extensions: mysql
80+
81+
- name: Install Composer dependencies for PHP
82+
uses: ramsey/composer-install@v3
83+
84+
- name: Setup Test Environment
85+
run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 latest
86+
87+
- name: Unit Testing with Coverage
88+
run: ./vendor/bin/phpunit --coverage-text --coverage-clover coverage.xml --log-junit junit.xml 2>&1 | tee coverage-output.log
89+
env:
90+
WP_ENVIRONMENT_TYPE: production
91+
92+
- name: Generate Checkstyle Report for Coverage Warnings
93+
if: always()
94+
run: |
95+
echo "Generating checkstyle report for coverage warnings..."
96+
97+
# Create checkstyle XML header
98+
cat > coverage-warnings.xml << 'EOF'
99+
<?xml version="1.0" encoding="UTF-8"?>
100+
<checkstyle version="4.3">
101+
EOF
102+
103+
# Check for PHPUnit warnings and process them
104+
if grep -q "WARNINGS!\|There was.*warning" coverage-output.log; then
105+
echo "Processing coverage warnings for checkstyle report..."
106+
107+
# Show warnings section for debugging
108+
echo "--- Warnings Found ---"
109+
sed -n '/There was.*warning/,/WARNINGS!/p' coverage-output.log | head -10
110+
echo "--- End Warnings ---"
111+
112+
# Extract warnings to temporary file to avoid subshell issues
113+
sed -n '/There was.*warning/,/WARNINGS!/p' coverage-output.log > temp-warnings.txt
114+
115+
# Process each line
116+
while IFS= read -r line; do
117+
echo "Processing line: $line"
118+
# Look for @covers warnings
119+
if [[ "$line" =~ \"@covers.*\"\ is\ invalid ]]; then
120+
echo "Found @covers warning line: $line"
121+
# Extract the @covers target - everything between quotes
122+
if [[ "$line" =~ \"@covers\ ([^\"]+)\" ]]; then
123+
COVERS_TARGET="${BASH_REMATCH[1]}"
124+
echo "Processing @covers warning for: $COVERS_TARGET"
125+
126+
# Find test file and line number
127+
echo "Searching for @covers annotation with target: $COVERS_TARGET"
128+
# Extract just the method name from the full class::method
129+
METHOD_NAME=$(echo "$COVERS_TARGET" | sed 's/.*:://')
130+
echo "Extracted method name: $METHOD_NAME"
131+
if TEST_FILE=$(find tests/ -name "*.php" -type f -exec grep -l "@covers.*$METHOD_NAME" {} \; | head -1); then
132+
echo "Found test file: $TEST_FILE"
133+
if LINE_NUM=$(grep -n "@covers.*$METHOD_NAME" "$TEST_FILE" | head -1 | cut -d: -f1); then
134+
echo "Found line number: $LINE_NUM"
135+
# Escape XML characters and add checkstyle entry
136+
MESSAGE=$(echo "Invalid @covers annotation: $COVERS_TARGET" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g; s/'"'"'/\&#39;/g')
137+
cat >> coverage-warnings.xml << EOF
138+
<file name="$TEST_FILE">
139+
<error line="$LINE_NUM" column="1" severity="warning" message="$MESSAGE" source="phpunit.coverage"/>
140+
</file>
141+
EOF
142+
fi
143+
fi
144+
fi
145+
# Look for deprecation warnings
146+
elif [[ "$line" =~ (deprecated|will\ no\ longer\ be\ possible) ]]; then
147+
echo "::warning::PHPUnit deprecation warning: ${line}"
148+
fi
149+
done < temp-warnings.txt
150+
151+
rm -f temp-warnings.txt
152+
WARNINGS_FOUND=true
153+
else
154+
echo "No coverage warnings found"
155+
WARNINGS_FOUND=false
156+
fi
157+
158+
# Close the checkstyle XML
159+
echo '</checkstyle>' >> coverage-warnings.xml
160+
161+
# Debug: show the generated checkstyle content
162+
echo "Generated checkstyle report:"
163+
cat coverage-warnings.xml
164+
165+
# Exit with error if warnings were found
166+
if [ "$WARNINGS_FOUND" = true ]; then
167+
echo "::error::Coverage analysis produced warnings"
168+
exit 1
169+
fi
170+
171+
- name: Annotate Coverage Warnings in PR
172+
if: always() && github.event_name == 'pull_request'
173+
run: |
174+
# Check if we have any errors in the checkstyle report
175+
if grep -q '<error' coverage-warnings.xml; then
176+
echo "Found coverage warnings to annotate"
177+
echo "Converting checkstyle report to PR annotations..."
178+
cs2pr --graceful-warnings coverage-warnings.xml
179+
else
180+
echo "No coverage warnings found in checkstyle report"
181+
fi
182+
env:
183+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

docs/development-environment.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ The coverage configuration is already set up in `phpunit.xml.dist` to analyze th
104104

105105
```bash
106106
# Start the environment with Xdebug enabled.
107-
npm run env -- start --xdebug=coverage
107+
npm run env-start -- --xdebug=coverage
108108
```
109109
```bash
110110
# Run tests with code coverage.

includes/activity/class-generic-object.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
* @method Base_Object set_summary( string $summary ) Sets the natural language summary of the object.
6262
* @method Base_Object set_summary_map( array|null $summary_map ) Sets the summary property of the object.
6363
* @method Base_Object set_target( string $target ) Sets the target property of the object.
64-
* @method Base_Object set_to( array|string $to ) Sets the primary recipients of the object.
64+
* @method Base_Object set_to( string|string[] $to ) Sets the primary recipients of the object.
6565
* @method Base_Object set_type( string $type ) Sets the type of the object.
6666
* @method Base_Object set_updated( string $updated ) Sets the date and time the object was updated in ISO 8601 format.
6767
* @method Base_Object set_url( string $url ) Sets the URL of the object.

tests/includes/class-test-activitypub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ public function test_prevent_empty_post_meta() {
321321
/**
322322
* Test get_post_metadata method.
323323
*
324-
* @covers ::get_post_metadata
324+
* @covers ::default_post_metadata
325325
*/
326326
public function test_get_post_metadata() {
327327
// Create a test post.

tests/includes/class-test-functions.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ public function provide_wp_versions() {
569569
* @dataProvider get_post_summary_data
570570
*
571571
* @param string $desc The description of the test.
572-
* @param object $post The post object.
572+
* @param array $post The post object.
573573
* @param string $expected The expected summary.
574574
* @param int $length The length of the summary.
575575
*/
@@ -714,7 +714,7 @@ public function test_get_user_id() {
714714
/**
715715
* Tests follow method.
716716
*
717-
* @covers ::follow
717+
* @covers \Activitypub\follow
718718
*/
719719
public function test_follow() {
720720
$user_id = self::factory()->user->create(
@@ -758,7 +758,7 @@ public function test_follow() {
758758
/**
759759
* Test that Update activities have the updated attribute set.
760760
*
761-
* @covers ::add_to_outbox
761+
* @covers \Activitypub\add_to_outbox
762762
*/
763763
public function test_webfinger_support() {
764764
$follow = new Activity();
@@ -811,7 +811,7 @@ public function test_webfinger_support() {
811811
*
812812
* @dataProvider data_normalize_url
813813
*
814-
* @covers ::normalize_url
814+
* @covers \Activitypub\normalize_url
815815
*
816816
* @param string $url The URL.
817817
* @param string $expected The expected result.
@@ -842,7 +842,7 @@ public function data_normalize_url() {
842842
*
843843
* @dataProvider data_normalize_host
844844
*
845-
* @covers ::normalize_host
845+
* @covers \Activitypub\normalize_host
846846
*
847847
* @param string $host The host.
848848
* @param string $expected The expected result.

tests/includes/class-test-moderation.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ public function test_get_user_blocks_empty() {
202202
*
203203
* @covers ::add_site_block
204204
* @covers ::get_site_blocks
205-
* @covers ::get_site_option_key_for_type
206205
*/
207206
public function test_add_site_block() {
208207
$this->assertTrue( Moderation::add_site_block( 'domain', 'spam-instance.com' ) );
@@ -375,6 +374,7 @@ public function test_hierarchical_blocking() {
375374
* Test edge cases with malformed activity data.
376375
*
377376
* @covers ::activity_is_blocked
377+
* @throws \Exception Thrown when an error occurs.
378378
*/
379379
public function test_activity_blocking_edge_cases() {
380380
// Test with empty activity.
@@ -434,8 +434,24 @@ public function test_activity_blocking_edge_cases() {
434434
),
435435
)
436436
);
437-
$this->expectError();
437+
438+
// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
439+
\set_error_handler(
440+
static function ( $errno, $errstr ) {
441+
throw new \Exception( \esc_html( $errstr ), \esc_html( $errno ) );
442+
},
443+
E_NOTICE | E_WARNING
444+
);
445+
446+
// PHP 7.2 uses "Undefined index", PHP 8+ uses "Undefined array key".
447+
if ( version_compare( PHP_VERSION, '8.0.0', '>=' ) ) {
448+
$this->expectExceptionMessage( 'Undefined array key &quot;id&quot;' );
449+
} else {
450+
$this->expectExceptionMessage( 'Undefined index: id' );
451+
}
438452
$this->assertFalse( Moderation::activity_is_blocked( $activity ) );
453+
454+
\restore_error_handler();
439455
}
440456

441457
/**

tests/includes/class-test-signature.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,6 @@ function () use ( $keys ) {
370370
* @covers \Activitypub\Signature\Http_Message_Signature::parse_signature_labels
371371
* @covers \Activitypub\Signature\Http_Message_Signature::verify_signature_label
372372
* @covers \Activitypub\Signature\Http_Message_Signature::verify_content_digest
373-
* @covers \Activitypub\Signature\Http_Message_Signature::resolve_algorithm
374373
* @covers \Activitypub\Signature\Http_Message_Signature::get_signature_base_string
375374
*/
376375
public function test_verify_http_signature_rfc9421() {
@@ -491,7 +490,6 @@ function ( $response, $parsed_args, $url ) {
491490
* @covers \Activitypub\Signature\Http_Message_Signature::parse_signature_labels
492491
* @covers \Activitypub\Signature\Http_Message_Signature::verify_signature_label
493492
* @covers \Activitypub\Signature\Http_Message_Signature::verify_content_digest
494-
* @covers \Activitypub\Signature\Http_Message_Signature::resolve_algorithm
495493
* @covers \Activitypub\Signature\Http_Message_Signature::get_signature_base_string
496494
*/
497495
public function test_verify_http_signature_rfc9421_get_request() {
@@ -571,7 +569,6 @@ function () use ( $keys ) {
571569
* @covers \Activitypub\Signature\Http_Message_Signature::parse_signature_labels
572570
* @covers \Activitypub\Signature\Http_Message_Signature::verify_signature_label
573571
* @covers \Activitypub\Signature\Http_Message_Signature::verify_content_digest
574-
* @covers \Activitypub\Signature\Http_Message_Signature::resolve_algorithm
575572
* @covers \Activitypub\Signature\Http_Message_Signature::get_signature_base_string
576573
*/
577574
public function test_verify_http_signature_rfc9421_algorithms() {

0 commit comments

Comments
 (0)