Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Test Coverage for Web Worker Offloading Plugin #1871

Open
wants to merge 7 commits into
base: trunk
Choose a base branch
from

Conversation

sarthak-19
Copy link
Contributor

@sarthak-19 sarthak-19 commented Feb 14, 2025

Summary

This is part of #1789:

  • Ignore Coverage for Non-Critical Code Blocks
  • Add Missing @covers Annotations
  • Add Missing Tests
Before : 28.00% ⚠️ After: 99.00% ✅
image  image

cc : @westonruter

Copy link

codecov bot commented Feb 14, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 68.01%. Comparing base (d94977f) to head (51a3dc8).
Report is 147 commits behind head on trunk.

Additional details and impacted files
@@            Coverage Diff             @@
##            trunk    #1871      +/-   ##
==========================================
+ Coverage   65.89%   68.01%   +2.12%     
==========================================
  Files          88       86       -2     
  Lines        6878     6985     +107     
==========================================
+ Hits         4532     4751     +219     
+ Misses       2346     2234     -112     
Flag Coverage Δ
multisite 68.01% <100.00%> (+2.12%) ⬆️
single 37.95% <2.32%> (-0.64%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

/**
* Test the function that loads third party plugin integrations.
*
* @covers ::plwwo_load_third_party_integrations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @covers ::plwwo_load_third_party_integrations
* @covers ::plwwo_load_third_party_integrations
* @runInSeparateProcess

Since a constant is not removed after a test completes, it's important to run in a separate process.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When trying to run is a separate process, I'm getting error as Serialization of 'Closure' is not allowed , and I'm not sure how to resolve this.

Attaching error log below :

Runtime:       PHP 8.2.27 with Xdebug 3.4.1
Configuration: /var/www/html/wp-content/plugins/performance/phpunit.xml.dist

..................<script id="web-worker-offloading-js-before">
window.partytown = {...(window.partytown || {}), ...{"lib":"\/wp-content\/plugins\/performance\/plugins\/web-worker-offloading\/build\/"}};
</script>
<script id="web-worker-offloading-js-after">
const t={preserveBehavior:!1},e=e=>{if("string"==typeof e)return[e,t];const[n,r=t]=e;return[n,{...t,...r}]},n=Object.freeze((t=>{const e=new Set;let n=[];do{Object.getOwnPropertyNames(n).forEach((t=>{"function"==typeof n[t]&&e.add(t)}))}while((n=Object.getPrototypeOf(n))!==Object.prototype);return Array.from(e)})());!function(t,r,o,i,a,s,c,d,l,p,u=t,f){function h(){f||(f=1,"/"==(c=(s.lib||"/~partytown/")+(s.debug?"debug/":""))[0]&&(l=r.querySelectorAll('script[type="text/partytown"]'),i!=t?i.dispatchEvent(new CustomEvent("pt1",{detail:t})):(d=setTimeout(v,1e4),r.addEventListener("pt0",w),a?y(1):o.serviceWorker?o.serviceWorker.register(c+(s.swPath||"partytown-sw.js"),{scope:c}).then((function(t){t.active?y():t.installing&&t.installing.addEventListener("statechange",(function(t){"activated"==t.target.state&&y()}))}),console.error):v())))}function y(e){p=r.createElement(e?"script":"iframe"),t._pttab=Date.now(),e||(p.style.display="block",p.style.width="0",p.style.height="0",p.style.border="0",p.style.visibility="hidden",p.setAttribute("aria-hidden",!0)),p.src=c+"partytown-"+(e?"atomics.js?v=0.10.2":"sandbox-sw.html?"+t._pttab),r.querySelector(s.sandboxParent||"body").appendChild(p)}function v(n,o){for(w(),i==t&&(s.forward||[]).map((function(n){const[r]=e(n);delete t[r.split(".")[0]]})),n=0;n<l.length;n++)(o=r.createElement("script")).innerHTML=l[n].innerHTML,o.nonce=s.nonce,r.head.appendChild(o);p&&p.parentNode.removeChild(p)}function w(){clearTimeout(d)}s=t.partytown||{},i==t&&(s.forward||[]).map((function(r){const[o,{preserveBehavior:i}]=e(r);u=t,o.split(".").map((function(e,r,o){var a;u=u[o[r]]=r+1<o.length?u[o[r]]||(a=o[r+1],n.includes(a)?[]:{}):(()=>{let e=null;if(i){const{methodOrProperty:n,thisObject:r}=((t,e)=>{let n=t;for(let t=0;t<e.length-1;t+=1)n=n[e[t]];return{thisObject:n,methodOrProperty:e.length>0?n[e[e.length-1]]:void 0}})(t,o);"function"==typeof n&&(e=(...t)=>n.apply(r,...t))}return function(){let n;return e&&(n=e(arguments)),(t._ptf=t._ptf||[]).push(o,arguments),n}})()}))})),"complete"==r.readyState?h():(t.addEventListener("DOMContentLoaded",h),t.addEventListener("load",h))}(window,document,navigator,top,window.crossOriginIsolated);
</script>
<script type="text/partytown" src="https://example.com/test-script.js?ver=1.0.0" id="test-script-js"></script>
Serialization of 'Closure' is not allowed
Script WP_MULTISITE=1 phpunit --exclude-group=ms-excluded handling the test-multisite event returned with error code 2
Script @test-multisite --verbose --testsuite web-worker-offloading was called via test-multisite:web-worker-offloading
✖ Command failed with exit code 2
Command failed with exit code 2

cc : @westonruter

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I typically see that when @runInSeparateProcess is added for a test that has a closure among the data in the data provider. But this test has no data provider, so I'm confused why it is occuring for this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhkk, so should I keep it as is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to various things to get it to work to run in a separate process but it was too messy. There are closures defined in some global variable in WordPress core, so I tried to remove them during the tests, but I still had trouble. We'll just not test that now I guess.

Patch
commit b1af2d50c95cee24c554b224d0fb06c7f61a34f2
Merge: 10002da03 a287b432a
Author: Weston Ruter <[email protected]>
Date:   Fri Feb 21 13:58:58 2025 +0800

    WIP on add/code_coverage_web_worker: 10002da03 fixup! Fix printing of output during test

diff --cc plugins/web-worker-offloading/tests/test-web-worker-offloading-load-third-party-integrations.php
index 000000000,3d6d7e2b7..d3491eea8
mode 000000,100644..100644
--- a/plugins/web-worker-offloading/tests/test-web-worker-offloading-load-third-party-integrations.php
+++ b/plugins/web-worker-offloading/tests/test-web-worker-offloading-load-third-party-integrations.php
@@@ -1,0 -1,66 +1,67 @@@
+ <?php
+ /**
+  * Test cases for Web Worker Offloading: Load Third Party Integrations
+  *
+  * @package web-worker-offloading
+  * @runInSeparateProcess
+  */
+ 
+ class Test_Web_Worker_Offloading_Load_Third_Party_Integrations extends WP_UnitTestCase {
+ 
+ 	/**
+ 	 * Original $_wp_registered_theme_features global.
+ 	 *
+ 	 * @var array
+ 	 */
 -	static private $original_wp_registered_theme_features;
++	private static $original_wp_registered_theme_features;
+ 
+ 	/**
+ 	 * Runs the routine before setting up all tests.
+ 	 */
+ 	public static function set_up_before_class(): void {
+ 		parent::set_up_before_class();
+ 		self::$original_wp_registered_theme_features = $GLOBALS['_wp_registered_theme_features'];
+ 
+ 		// Empty global which contains a closure since it cannot be serialized to run in a separate process.
+ 		$GLOBALS['_wp_registered_theme_features'] = array();
+ 	}
+ 
+ 	/**
+ 	 * Runs the routine after all tests have been run.
+ 	 */
+ 	public static function tear_down_after_class(): void {
+ 		parent::tear_down_after_class();
+ 		$GLOBALS['_wp_registered_theme_features'] = self::$original_wp_registered_theme_features;
+ 	}
+ 
+ 	/**
 -	 * Test the function doesn't third party plugin integrations when none are active.
++	 * Test the function that loads third party plugin integrations.
+ 	 *
+ 	 * @covers ::plwwo_load_third_party_integrations
++	 *
++	 * @runInSeparateProcess
+ 	 */
 -	public function test_plwwo_load_third_party_integrations_when_none_active(): void {
++	public function test_plwwo_load_third_party_integrations_when_sitekit_active(): void {
++		if ( ! defined( 'GOOGLESITEKIT_VERSION' ) ) {
++			define( 'GOOGLESITEKIT_VERSION', '1.0.0' );
++		}
+ 		do_action( 'plugins_loaded' );
+ 
 -		// Check that the integrations have been loaded.
 -		$this->assertFalse( function_exists( 'plwwo_google_site_kit_configure' ) );
++		$this->assertTrue( function_exists( 'plwwo_google_site_kit_configure' ) );
+ 		$this->assertFalse( function_exists( 'plwwo_rank_math_configure' ) );
+ 		$this->assertFalse( function_exists( 'plwwo_woocommerce_configure' ) );
+ 	}
+ 
+ 	/**
 -	 * Test the function that loads third party plugin integrations.
++	 * Test the function doesn't third party plugin integrations when none are active.
+ 	 *
+ 	 * @covers ::plwwo_load_third_party_integrations
+ 	 */
 -	public function test_plwwo_load_third_party_integrations_when_sitekit_active(): void {
 -		if ( ! defined( 'GOOGLESITEKIT_VERSION' ) ) {
 -			define( 'GOOGLESITEKIT_VERSION', '1.0.0' );
 -		}
++	public function test_plwwo_load_third_party_integrations_when_none_active(): void {
+ 		do_action( 'plugins_loaded' );
+ 
 -		$this->assertTrue( function_exists( 'plwwo_google_site_kit_configure' ) );
++		$this->assertFalse( function_exists( 'plwwo_google_site_kit_configure' ) );
+ 		$this->assertFalse( function_exists( 'plwwo_rank_math_configure' ) );
+ 		$this->assertFalse( function_exists( 'plwwo_woocommerce_configure' ) );
+ 	}
+ }
diff --cc tools/phpunit/bootstrap.php
index b845c92fd,b845c92fd..2be1db391
--- a/tools/phpunit/bootstrap.php
+++ b/tools/phpunit/bootstrap.php
@@@ -42,30 -42,30 +42,31 @@@ if ( false !== strpos( $_test_root, 'wp
  require_once $_test_root . '/includes/functions.php';
  
  // Check if we use the plugin's test suite. If so, disable the PL plugin and only load the requested plugin.
--$plugin_name = '';
++global $test_plugin_name;
++$test_plugin_name = '';
  foreach ( $_SERVER['argv'] as $index => $arg ) {
  	if (
  		'--testsuite' === $arg &&
  		isset( $_SERVER['argv'][ $index + 1 ] ) &&
  		file_exists( TESTS_REPO_ROOT_DIR . '/plugins/' . $_SERVER['argv'][ $index + 1 ] )
  	) {
--		$plugin_name = $_SERVER['argv'][ $index + 1 ];
++		$test_plugin_name = $_SERVER['argv'][ $index + 1 ];
  	}
  }
  
  // Set default plugin to load if not specified.
--if ( '' === $plugin_name ) {
--	$plugin_name = 'performance-lab';
++if ( '' === $test_plugin_name ) {
++	$test_plugin_name = 'performance-lab';
  }
  
--define( 'TESTS_PLUGIN_DIR', TESTS_REPO_ROOT_DIR . "/plugins/$plugin_name" );
++define( 'TESTS_PLUGIN_DIR', TESTS_REPO_ROOT_DIR . "/plugins/$test_plugin_name" );
  
  /**
   * Load plugin bootstrap and any dependencies.
   *
   * @param string $plugin_name Plugin slug to load.
   */
--$load_plugin = static function ( string $plugin_name ) use ( &$load_plugin ): void {
++function test_load_plugin( string $plugin_name ): void {
  	$plugin_test_path = TESTS_REPO_ROOT_DIR . '/plugins/' . $plugin_name;
  	if ( file_exists( $plugin_test_path . '/' . $plugin_name . '.php' ) ) {
  		$plugin_file = $plugin_test_path . '/' . $plugin_name . '.php';
@@@ -78,7 -78,7 +79,7 @@@
  
  	if ( 1 === preg_match( '/^ \* Requires Plugins:\s*(.+)$/m', (string) file_get_contents( $plugin_file ), $matches ) ) {
  		foreach ( (array) preg_split( '/\s*,\s*/', $matches[1] ) as $requires_plugin ) {
--			$load_plugin( (string) $requires_plugin );
++			test_load_plugin( (string) $requires_plugin );
  		}
  	}
  
@@@ -91,42 -91,42 +92,49 @@@
  	require_once $plugin_file;
  };
  
++function test_load_plugin_at_plugins_loaded(): void {
++	global $test_plugin_name;
++	test_load_plugin( $test_plugin_name );
++}
++
  tests_add_filter(
  	'plugins_loaded',
--	static function () use ( $load_plugin, $plugin_name ): void {
--		$load_plugin( $plugin_name );
--	},
++	'test_load_plugin_at_plugins_loaded',
  	1
  );
  
--if ( 'performance-lab' === $plugin_name ) {
++function test_require_performance_lab_includes(): void {
++	require_once TESTS_REPO_ROOT_DIR . '/plugins/performance-lab/includes/admin/load.php';
++	require_once TESTS_REPO_ROOT_DIR . '/plugins/performance-lab/includes/admin/server-timing.php';
++	require_once TESTS_REPO_ROOT_DIR . '/plugins/performance-lab/includes/admin/plugins.php';
++}
++
++if ( 'performance-lab' === $test_plugin_name ) {
  	// Add filter to ensure the plugin's admin integration is loaded for tests.
  	tests_add_filter(
  		'plugins_loaded',
--		static function () use ( $plugin_name ): void {
--			require_once TESTS_REPO_ROOT_DIR . '/plugins/' . $plugin_name . '/includes/admin/load.php';
--			require_once TESTS_REPO_ROOT_DIR . '/plugins/' . $plugin_name . '/includes/admin/server-timing.php';
--			require_once TESTS_REPO_ROOT_DIR . '/plugins/' . $plugin_name . '/includes/admin/plugins.php';
--		},
++		'test_require_performance_lab_includes',
  		1
  	);
  }
  
++function test_clean_up_object_cache_dropins( bool $passthrough ): bool {
++	$cleanup_files = array(
++		WP_CONTENT_DIR . '/object-cache.php',
++		WP_CONTENT_DIR . '/object-cache-plst-orig.php',
++	);
++	foreach ( $cleanup_files as $cleanup_file ) {
++		if ( file_exists( $cleanup_file ) ) {
++			unlink( $cleanup_file );
++		}
++	}
++	return $passthrough;
++}
++
  // Clean up object-cache drop-ins after possible previous failed tests.
  tests_add_filter(
  	'enable_loading_object_cache_dropin',
--	static function ( bool $passthrough ): bool {
--		$cleanup_files = array(
--			WP_CONTENT_DIR . '/object-cache.php',
--			WP_CONTENT_DIR . '/object-cache-plst-orig.php',
--		);
--		foreach ( $cleanup_files as $cleanup_file ) {
--			if ( file_exists( $cleanup_file ) ) {
--				unlink( $cleanup_file );
--			}
--		}
--		return $passthrough;
--	}
++	'test_clean_up_object_cache_dropins'
  );
  
  // Start up the WP testing environment.

@sarthak-19 sarthak-19 marked this pull request as ready for review February 18, 2025 15:11
Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @[email protected].

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: [email protected].

Co-authored-by: westonruter <[email protected]>
Co-authored-by: sarthak-19 <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@westonruter westonruter added [Type] Enhancement A suggestion for improvement of an existing feature skip changelog PRs that should not be mentioned in changelogs [Plugin] Web Worker Offloading Issues for the Web Worker Offloading plugin. labels Feb 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Plugin] Web Worker Offloading Issues for the Web Worker Offloading plugin. skip changelog PRs that should not be mentioned in changelogs [Type] Enhancement A suggestion for improvement of an existing feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants