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

PoC: PHPUnit support #112

Closed
wants to merge 1 commit into from
Closed

PoC: PHPUnit support #112

wants to merge 1 commit into from

Conversation

adamziel
Copy link
Collaborator

@adamziel adamziel commented Jan 13, 2023

Description

Work in progress. Aims to solves #111

To test, run npx jest ./src/php-wasm/__tests__/php.ts

CleanShot 2023-01-13 at 13 10 48@2x

How does it work?

Here's a step-by-step log of how I arrived at this PR:

Step 0: Get a working wordpress-develop setup

Clone the repo, install composer dependencies, and build WordPress as you normally would.

Step 1: Patch PHPUnit and bootstrap.php

I had to update the following files to get the tests running:

  1. Remove the #!/usr/bin/env php shebang from vendor/bin/phpunit and vendor/phpunit/phpunit/phpunit – WASM SAPI does not understand it
  2. Remove mbstring and xmlwriter from the list of required PHP extensions in vendor/phpunit/phpunit/phpunit - WASM SAPI does not support them yet (although perhaps adding support wouldn't be too hard)
  3. Remove gd from the list of required PHP extensions in test/phpunit/includes/bootstrap.php
  4. Install a drop-in SQLIte plugin for WordPress just like in a regular playground.

Step 2: Package wordpress-develop

Then, I turned wordpress-develop into an emscripten data bundle using the following Dockerfile:

FROM emscripten/emsdk:3.1.24 as emscripten
SHELL ["/bin/bash", "-c"]
WORKDIR /root

RUN mkdir /wordpress /root/output
COPY package-lock.json  phpcompat.xml.dist    webpack.config.js     wp-config.php \
     composer.json  docker-compose.yml  jsdoc.conf.json  package.json       phpcs.xml.dist SECURITY.md  wp-cli.yml wp-tests-config-sample.php \
     composer.lock  Dockerfile          packagehash.txt    phpunit.xml.dist   wp-config-sample.php  wp-tests-config.php \
     /wordpress/
COPY build /wordpress/build
COPY tests /wordpress/tests
COPY vendor /wordpress/vendor
COPY tools /wordpress/tools
RUN cd /wordpress/build && find ./ -type f \( \
    -name '*.eot' -o -name '*.gif' -o -name '*.htaccess' \
    -o -name '*.md' -o -name '*.mp4' -o -name '*.png' \
    -o -name '*.scss' -o -name '*.stylelintignore' -o -name '*.svg' \
    -o -name '*.ttf' -o -name '*.txt' -o -name '*.woff' \
    -o -name '*.woff2' -o -name '*.jpeg' -o -name '*.jpg' \
    -o -name '*.map' \
    \) -delete && \
    find ./ -type f -name '*.css' \
    -not -path '*/wp-includes/blocks/*/*.min.css' \
    -not -path '*/wp-content/themes/*/style.css' \
    -delete && \
    find ./ -type f -name '*-rtl.min.css' -delete && \
    # Keep only the JS files that are read by PHP
    find ./ -type f -name '*.js' \
    -not -path '*/wp-includes/blocks/*/*.min.js' \
    -not -name 'wp-emoji-loader.min.js' \
    -delete && \
    # Remove any empty directories
    find . -type d -empty -delete && \
    # Remove non-essential files
    mv wp-content/themes/twentytwentytwo ./ && \
    rm -r wp-content/themes/* && \
    mv ./twentytwentytwo ./wp-content/themes

RUN du -a /wordpress 2>/dev/null | sort -n -r | head -n 20;

# Bundle the .data file using Emscripten 
RUN /emsdk/upstream/emscripten/tools/file_packager.py \
    /root/output/wp-dev.data \
    --export-name="PHPModule" \
    --no-node \
    --preload /wordpress \
    --js-output=/root/output/wp-dev.js

RUN echo 'export default function(PHPModule) {' > /root/wp-dev-pre.js
RUN echo '}' > /root/wp-dev-post.js
RUN mv /root/output/wp-dev.js /root/wp-dev.js
RUN cat /root/wp-dev-pre.js /root/wp-dev.js /root/wp-dev-post.js > /root/output/wp-dev.js

I then ran it as follows:

mkdir output
docker build . --tag=wordpress-develop-tmp --progress=plain
docker run --name wordpress-develop-tmp-c --rm -v `pwd`/output:/output wordpress-develop-tmp sh -c 'cp -r /root/output/* /output/'

As a result, I got two files: wp-dev.js and wp-dev.data.

Step 3: Build Playground for node.js and with Libxml support

The default WordPress playground build does not support libxml so I re-ran the build:php:node:8.0 script with WITH_LIBXML=yes env variable.

Step 4: Run PHPUnit inside a jest test

Jest is already configured to run a node.js build of PHP which makes it a convenient choice for this PoC. I added the following test:

// wordpress-develop environment
import * as wpLoaderModule from '../../../build/wp-dev.js';
// PHP 8.0 – using 5.6, 8.1, or any other version means just updating this import:
import * as phpLoaderModule from '../../../build/php-8.0.node.js';

describe('WP PHPUnit', () => {
	it.only('should run WordPress phpunit tests', async () => {
		const php = await startPHP(
			phpLoaderModule,
			'NODE',
			{
				// Required to pick up the file from disk – otherwise emscripten will attempt XMLHTTPRequest:
				getPreloadedPackage(filename) {
					return readFileSync(
						__dirname + '/../../../build/' + filename,
						null
					).buffer;
				},
			},
			[wpLoaderModule]
		);
		expect(php).toBeTruthy();
		php.writeFile(
			'/wordpress/tests.php',
			`<?php
			// PHPUnit uses the CLI constants we don't have in WASM SAPI.
			// No problem – let's define them here:
			define('STDERR', fopen('php://stderr', 'w'));
			define('STDOUT', fopen('php://stdout', 'w'));
			define('STDIN', fopen('php://stdin', 'w'));

			// Preconfigure WordPress tests:
			define('WP_RUN_CORE_TESTS', true);
			putenv('WP_TESTS_SKIP_INSTALL=1');

			// Provide CLI args for PHPUnit:
			$_SERVER['argv'] = ['./vendor/bin/phpunit', '-c', './phpunit.xml.dist', '--filter', 'Tests_Formatting_Utf8UriEncode'];
			chdir('/wordpress');

			// Let's go!
			require(__DIR__. "/vendor/bin/phpunit");
			`
		);
		const result = php.run({
			scriptPath: '/wordpress/tests.php',
		});
		console.log(result.errors);
		console.log(
			new TextDecoder().decode(result.body)
		);
	});
});

The output

A few PHP warnings aside, it works nicely!

$ npx jest ./src/php-wasm/__tests__/php.ts 
  console.log
    PHP Warning:  shell_exec(): Unable to execute 'stty size' in /wordpress/vendor/sebastian/environment/src/Console.php on line 136
    PHP Warning:  shell_exec(): Unable to execute 'stty' in /wordpress/vendor/sebastian/environment/src/Console.php on line 142

      at Object.<anonymous> (src/php-wasm/__tests__/<stdin>:43:11)

  console.log
    Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
    Not running ajax tests. To execute these, use --group ajax.
    Not running ms-files tests. To execute these, use --group ms-files.
    Not running external-http tests. To execute these, use --group external-http.
    <br />
    <b>Warning</b>:  shell_exec(): Unable to execute 'stty size' in <b>/wordpress/vendor/sebastian/environment/src/Console.php</b> on line <b>136</b><br />
    <br />
    <b>Warning</b>:  shell_exec(): Unable to execute 'stty' in <b>/wordpress/vendor/sebastian/environment/src/Console.php</b> on line <b>142</b><br />
    PHPUnit 9.5.20 #StandWithUkraine
    
    Warning:       Your XML configuration validates against a deprecated schema.
    Suggestion:    Migrate your XML configuration using &amp;quot;--migrate-configuration&amp;quot;!
    
    ............                                                      12 / 12 (100%)
    
    Time: 00:00.305, Memory: 118.00 MB
    
    OK (12 tests, 12 assertions)

      at Object.<anonymous> (src/php-wasm/__tests__/<stdin>:44:11)

 PASS  src/php-wasm/__tests__/php.ts
  WP PHPUnit
    ✓ should run WordPress phpunit tests (3332 ms)

cc @hellofromtonya

@adamziel
Copy link
Collaborator Author

adamziel commented Jan 13, 2023

There are some test failures in Tests_Canonical_PageOnFront (PHP=8.0):

    PHPUnit 9.5.20 #StandWithUkraine
    
    Warning:       Your XML configuration validates against a deprecated schema.
    Suggestion:    Migrate your XML configuration using &amp;quot;--migrate-configuration&amp;quot;!
    
    ....FFF...                                                        10 / 10 (100%)
    
    Time: 00:01.054, Memory: 120.00 MB
    
    There were 3 failures:
    
    1) Tests_Canonical_PageOnFront::test with data set #4 ('/front-page/', '/', 20385)
    Ticket #20385
    Failed asserting that two strings are identical.
    --- Expected
    +++ Actual
    @@ @@
    -'/'
    +'/front-page/'
    
    /wordpress/tests/phpunit/includes/testcase-canonical.php:311
    /wordpress/tests/phpunit/tests/canonical/pageOnFront.php:39
    /wordpress/vendor/bin/phpunit:119
    /wordpress/tests.php:9
    
    2) Tests_Canonical_PageOnFront::test with data set #5 ('/front-page/2/', '/page/2/', 35344)
    Ticket #35344
    Failed asserting that two strings are identical.
    --- Expected
    +++ Actual
    @@ @@
    -'/page/2/'
    +'/front-page/2/'
    
    /wordpress/tests/phpunit/includes/testcase-canonical.php:311
    /wordpress/tests/phpunit/tests/canonical/pageOnFront.php:39
    /wordpress/vendor/bin/phpunit:119
    /wordpress/tests.php:9
    
    3) Tests_Canonical_PageOnFront::test with data set #6 ('/front-page/?page=2', '/page/2/', 35344)
    Ticket #35344
    Failed asserting that two strings are identical.
    --- Expected
    +++ Actual
    @@ @@
    -'/page/2/'
    +'/front-page/2/'
    
    /wordpress/tests/phpunit/includes/testcase-canonical.php:311
    /wordpress/tests/phpunit/tests/canonical/pageOnFront.php:39
    /wordpress/vendor/bin/phpunit:119
    /wordpress/tests.php:9
    
    FAILURES!
    Tests: 10, Assertions: 10, Failures: 3.

@adamziel
Copy link
Collaborator Author

adamziel commented Jan 14, 2023

php-cli.js seems like a more promising approach as it removes the need for patching PHP files.

@adamziel
Copy link
Collaborator Author

Closing this one in favor of #114

@adamziel adamziel closed this Jan 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant