Skip to content

Commit

Permalink
Env: Download WordPress PHPUnit Into Container (#41780)
Browse files Browse the repository at this point in the history
* Added WordPress PHPUnit Library To `wp-env`

This commit adds support for downloading the
WordPress PHPUnit source and mounting it at
/phpunit in the container. It uses the version
of WordPress installed in the container to
decide what version to download.

* Improved `WordPress/wp-develop` Git Performance

This commit minimizes the amount of data that needs
to be downloaded when we pull in the test framework.
It does this by not cloning the entire repository and
only checking out once we've set up the sparse
checkout.

* Moved test library to `/wordpress-phpunit`

This commit moves the test library from `/phpunit'
to `/wordpress-phpunit`. This change is to avoid
potential confusion. `/phpunit` sounds like a
tool while `/wordpress-phpunit` is not so
ambiguous.

* Removed `wp-phpunit/wp-phpunit` Native Support

The entire point of including the test files is that they are better
to use than a package like this. We should have an opinionated
default and allow them to override it if they really want to.

* Fixed Unit Tests

Since we've made it so that the default is no longer to support
`wp-phpunit/wp-phpunit`, we need to explicitly override the
default `wp-env` behavior. This will be cleaned up in a future
pull request that switches over entirely to the test files in the
environment.

* Documented PHPUnit Test File Inclusion

* Added `wp-phpunit/wp-phpunit` Notice to Changelog
  • Loading branch information
ObliviousHarmony authored Jul 15, 2022
1 parent a7181f2 commit 9b13f84
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 34 deletions.
8 changes: 8 additions & 0 deletions packages/env/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

### Breaking Changes
- Removed the `WP_PHPUNIT__TESTS_CONFIG` environment variable from the `phpunit` container. **This removes automatic support for the `wp-phpunit/wp-phpunit` Composer package. To continue using the package, set the following two environment variables in your `phpunit.xml` file or similar: `WP_TESTS_DIR=""` and `WP_PHPUNIT__TESTS_CONFIG="/wordpress-phpunit/wp-tests-config.php"`.**
- Removed the generated `/var/www/html/phpunit-wp-config.php` file from the environment.

### Enhancement
- Read WordPress' version and include the corresponding PHPUnit test files in the environment.
- Set the `WP_TESTS_DIR` environment variable in all containers to point at the PHPUnit test files.

## 4.8.0 (2022-06-01)
### Enhancement
- Removed the need for quotation marks when passing options to `wp-env run`.
Expand Down
8 changes: 8 additions & 0 deletions packages/env/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,14 @@ wp-env start --debug
...
```

## Using included WordPress PHPUnit test files

Out of the box `wp-env` includes the [WordPress' PHPUnit test files](https://develop.svn.wordpress.org/trunk/tests/phpunit/) corresponding to the version of WordPress installed. There is an environment variable, `WP_TESTS_DIR`, which points to the location of these files within each container. By including these files in the environment, we remove the need for you to use a package or install and mount them yourself. If you do not want to use these files, you should ignore the `WP_TESTS_DIR` environment variable and load them from the location of your choosing.

### Customizing the `wp-tests-config.php` file

While we do provide a default `wp-tests-config.php` file within the environment, there may be cases where you want to use your own. WordPress provides a `WP_TESTS_CONFIG_FILE_PATH` constant that you can use to change the `wp-config.php` file used for testing. Set this to a desired path in your `bootstrap.php` file and the file you've chosen will be used instead of the one included in the environment.

## Using Xdebug

Xdebug is installed in the wp-env environment, but it is turned off by default. To enable Xdebug, you can use the `--xdebug` flag with the `wp-env start` command. Here is a reference to how the flag works:
Expand Down
41 changes: 33 additions & 8 deletions packages/env/lib/build-docker-compose-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@ const { dbEnv } = require( './config' );
/**
* Gets the volume mounts for an individual service.
*
* @param {WPServiceConfig} config The service config to get the mounts from.
* @param {string} wordpressDefault The default internal path for the WordPress
* source code (such as tests-wordpress).
* @param {string} workDirectoryPath The working directory for wp-env.
* @param {WPServiceConfig} config The service config to get the mounts from.
* @param {string} wordpressDefault The default internal path for the WordPress
* source code (such as tests-wordpress).
*
* @return {string[]} An array of volumes to mount in string format.
*/
function getMounts( config, wordpressDefault = 'wordpress' ) {
function getMounts(
workDirectoryPath,
config,
wordpressDefault = 'wordpress'
) {
// Top-level WordPress directory mounts (like wp-content/themes)
const directoryMounts = Object.entries( config.mappings ).map(
( [ wpDir, source ] ) => `${ source.path }:/var/www/html/${ wpDir }`
Expand All @@ -45,9 +50,19 @@ function getMounts( config, wordpressDefault = 'wordpress' ) {
config.coreSource ? config.coreSource.path : wordpressDefault
}:/var/www/html`;

const corePHPUnitMount = `${ path.join(
workDirectoryPath,
wordpressDefault === 'wordpress'
? 'WordPress-PHPUnit'
: 'tests-WordPress-PHPUnit',
'tests',
'phpunit'
) }:/wordpress-phpunit`;

return [
...new Set( [
coreMount,
corePHPUnitMount,
...directoryMounts,
...pluginMounts,
...themeMounts,
Expand All @@ -64,8 +79,15 @@ function getMounts( config, wordpressDefault = 'wordpress' ) {
* @return {Object} A docker-compose config object, ready to serialize into YAML.
*/
module.exports = function buildDockerComposeConfig( config ) {
const developmentMounts = getMounts( config.env.development );
const testsMounts = getMounts( config.env.tests, 'tests-wordpress' );
const developmentMounts = getMounts(
config.workDirectoryPath,
config.env.development
);
const testsMounts = getMounts(
config.workDirectoryPath,
config.env.tests,
'tests-wordpress'
);

// When both tests and development reference the same WP source, we need to
// ensure that tests pulls from a copy of the files so that it maintains
Expand Down Expand Up @@ -208,6 +230,7 @@ module.exports = function buildDockerComposeConfig( config ) {
environment: {
...dbEnv.credentials,
...dbEnv.development,
WP_TESTS_DIR: '/wordpress-phpunit',
},
volumes: developmentMounts,
},
Expand All @@ -218,6 +241,7 @@ module.exports = function buildDockerComposeConfig( config ) {
environment: {
...dbEnv.credentials,
...dbEnv.tests,
WP_TESTS_DIR: '/wordpress-phpunit',
},
volumes: testsMounts,
},
Expand All @@ -229,6 +253,7 @@ module.exports = function buildDockerComposeConfig( config ) {
environment: {
...dbEnv.credentials,
...dbEnv.development,
WP_TESTS_DIR: '/wordpress-phpunit',
},
},
'tests-cli': {
Expand All @@ -239,6 +264,7 @@ module.exports = function buildDockerComposeConfig( config ) {
environment: {
...dbEnv.credentials,
...dbEnv.tests,
WP_TESTS_DIR: '/wordpress-phpunit',
},
},
composer: {
Expand All @@ -256,8 +282,7 @@ module.exports = function buildDockerComposeConfig( config ) {
],
environment: {
LOCAL_DIR: 'html',
WP_PHPUNIT__TESTS_CONFIG:
'/var/www/html/phpunit-wp-config.php',
WP_TESTS_DIR: '/wordpress-phpunit',
...dbEnv.credentials,
...dbEnv.tests,
},
Expand Down
12 changes: 6 additions & 6 deletions packages/env/lib/commands/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const { spawn } = require( 'child_process' );
const initConfig = require( '../init-config' );

/**
* @typedef {import('../config').Config} Config
* @typedef {import('../config').WPConfig} WPConfig
*/

/**
Expand Down Expand Up @@ -42,11 +42,11 @@ module.exports = async function run( { container, command, spinner, debug } ) {
/**
* Runs an arbitrary command on the given Docker container.
*
* @param {Object} options
* @param {string} options.container The Docker container to run the command on.
* @param {string} options.command The command to run.
* @param {Config} options.config The wp-env configuration.
* @param {Object} options.spinner A CLI spinner which indicates progress.
* @param {Object} options
* @param {string} options.container The Docker container to run the command on.
* @param {string} options.command The command to run.
* @param {WPConfig} options.config The wp-env configuration.
* @param {Object} options.spinner A CLI spinner which indicates progress.
*/
function spawnCommandDirectly( { container, command, config, spinner } ) {
const composeCommand = [
Expand Down
22 changes: 21 additions & 1 deletion packages/env/lib/commands/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@ const retry = require( '../retry' );
const stop = require( './stop' );
const initConfig = require( '../init-config' );
const downloadSources = require( '../download-sources' );
const downloadWPPHPUnit = require( '../download-wp-phpunit' );
const {
checkDatabaseConnection,
configureWordPress,
setupWordPressDirectories,
readWordPressVersion,
} = require( '../wordpress' );
const { didCacheChange, setCache } = require( '../cache' );
const md5 = require( '../md5' );

/**
* @typedef {import('../config').Config} Config
* @typedef {import('../config').WPConfig} WPConfig
*/
const CONFIG_CACHE_KEY = 'config_checksum';

Expand Down Expand Up @@ -127,7 +129,25 @@ module.exports = async function start( { spinner, debug, update, xdebug } ) {
] );

if ( shouldConfigureWp ) {
spinner.text = 'Setting up WordPress directories';

await setupWordPressDirectories( config );

// Use the WordPress versions to download the PHPUnit suite.
const wpVersions = await Promise.all( [
readWordPressVersion(
config.env.development.coreSource,
spinner,
debug
),
readWordPressVersion( config.env.tests.coreSource, spinner, debug ),
] );
await downloadWPPHPUnit(
config,
{ development: wpVersions[ 0 ], tests: wpVersions[ 1 ] },
spinner,
debug
);
}

spinner.text = 'Starting WordPress.';
Expand Down
6 changes: 3 additions & 3 deletions packages/env/lib/download-sources.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ const extractZip = util.promisify( require( 'extract-zip' ) );
const rimraf = util.promisify( require( 'rimraf' ) );

/**
* @typedef {import('./config').Config} Config
* @typedef {import('./config').WPConfig} WPConfig
* @typedef {import('./config').WPSource} WPSource
*/

/**
* Download each source for each environment. If the same source is used in
* multiple environments, it will only be downloaded once.
*
* @param {Config} config The wp-env configuration object.
* @param {Object} spinner The spinner object to show progress.
* @param {WPConfig} config The wp-env configuration object.
* @param {Object} spinner The spinner object to show progress.
* @return {Promise} Returns a promise which resolves when the downloads finish.
*/
module.exports = function downloadSources( config, spinner ) {
Expand Down
140 changes: 140 additions & 0 deletions packages/env/lib/download-wp-phpunit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
'use strict';
/**
* External dependencies
*/
const SimpleGit = require( 'simple-git' );
const fs = require( 'fs' );
const path = require( 'path' );

/**
* @typedef {import('./config').WPConfig} WPConfig
*/

/**
* Downloads the WordPress PHPUnit files for each environment into the appropriate directories.
*
* @param {WPConfig} config The wp-env config object.
* @param {Object} wpVersions The WordPress versions for each environment.
* @param {Object} spinner The spinner object to show progress.
* @param {boolean} debug Indicates whether or not debug mode is active.
* @return {Promise} Returns a promise which resolves when the downloads finish.
*/
module.exports = function downloadWPPHPUnit(
config,
wpVersions,
spinner,
debug
) {
const progresses = {};
const getProgressSetter = ( id ) => ( progress ) => {
progresses[ id ] = progress;
spinner.text =
'Downloading WordPress PHPUnit Suite.\n' +
Object.entries( progresses )
.map(
( [ key, value ] ) =>
` - ${ key }: ${ ( value * 100 ).toFixed( 0 ) }/100%`
)
.join( '\n' );
};

const promises = [];
for ( const env in config.env ) {
const wpVersion = wpVersions[ env ] ? wpVersions[ env ] : null;
const directory = path.join(
config.workDirectoryPath,
env === 'development'
? 'WordPress-PHPUnit'
: 'tests-WordPress-PHPUnit'
);
promises.push(
downloadTestSuite( directory, wpVersion, {
onProgress: getProgressSetter,
spinner,
debug,
} )
);
}

return Promise.all( promises );
};

/**
* Downloads the PHPUnit tests for a given WordPress version into the appropriate directory.
*
* @param {string} directory The directory to place the PHPUnit tests in.
* @param {string} wpVersion The version of WordPress to install PHPUnit tests for. Trunk when empty.
* @param {Object} options
* @param {Function} options.onProgress A function called with download progress. Will be invoked with one argument: a number that ranges from 0 to 1 which indicates current download progress for this source.
* @param {Object} options.spinner A CLI spinner which indicates progress.
* @param {boolean} options.debug True if debug mode is enabled.
*/
async function downloadTestSuite(
directory,
wpVersion,
{ onProgress, spinner, debug }
) {
const log = debug
? ( message ) => {
spinner.info( `SimpleGit: ${ message }` );
spinner.start();
}
: () => {};
onProgress( 0 );

const progressHandler = ( { progress } ) => {
onProgress( progress / 100 );
};

// Make sure that the version is in X.X.X format. This is required
// because WordPress/wordpress-develop uses X.X.X tags but
// WordPress uses X.X for non-patch releases.
if ( wpVersion && wpVersion.match( /^[0-9]+.[0-9]+$/ ) ) {
wpVersion += '.0';
}

log( 'Cloning or getting the PHPUnit suite from GitHub.' );
const git = SimpleGit( { progress: progressHandler } );

const isRepo =
fs.existsSync( directory ) &&
( await git.cwd( directory ).checkIsRepo( 'root' ) );

if ( isRepo ) {
log( 'Repo already exists, using it.' );
} else {
await git.clone(
'https://github.com/WordPress/wordpress-develop.git',
directory,
{
'--depth': '1',
'--no-checkout': null,
}
);
await git.cwd( directory );

// We use a sparse checkout to minimize the amount of data we need to download.
log( 'Enabling sparse checkout.' );
await git.raw( 'sparse-checkout', 'set', '--cone', 'tests/phpunit' );
}

// Figure out the ref that we need to checkout to get the correct version of the PHPUnit library.
// Alpha, Beta, and RC versions are bleeding edge and should pull from trunk.
let ref;
const fetchRaw = [];
if ( ! wpVersion || wpVersion.match( /-(?:alpha|beta|rc)/ ) ) {
ref = 'trunk';
fetchRaw.push( 'fetch', 'origin', ref, '--depth', '1' );
} else {
ref = `tags/${ wpVersion }`;
fetchRaw.push( 'fetch', 'origin', 'tag', wpVersion, '--depth', '1' );
}

log( `Fetching ${ ref }.` );
await git.raw( fetchRaw );

log( `Checking out ${ ref }.` );
await git.checkout( ref );

onProgress( 1 );
}
Loading

0 comments on commit 9b13f84

Please sign in to comment.