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

Behat code coverage support #234

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,13 @@
<rule ref="WordPress.NamingConventions.PrefixAllGlobals">
<exclude-pattern>*/utils/polyfills\.php$</exclude-pattern>
</rule>

<!-- This is a procedural stand-alone file that is never loaded in a WordPress context,
so this file does not have to comply with WP naming conventions. -->
<rule ref="WordPress.NamingConventions.PrefixAllGlobals">
<exclude-pattern>*/maybe-generate-wp-cli-coverage\.php$</exclude-pattern>
</rule>
<rule ref="WordPress.WP.GlobalVariablesOverride">
<exclude-pattern>*/maybe-generate-wp-cli-coverage\.php$</exclude-pattern>
</rule>
</ruleset>
75 changes: 67 additions & 8 deletions src/Context/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Testwork\Hook\Scope\AfterSuiteScope;
use Behat\Testwork\Hook\Scope\BeforeSuiteScope;
use Behat\Behat\Hook\Scope\AfterFeatureScope;
use Behat\Behat\Hook\Scope\BeforeFeatureScope;
use RuntimeException;
use WP_CLI\Process;
use WP_CLI\Utils;
Expand Down Expand Up @@ -107,6 +109,48 @@ class FeatureContext implements SnippetAcceptingContext {
private static $scenario_count = 0; // Scenario count, incremented on `@AfterScenario`.
private static $proc_method_run_times = []; // Array of run time info for proc methods, keyed by method name and arg, each a 2-element array containing run time and run count.

/**
* The current feature.
*
* @var \Behat\Gherkin\Node\FeatureNode|null
*/
private static $feature;

/**
* The current scenario.
*
* @var \Behat\Gherkin\Node\ScenarioInterface|null
*/
private $scenario;

/**
* @BeforeFeature
*/
public static function store_feature( BeforeFeatureScope $scope ) {
self::$feature = $scope->getFeature();
}

/**
* @BeforeScenario
*/
public function store_scenario( BeforeScenarioScope $scope ) {
$this->scenario = $scope->getScenario();
}

/**
* @AfterScenario
*/
public function forget_scenario( AfterScenarioScope $scope ) {
$this->scenario = null;
}

/**
* @AfterFeature
*/
public static function forget_feature( AfterFeatureScope $scope ) {
self::$feature = null;
}

/**
* Get the path to the Composer vendor folder.
*
Expand Down Expand Up @@ -330,9 +374,9 @@ private static function get_behat_internal_variables() {
}

/**
* Download and extract a single copy of the sqlite-database-integration plugin
* for use in subsequent WordPress copies
*/
* Download and extract a single copy of the sqlite-database-integration plugin
* for use in subsequent WordPress copies
*/
private static function download_sqlite_plugin( $dir ) {
$download_url = 'https://downloads.wordpress.org/plugin/sqlite-database-integration.zip';
$download_location = $dir . '/sqlite-database-integration.zip';
Expand Down Expand Up @@ -367,9 +411,9 @@ private static function download_sqlite_plugin( $dir ) {
}

/**
* Given a WordPress installation with the sqlite-database-integration plugin,
* configure it to use SQLite as the database by placing the db.php dropin file
*/
* Given a WordPress installation with the sqlite-database-integration plugin,
* configure it to use SQLite as the database by placing the db.php dropin file
*/
private static function configure_sqlite( $dir ) {
$db_copy = $dir . '/wp-content/mu-plugins/sqlite-database-integration/db.copy';
$db_dropin = $dir . '/wp-content/db.php';
Expand Down Expand Up @@ -875,10 +919,25 @@ public function proc( $command, $assoc_args = [], $path = '' ) {
}

$env = self::get_process_env_variables();

if ( isset( $this->variables['SUITE_CACHE_DIR'] ) ) {
$env['WP_CLI_CACHE_DIR'] = $this->variables['SUITE_CACHE_DIR'];
}

if ( isset( $this->variables['PROJECT_DIR'] ) ) {
$env['BEHAT_PROJECT_DIR'] = $this->variables['PROJECT_DIR'];
}

if ( self::$feature ) {
$env['BEHAT_FEATURE_TITLE'] = self::$feature->getTitle();
}

if ( $this->scenario ) {
$env['BEHAT_SCENARIO_TITLE'] = $this->scenario->getTitle();
}

$env['WP_CLI_TEST_DBTYPE'] = self::$db_type;

if ( isset( $this->variables['RUN_DIR'] ) ) {
$cwd = "{$this->variables['RUN_DIR']}/{$path}";
} else {
Expand Down Expand Up @@ -1240,8 +1299,8 @@ private static function dir_diff_copy( $upd_dir, $src_dir, $cop_dir ) {
}
self::copy_dir( $upd_file, $cop_file );
} elseif ( ! copy( $upd_file, $cop_file ) ) {
$error = error_get_last();
throw new RuntimeException( sprintf( "Failed to copy '%s' to '%s': %s. " . __FILE__ . ':' . __LINE__, $upd_file, $cop_file, $error['message'] ) );
$error = error_get_last();
throw new RuntimeException( sprintf( "Failed to copy '%s' to '%s': %s. " . __FILE__ . ':' . __LINE__, $upd_file, $cop_file, $error['message'] ) );
}
} elseif ( is_dir( $upd_file ) ) {
self::dir_diff_copy( $upd_file, $src_file, $cop_file );
Expand Down
5 changes: 5 additions & 0 deletions src/Context/WhenStepDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public function when_i_launch_in_the_background( $cmd ) {
* @When /^I (run|try) `([^`]+)`$/
*/
public function when_i_run( $mode, $cmd ) {
$with_code_coverage = (string) getenv( 'WP_CLI_TEST_COVERAGE' );
if ( \in_array( $with_code_coverage, [ 'true', '1' ], true ) ) {
$cmd = preg_replace( '/(^wp )|( wp )|(\/wp )/', '$1$2$3--require={PROJECT_DIR}/vendor/wp-cli/wp-cli-tests/utils/maybe-generate-wp-cli-coverage.php ', $cmd );
}

$cmd = $this->replace_variables( $cmd );
$this->result = $this->wpcli_tests_invoke_proc( $this->proc( $cmd ), $mode );
list( $this->result->stdout, $this->email_sends ) = $this->wpcli_tests_capture_email_sends( $this->result->stdout );
Expand Down
47 changes: 47 additions & 0 deletions utils/maybe-generate-wp-cli-coverage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\Selector;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\Report\Clover;

$root_folder = realpath( dirname( dirname( dirname( __DIR__ ) ) ) );

if ( ! class_exists( 'SebastianBergmann\CodeCoverage\Filter' ) ) {
require "{$root_folder}/vendor/autoload.php";
}

$filter = new Filter();
$filter->includeDirectory( "{$root_folder}/includes" );
$filter->includeFiles( array( "{$root_folder}/plugin.php" ) );

$coverage = new CodeCoverage(
( new Selector() )->forLineCoverage( $filter ),
$filter
);

$feature = getenv( 'BEHAT_FEATURE_TITLE' );
$scenario = getenv( 'BEHAT_SCENARIO_TITLE' );
$name = "{$feature} - {$scenario}";

$coverage->start( $name );

register_shutdown_function(
static function () use ( $coverage, $feature, $scenario, $name ) {
$coverage->stop();

$project_dir = (string) getenv( 'BEHAT_PROJECT_DIR' );

$feature_suffix = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $feature ) );
$scenario_suffix = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $scenario ) );
$db_type = strtolower( getenv( 'WP_CLI_TEST_DBTYPE' ) );
$destination = "$project_dir/build/logs/$feature_suffix-$scenario_suffix-$db_type.xml";

$dir = dirname( $destination );
if ( ! file_exists( $dir ) ) {
mkdir( $dir, 0777, true /*recursive*/ );
}

( new Clover() )->process( $coverage, $destination, $name );
}
);