Skip to content

Instantly share code, notes, and snippets.

@miya0001
Created October 11, 2017 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miya0001/d17a036a2a82a86f293b459f4d7868d9 to your computer and use it in GitHub Desktop.
Save miya0001/d17a036a2a82a86f293b459f4d7868d9 to your computer and use it in GitHub Desktop.
patch
diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php
index aaf39713..ae0adb9d 100644
--- a/features/bootstrap/FeatureContext.php
+++ b/features/bootstrap/FeatureContext.php
@@ -1,14 +1,14 @@
<?php
use Behat\Behat\Context\ClosuredContextInterface,
- Behat\Behat\Context\TranslatedContextInterface,
- Behat\Behat\Context\BehatContext,
- Behat\Behat\Event\SuiteEvent;
+Behat\Behat\Context\TranslatedContextInterface,
+Behat\Behat\Context\BehatContext,
+Behat\Behat\Event\SuiteEvent;
use \WP_CLI\Process;
use \WP_CLI\Utils;
-// Inside a community package
+
if ( file_exists( __DIR__ . '/utils.php' ) ) {
require_once __DIR__ . '/utils.php';
require_once __DIR__ . '/Process.php';
@@ -27,7 +27,7 @@ if ( file_exists( __DIR__ . '/utils.php' ) ) {
putenv( 'WP_CLI_CONFIG_PATH=' . $project_config );
}
}
-// Inside WP-CLI
+
} else {
require_once __DIR__ . '/../../php/utils.php';
require_once __DIR__ . '/../../php/WP_CLI/Process.php';
@@ -39,41 +39,41 @@ if ( file_exists( __DIR__ . '/utils.php' ) ) {
}
}
-/**
- * Features context.
- */
+
+
+
class FeatureContext extends BehatContext implements ClosuredContextInterface {
- /**
- * The current working directory for scenarios that have a "Given a WP install" or "Given an empty directory" step. Variable RUN_DIR. Lives until the end of the scenario.
- */
+
+
+
private static $run_dir;
- /**
- * Where WordPress core is downloaded to for caching, and which is copied to RUN_DIR during a "Given a WP install" step. Lives until manually deleted.
- */
+
+
+
private static $cache_dir;
- /**
- * The directory that holds the install cache, and which is copied to RUN_DIR during a "Given a WP install" step. Recreated on each suite run.
- */
+
+
+
private static $install_cache_dir;
- /**
- * The directory that the WP-CLI cache (WP_CLI_CACHE_DIR, normally "$HOME/.wp-cli/cache") is set to on a "Given an empty cache" step.
- * Variable SUITE_CACHE_DIR. Lives until the end of the scenario (or until another "Given an empty cache" step within the scenario).
- */
+
+
+
+
private static $suite_cache_dir;
- /**
- * Where the current WP-CLI source repository is copied to for Composer-based tests with a "Given a dependency on current wp-cli" step.
- * Variable COMPOSER_LOCAL_REPOSITORY. Lives until the end of the suite.
- */
+
+
+
+
private static $composer_local_repository;
- /**
- * The test database settings. All but `dbname` can be set via environment variables. The database is dropped at the start of each scenario and created on a "Given a WP install" step.
- */
+
+
+
private static $db_settings = array(
'dbname' => 'wp_cli_test',
'dbuser' => 'wp_cli_test',
@@ -81,44 +81,44 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
'dbhost' => '127.0.0.1',
);
- /**
- * Array of background process ids started by the current scenario. Used to terminate them at the end of the scenario.
- */
+
+
+
private $running_procs = array();
- /**
- * Array of variables available as {VARIABLE_NAME}. Some are always set: CORE_CONFIG_SETTINGS, SRC_DIR, CACHE_DIR, WP_VERSION-version-latest. Some are step-dependent:
- * RUN_DIR, SUITE_CACHE_DIR, COMPOSER_LOCAL_REPOSITORY, PHAR_PATH. Scenarios can define their own variables using "Given save" steps. Variables are reset for each scenario.
- */
+
+
+
+
public $variables = array();
- /**
- * The current feature file and scenario line number as '<file>.<line>'. Used in RUN_DIR and SUITE_CACHE_DIR directory names. Set at the start of each scenario.
- */
+
+
+
private static $temp_dir_infix;
- /**
- * Settings and variables for WP_CLI_TEST_LOG_RUN_TIMES run time logging.
- */
- private static $log_run_times; // Whether to log run times - WP_CLI_TEST_LOG_RUN_TIMES env var. Set on `@BeforeScenario'.
- private static $suite_start_time; // When the suite started, set on `@BeforeScenario'.
- private static $output_to; // Where to output log - stdout|error_log. Set on `@BeforeSuite`.
- private static $num_top_processes; // Number of processes/methods to output by longest run times. Set on `@BeforeSuite`.
- private static $num_top_scenarios; // Number of scenarios to output by longest run times. Set on `@BeforeSuite`.
-
- private static $scenario_run_times = array(); // Scenario run times (top `self::$num_top_scenarios` only).
- private static $scenario_count = 0; // Scenario count, incremented on `@AfterScenario`.
- private static $proc_method_run_times = array(); // 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.
-
- /**
- * Get the environment variables required for launched `wp` processes
- */
+
+
+
+ private static $log_run_times;
+ private static $suite_start_time;
+ private static $output_to;
+ private static $num_top_processes;
+ private static $num_top_scenarios;
+
+ private static $scenario_run_times = array();
+ private static $scenario_count = 0;
+ private static $proc_method_run_times = array();
+
+
+
+
private static function get_process_env_variables() {
- // Ensure we're using the expected `wp` binary
+
$bin_dir = getenv( 'WP_CLI_BIN_DIR' ) ?: realpath( __DIR__ . '/../../bin' );
$vendor_dir = realpath( __DIR__ . '/../../vendor/bin' );
$env = array(
- 'PATH' => $bin_dir . ':' . $vendor_dir . ':' . getenv( 'PATH' ),
+ 'PATH' => $bin_dir . ':' . $vendor_dir . ':' . getenv( 'PATH' ),
'BEHAT_RUN' => 1,
'HOME' => sys_get_temp_dir() . '/wp-cli-home',
);
@@ -140,10 +140,10 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return $env;
}
- /**
- * We cache the results of `wp core download` to improve test performance.
- * Ideally, we'd cache at the HTTP layer for more reliable tests.
- */
+
+
+
+
private static function cache_wp_files() {
$wp_version_suffix = ( $wp_version = getenv( 'WP_VERSION' ) ) ? "-$wp_version" : '';
self::$cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-download-cache' . $wp_version_suffix;
@@ -158,11 +158,11 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
Process::create( $cmd, null, self::get_process_env_variables() )->run_check();
}
- /**
- * @BeforeSuite
- */
+
+
+
public static function prepare( SuiteEvent $event ) {
- // Test performance statistics - useful for detecting slow tests.
+
if ( self::$log_run_times = getenv( 'WP_CLI_TEST_LOG_RUN_TIMES' ) ) {
self::log_run_times_before_suite( $event );
}
@@ -176,7 +176,7 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
echo 'WordPress ' . $result->stdout;
echo PHP_EOL;
- // Remove install cache if any (not setting the static var).
+
$wp_version_suffix = ( $wp_version = getenv( 'WP_VERSION' ) ) ? "-$wp_version" : '';
$install_cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-install-cache' . $wp_version_suffix;
if ( file_exists( $install_cache_dir ) ) {
@@ -184,9 +184,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
}
}
- /**
- * @AfterSuite
- */
+
+
+
public static function afterSuite( SuiteEvent $event ) {
if ( self::$composer_local_repository ) {
self::remove_dir( self::$composer_local_repository );
@@ -198,9 +198,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
}
}
- /**
- * @BeforeScenario
- */
+
+
+
public function beforeScenario( $event ) {
if ( self::$log_run_times ) {
self::log_run_times_before_scenario( $event );
@@ -208,38 +208,38 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
$this->variables['SRC_DIR'] = realpath( __DIR__ . '/../..' );
- // Used in the names of the RUN_DIR and SUITE_CACHE_DIR directories.
+
self::$temp_dir_infix = null;
if ( $file = self::get_event_file( $event, $line ) ) {
self::$temp_dir_infix = basename( $file ) . '.' . $line;
}
}
- /**
- * @AfterScenario
- */
+
+
+
public function afterScenario( $event ) {
if ( self::$run_dir ) {
- // remove altered WP install, unless there's an error
+
if ( $event->getResult() < 4 ) {
self::remove_dir( self::$run_dir );
}
self::$run_dir = null;
}
- // Remove WP-CLI package directory if any. Set to `wp package path` by package-command and scaffold-package-command features, and by cli-info.feature.
+
if ( isset( $this->variables['PACKAGE_PATH'] ) ) {
self::remove_dir( $this->variables['PACKAGE_PATH'] );
}
- // Remove SUITE_CACHE_DIR if any.
+
if ( self::$suite_cache_dir ) {
self::remove_dir( self::$suite_cache_dir );
self::$suite_cache_dir = null;
}
- // Remove any background processes.
+
foreach ( $this->running_procs as $proc ) {
$status = proc_get_status( $proc );
self::terminate_proc( $status['pid'] );
@@ -250,9 +250,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
}
}
- /**
- * Terminate a process and any of its children.
- */
+
+
+
private static function terminate_proc( $master_pid ) {
$output = `ps -o ppid,pid,command | grep $master_pid`;
@@ -270,16 +270,16 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
if ( ! posix_kill( (int) $master_pid, 9 ) ) {
$errno = posix_get_last_error();
- // Ignore "No such process" error as that's what we want.
- if ( 3 /*ESRCH*/ !== $errno ) {
+
+ if ( 3 !== $errno ) {
throw new RuntimeException( posix_strerror( $errno ) );
}
}
}
- /**
- * Create a temporary WP_CLI_CACHE_DIR. Exposed as SUITE_CACHE_DIR in "Given an empty cache" step.
- */
+
+
+
public static function create_cache_dir() {
if ( self::$suite_cache_dir ) {
self::remove_dir( self::$suite_cache_dir );
@@ -289,12 +289,12 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return self::$suite_cache_dir;
}
- /**
- * Initializes context.
- * Every scenario gets its own context object.
- *
- * @param array $parameters context parameters (set them up through behat.yml)
- */
+
+
+
+
+
+
public function __construct( array $parameters ) {
if ( getenv( 'WP_CLI_TEST_DBUSER' ) ) {
self::$db_settings['dbuser'] = getenv( 'WP_CLI_TEST_DBUSER' );
@@ -321,9 +321,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return array();
}
- /**
- * Replace {VARIABLE_NAME}. Note that variable names can only contain uppercase letters and underscores (no numbers).
- */
+
+
+
public function replace_variables( $str ) {
$ret = preg_replace_callback( '/\{([A-Z_]+)\}/', array( $this, '_replace_var' ), $str );
if ( false !== strpos( $str, '{WP_VERSION-' ) ) {
@@ -332,9 +332,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return $ret;
}
- /**
- * Replace variables callback.
- */
+
+
+
private function _replace_var( $matches ) {
$cmd = $matches[0];
@@ -345,9 +345,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return $cmd;
}
- /**
- * Substitute "{WP_VERSION-version-latest}" variables.
- */
+
+
+
private function _replace_wp_versions( $str ) {
static $wp_versions = null;
if ( null === $wp_versions ) {
@@ -355,7 +355,7 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
$response = Requests::get( 'https://api.wordpress.org/core/version-check/1.7/', null, array( 'timeout' => 30 ) );
if ( 200 === $response->status_code && ( $body = json_decode( $response->body ) ) && is_object( $body ) && isset( $body->offers ) && is_array( $body->offers ) ) {
- // Latest version alias.
+
$wp_versions["{WP_VERSION-latest}"] = count( $body->offers ) ? $body->offers[0]->version : '';
foreach ( $body->offers as $offer ) {
$sub_ver = preg_replace( '/(^[0-9]+\.[0-9]+)\.[0-9]+$/', '$1', $offer->version );
@@ -376,9 +376,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return strtr( $str, $wp_versions );
}
- /**
- * Get the file and line number for the current behat event.
- */
+
+
+
private static function get_event_file( $event, &$line ) {
if ( method_exists( $event, 'getScenario' ) ) {
$scenario_feature = $event->getScenario();
@@ -393,9 +393,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return $scenario_feature->getFile();
}
- /**
- * Create the RUN_DIR directory, unless already set for this scenario.
- */
+
+
+
public function create_run_dir() {
if ( !isset( $this->variables['RUN_DIR'] ) ) {
self::$run_dir = $this->variables['RUN_DIR'] = sys_get_temp_dir() . '/' . uniqid( 'wp-cli-test-run-' . self::$temp_dir_infix . '-', TRUE );
@@ -406,14 +406,14 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
public function build_phar( $version = 'same' ) {
$this->variables['PHAR_PATH'] = $this->variables['RUN_DIR'] . '/' . uniqid( "wp-cli-build-", TRUE ) . '.phar';
- // Test running against a package installed as a WP-CLI dependency
- // WP-CLI installed as a project dependency
+
+
$make_phar_path = __DIR__ . '/../../../../../utils/make-phar.php';
if ( ! file_exists( $make_phar_path ) ) {
- // Test running against WP-CLI proper
+
$make_phar_path = __DIR__ . '/../../utils/make-phar.php';
if ( ! file_exists( $make_phar_path ) ) {
- // WP-CLI as a dependency of this project
+
$make_phar_path = __DIR__ . '/../../vendor/wp-cli/wp-cli/utils/make-phar.php';
}
}
@@ -437,8 +437,8 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
);
$this->variables['PHAR_PATH'] = $this->variables['RUN_DIR'] . '/'
- . uniqid( 'wp-cli-download-', true )
- . '.phar';
+ . uniqid( 'wp-cli-download-', true )
+ . '.phar';
Process::create( Utils\esc_cmd(
'curl -sSfL %1$s > %2$s && chmod +x %2$s',
@@ -447,9 +447,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
) )->run_check();
}
- /**
- * CACHE_DIR is a cache for downloaded test data such as images. Lives until manually deleted.
- */
+
+
+
private function set_cache_dir() {
$path = sys_get_temp_dir() . '/wp-cli-test-cache';
if ( ! file_exists( $path ) ) {
@@ -458,13 +458,13 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
$this->variables['CACHE_DIR'] = $path;
}
- /**
- * Run a MySQL command with `$db_settings`.
- *
- * @param string $sql_cmd Command to run.
- * @param array $assoc_args Optional. Associative array of options. Default empty.
- * @param bool $add_database Optional. Whether to add dbname to the $sql_cmd. Default false.
- */
+
+
+
+
+
+
+
private static function run_sql( $sql_cmd, $assoc_args = array(), $add_database = false ) {
$default_assoc_args = array(
'host' => self::$db_settings['dbhost'],
@@ -509,9 +509,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return Process::create( $command, $cwd, $env );
}
- /**
- * Start a background process. Will automatically be closed when the tests finish.
- */
+
+
+
public function background_proc( $cmd ) {
$descriptors = array(
0 => STDIN,
@@ -536,16 +536,16 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
rename( $this->variables['RUN_DIR'] . "/$src", $this->variables['RUN_DIR'] . "/$dest" );
}
- /**
- * Remove a directory (recursive).
- */
+
+
+
public static function remove_dir( $dir ) {
Process::create( Utils\esc_cmd( 'rm -rf %s', $dir ) )->run_check();
}
- /**
- * Copy a directory (recursive). Destination directory must exist.
- */
+
+
+
public static function copy_dir( $src_dir, $dest_dir ) {
Process::create( Utils\esc_cmd( "cp -r %s/* %s", $src_dir, $dest_dir ) )->run_check();
}
@@ -565,7 +565,7 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
self::copy_dir( self::$cache_dir, $dest_dir );
- // disable emailing
+
mkdir( $dest_dir . '/wp-content/mu-plugins' );
copy( __DIR__ . '/../extra/no-mail.php', $dest_dir . '/wp-content/mu-plugins/no-mail.php' );
}
@@ -573,7 +573,7 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
public function create_config( $subdir = '', $extra_php = false ) {
$params = self::$db_settings;
- // Replaces all characters that are not alphanumeric or an underscore into an underscore.
+
$params['dbprefix'] = $subdir ? preg_replace( '#[^a-zA-Z\_0-9]#', '_', $subdir ) : 'wp_';
$params['skip-salts'] = true;
@@ -628,13 +628,13 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
if ( $install_cache_path && file_exists( $install_cache_path ) ) {
self::copy_dir( $install_cache_path, $run_dir );
- self::run_sql( 'mysql --no-defaults', array( 'execute' => "source {$install_cache_path}.sql" ), true /*add_database*/ );
+ self::run_sql( 'mysql --no-defaults', array( 'execute' => "source {$install_cache_path}.sql" ), true );
} else {
$this->proc( 'wp core install', $install_args, $subdir )->run_check();
if ( $install_cache_path ) {
mkdir( $install_cache_path );
self::dir_diff_copy( $run_dir, self::$cache_dir, $install_cache_path );
- self::run_sql( 'mysqldump --no-defaults', array( 'result-file' => "{$install_cache_path}.sql" ), true /*add_database*/ );
+ self::run_sql( 'mysqldump --no-defaults', array( 'result-file' => "{$install_cache_path}.sql" ), true );
}
}
}
@@ -721,9 +721,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
$this->proc( $this->variables['COMPOSER_PATH'] . ' ' . $cmd )->run_check();
}
- /**
- * Initialize run time logging.
- */
+
+
+
private static function log_run_times_before_suite( $event ) {
self::$suite_start_time = microtime( true );
@@ -731,12 +731,12 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
$travis = getenv( 'TRAVIS' );
- // Default output settings.
+
self::$output_to = 'stdout';
self::$num_top_processes = $travis ? 10 : 40;
self::$num_top_scenarios = $travis ? 10 : 20;
- // Allow setting of above with "WP_CLI_TEST_LOG_RUN_TIMES=<output_to>[,<num_top_processes>][,<num_top_scenarios>]" formatted env var.
+
if ( preg_match( '/^(stdout|error_log)?(,[0-9]+)?(,[0-9]+)?$/i', self::$log_run_times, $matches ) ) {
if ( isset( $matches[1] ) ) {
self::$output_to = strtolower( $matches[1] );
@@ -750,18 +750,18 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
}
}
- /**
- * Record the start time of the scenario into the `$scenario_run_times` array.
- */
+
+
+
private static function log_run_times_before_scenario( $event ) {
if ( $scenario_key = self::get_scenario_key( $event ) ) {
self::$scenario_run_times[ $scenario_key ] = -microtime( true );
}
}
- /**
- * Save the run time of the scenario into the `$scenario_run_times` array. Only the top `self::$num_top_scenarios` are kept.
- */
+
+
+
private static function log_run_times_after_scenario( $event ) {
if ( $scenario_key = self::get_scenario_key( $event ) ) {
self::$scenario_run_times[ $scenario_key ] += microtime( true );
@@ -773,14 +773,14 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
}
}
- /**
- * Copy files in updated directory that are not in source directory to copy directory. ("Incremental backup".)
- * Note: does not deal with changed files (ie does not compare file contents for changes), for speed reasons.
- *
- * @param string $upd_dir The directory to search looking for files/directories not in `$src_dir`.
- * @param string $src_dir The directory to be compared to `$upd_dir`.
- * @param string $cop_dir Where to copy any files/directories in `$upd_dir` but not in `$src_dir` to.
- */
+
+
+
+
+
+
+
+
private static function dir_diff_copy( $upd_dir, $src_dir, $cop_dir ) {
if ( false === ( $files = scandir( $upd_dir ) ) ) {
$error = error_get_last();
@@ -792,7 +792,7 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
$cop_file = $cop_dir . '/' . $file;
if ( ! file_exists( $src_file ) ) {
if ( is_dir( $upd_file ) ) {
- if ( ! file_exists( $cop_file ) && ! mkdir( $cop_file, 0777, true /*recursive*/ ) ) {
+ if ( ! file_exists( $cop_file ) && ! mkdir( $cop_file, 0777, true ) ) {
$error = error_get_last();
throw new \RuntimeException( sprintf( "Failed to create copy directory '%s': %s. " . __FILE__ . ':' . __LINE__, $cop_file, $error['message'] ) );
}
@@ -809,10 +809,10 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
}
}
- /**
- * Get the scenario key used for `$scenario_run_times` array.
- * Format "<grandparent-dir> <feature-file>:<line-number>", eg "core-command core-update.feature:221".
- */
+
+
+
+
private static function get_scenario_key( $event ) {
$scenario_key = '';
if ( $file = self::get_event_file( $event, $line ) ) {
@@ -822,21 +822,21 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return $scenario_key;
}
- /**
- * Print out stats on the run times of processes and scenarios.
- */
+
+
+
private static function log_run_times_after_suite( $event ) {
$suite = '';
if ( self::$scenario_run_times ) {
- // Grandparent directory is first part of key.
+
$keys = array_keys( self::$scenario_run_times );
$suite = substr( $keys[0], 0, strpos( $keys[0], ' ' ) );
}
$run_from = Utils\basename( dirname( dirname( __DIR__ ) ) );
- // Format same as Behat, if have minutes.
+
$fmt = function ( $time ) {
$mins = floor( $time / 60 );
return round( $time, 3 ) . ( $mins ? ( ' (' . $mins . 'm' . round( $time - ( $mins * 60 ), 3 ) . 's)' ) : '' );
@@ -846,7 +846,7 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
$log = PHP_EOL . str_repeat( '(', 80 ) . PHP_EOL;
- // Process and proc method run times.
+
$run_times = array_merge( Process::$run_times, self::$proc_method_run_times );
list( $ptime, $calls ) = array_reduce( $run_times, function ( $carry, $item ) {
@@ -863,7 +863,7 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
);
uasort( $run_times, function ( $a, $b ) {
- return $a[0] === $b[0] ? 0 : ( $a[0] < $b[0] ? 1 : -1 ); // Reverse sort.
+ return $a[0] === $b[0] ? 0 : ( $a[0] < $b[0] ? 1 : -1 );
} );
$tops = array_slice( $run_times, 0, self::$num_top_processes, true );
@@ -873,7 +873,7 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
return sprintf( ' %3d. %7.3f %3d %s', $i + 1, round( $v[0], 3 ), $v[1], $k );
}, array_keys( $tops ), $tops, array_keys( array_keys( $tops ) ) ) ) . PHP_EOL;
- // Scenario run times.
+
arsort( self::$scenario_run_times );
$tops = array_slice( self::$scenario_run_times, 0, self::$num_top_scenarios, true );
@@ -892,9 +892,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
}
}
- /**
- * Log the run time of a proc method (one that doesn't use Process but does (use a function that does) a `proc_open()`).
- */
+
+
+
private static function log_proc_method_run_time( $key, $start_time ) {
$run_time = microtime( true ) - $start_time;
if ( ! isset( self::$proc_method_run_times[ $key ] ) ) {
diff --git a/features/bootstrap/Process.php b/features/bootstrap/Process.php
index 5016580b..fda17256 100644
--- a/features/bootstrap/Process.php
+++ b/features/bootstrap/Process.php
@@ -2,51 +2,51 @@
namespace WP_CLI;
-/**
- * Run a system process, and learn what happened.
- */
+
+
+
class Process {
- /**
- * @var string The full command to execute by the system.
- */
+
+
+
private $command;
- /**
- * @var string|null The path of the working directory for the process or NULL if not specified (defaults to current working directory).
- */
+
+
+
private $cwd;
- /**
- * @var array Environment variables to set when running the command.
- */
+
+
+
private $env;
- /**
- * @var array Descriptor spec for `proc_open()`.
- */
+
+
+
private static $descriptors = array(
0 => STDIN,
1 => array( 'pipe', 'w' ),
2 => array( 'pipe', 'w' ),
);
- /**
- * @var bool Whether to log run time info or not.
- */
+
+
+
public static $log_run_times = false;
- /**
- * @var array Array of process run time info, keyed by process command, each a 2-element array containing run time and run count.
- */
+
+
+
public static $run_times = array();
- /**
- * @param string $command Command to execute.
- * @param string $cwd Directory to execute the command in.
- * @param array $env Environment variables to set when running the command.
- *
- * @return Process
- */
+
+
+
+
+
+
+
public static function create( $command, $cwd = null, $env = array() ) {
$proc = new self;
@@ -59,11 +59,11 @@ class Process {
private function __construct() {}
- /**
- * Run the command.
- *
- * @return ProcessRun
- */
+
+
+
+
+
public function run() {
$start_time = microtime( true );
@@ -100,15 +100,15 @@ class Process {
);
}
- /**
- * Run the command, but throw an Exception on error.
- *
- * @return ProcessRun
- */
+
+
+
+
+
public function run_check() {
$r = $this->run();
- // $r->STDERR is incorrect, but kept incorrect for backwards-compat
+
if ( $r->return_code || ! empty( $r->STDERR ) ) {
throw new \RuntimeException( $r );
}
diff --git a/features/bootstrap/ProcessRun.php b/features/bootstrap/ProcessRun.php
index 96b4c80b..1ee16b04 100644
--- a/features/bootstrap/ProcessRun.php
+++ b/features/bootstrap/ProcessRun.php
@@ -2,61 +2,61 @@
namespace WP_CLI;
-/**
- * Results of an executed command.
- */
+
+
+
class ProcessRun {
- /**
- * @var string The full command executed by the system.
- */
+
+
+
public $command;
- /**
- * @var string Captured output from the process' STDOUT.
- */
+
+
+
public $stdout;
- /**
- * @var string Captured output from the process' STDERR.
- */
+
+
+
public $stderr;
- /**
- * @var string|null The path of the working directory for the process or NULL if not specified (defaults to current working directory).
- */
+
+
+
public $cwd;
- /**
- * @var array Environment variables set for this process.
- */
+
+
+
public $env;
- /**
- * @var int Exit code of the process.
- */
+
+
+
public $return_code;
- /**
- * @var float The run time of the process.
- */
+
+
+
public $run_time;
- /**
- * @var array $props Properties of executed command.
- */
+
+
+
public function __construct( $props ) {
foreach ( $props as $key => $value ) {
$this->$key = $value;
}
}
- /**
- * Return properties of executed command as a string.
- *
- * @return string
- */
+
+
+
+
+
public function __toString() {
- $out = "$ $this->command\n";
+ $out = "$ $this->command\n";
$out .= "$this->stdout\n$this->stderr";
$out .= "cwd: $this->cwd\n";
$out .= "run time: $this->run_time\n";
diff --git a/features/bootstrap/support.php b/features/bootstrap/support.php
index a37a064f..4df7b790 100644
--- a/features/bootstrap/support.php
+++ b/features/bootstrap/support.php
@@ -1,6 +1,12 @@
<?php
-// Utility functions used by Behat steps
+
+
+function assertRegExp( $regex, $actual ) {
+ if ( ! preg_match( $regex, $actual ) ) {
+ throw new Exception( "Actual value: " . var_export( $actual, true ) );
+ }
+}
function assertEquals( $expected, $actual ) {
if ( $expected != $actual ) {
@@ -53,7 +59,7 @@ function checkString( $output, $expected, $action, $message = false ) {
}
function compareTables( $expected_rows, $actual_rows, $output ) {
- // the first row is the header and must be present
+
if ( $expected_rows[0] != $actual_rows[0] ) {
throw new \Exception( $output );
}
@@ -89,41 +95,41 @@ function compareContents( $expected, $actual ) {
return true;
}
-/**
- * Compare two strings containing JSON to ensure that @a $actualJson contains at
- * least what the JSON string @a $expectedJson contains.
- *
- * @return whether or not @a $actualJson contains @a $expectedJson
- * @retval true @a $actualJson contains @a $expectedJson
- * @retval false @a $actualJson does not contain @a $expectedJson
- *
- * @param[in] $actualJson the JSON string to be tested
- * @param[in] $expectedJson the expected JSON string
- *
- * Examples:
- * expected: {'a':1,'array':[1,3,5]}
- *
- * 1 )
- * actual: {'a':1,'b':2,'c':3,'array':[1,2,3,4,5]}
- * return: true
- *
- * 2 )
- * actual: {'b':2,'c':3,'array':[1,2,3,4,5]}
- * return: false
- * element 'a' is missing from the root object
- *
- * 3 )
- * actual: {'a':0,'b':2,'c':3,'array':[1,2,3,4,5]}
- * return: false
- * the value of element 'a' is not 1
- *
- * 4 )
- * actual: {'a':1,'b':2,'c':3,'array':[1,2,4,5]}
- * return: false
- * the contents of 'array' does not include 3
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
function checkThatJsonStringContainsJsonString( $actualJson, $expectedJson ) {
- $actualValue = json_decode( $actualJson );
+ $actualValue = json_decode( $actualJson );
$expectedValue = json_decode( $expectedJson );
if ( !$actualValue ) {
@@ -133,26 +139,26 @@ function checkThatJsonStringContainsJsonString( $actualJson, $expectedJson ) {
return compareContents( $expectedValue, $actualValue );
}
-/**
- * Compare two strings to confirm $actualCSV contains $expectedCSV
- * Both strings are expected to have headers for their CSVs.
- * $actualCSV must match all data rows in $expectedCSV
- *
- * @param string A CSV string
- * @param array A nested array of values
- * @return bool Whether $actualCSV contains $expectedCSV
- */
+
+
+
+
+
+
+
+
+
function checkThatCsvStringContainsValues( $actualCSV, $expectedCSV ) {
$actualCSV = array_map( 'str_getcsv', explode( PHP_EOL, $actualCSV ) );
if ( empty( $actualCSV ) )
return false;
- // Each sample must have headers
+
$actualHeaders = array_values( array_shift( $actualCSV ) );
$expectedHeaders = array_values( array_shift( $expectedCSV ) );
- // Each expectedCSV must exist somewhere in actualCSV in the proper column
+
$expectedResult = 0;
foreach ( $expectedCSV as $expected_row ) {
$expected_row = array_combine( $expectedHeaders, $expected_row );
@@ -170,19 +176,19 @@ function checkThatCsvStringContainsValues( $actualCSV, $expectedCSV ) {
return $expectedResult >= count( $expectedCSV );
}
-/**
- * Compare two strings containing YAML to ensure that @a $actualYaml contains at
- * least what the YAML string @a $expectedYaml contains.
- *
- * @return whether or not @a $actualYaml contains @a $expectedJson
- * @retval true @a $actualYaml contains @a $expectedJson
- * @retval false @a $actualYaml does not contain @a $expectedJson
- *
- * @param[in] $actualYaml the YAML string to be tested
- * @param[in] $expectedYaml the expected YAML string
- */
+
+
+
+
+
+
+
+
+
+
+
function checkThatYamlStringContainsYamlString( $actualYaml, $expectedYaml ) {
- $actualValue = Mustangostang\Spyc::YAMLLoad( $actualYaml );
+ $actualValue = Mustangostang\Spyc::YAMLLoad( $actualYaml );
$expectedValue = Mustangostang\Spyc::YAMLLoad( $expectedYaml );
if ( !$actualValue ) {
diff --git a/features/bootstrap/utils.php b/features/bootstrap/utils.php
index 32caac15..2989254e 100644
--- a/features/bootstrap/utils.php
+++ b/features/bootstrap/utils.php
@@ -1,6 +1,6 @@
<?php
-// Utilities that do NOT depend on WordPress code.
+
namespace WP_CLI\Utils;
@@ -16,7 +16,7 @@ function inside_phar() {
return 0 === strpos( WP_CLI_ROOT, PHAR_STREAM_PREFIX );
}
-// Files that need to be read by external programs have to be extracted from the Phar archive.
+
function extract_from_phar( $path ) {
if ( ! inside_phar() ) {
return $path;
@@ -30,7 +30,9 @@ function extract_from_phar( $path ) {
register_shutdown_function(
function() use ( $tmp_path ) {
- @unlink( $tmp_path );
+ if ( file_exists( $tmp_path ) ) {
+ unlink( $tmp_path );
+ }
}
);
@@ -58,15 +60,15 @@ function load_dependencies() {
}
if ( ! $has_autoload ) {
- fputs( STDERR, "Internal error: Can't find Composer autoloader.\nTry running: composer install\n" );
+ fwrite( STDERR, "Internal error: Can't find Composer autoloader.\nTry running: composer install\n" );
exit( 3 );
}
}
function get_vendor_paths() {
$vendor_paths = array(
- WP_CLI_ROOT . '/../../../vendor', // part of a larger project / installed via Composer (preferred)
- WP_CLI_ROOT . '/vendor', // top-level project / installed as Git clone
+ WP_CLI_ROOT . '/../../../vendor',
+ WP_CLI_ROOT . '/vendor',
);
$maybe_composer_json = WP_CLI_ROOT . '/../../../composer.json';
if ( file_exists( $maybe_composer_json ) && is_readable( $maybe_composer_json ) ) {
@@ -78,7 +80,7 @@ function get_vendor_paths() {
return $vendor_paths;
}
-// Using require() directly inside a class grants access to private methods to the loaded code
+
function load_file( $path ) {
require_once $path;
}
@@ -91,25 +93,25 @@ function load_command( $name ) {
}
}
-/**
- * Like array_map(), except it returns a new iterator, instead of a modified array.
- *
- * Example:
- *
- * $arr = array('Football', 'Socker');
- *
- * $it = iterator_map($arr, 'strtolower', function($val) {
- * return str_replace('foo', 'bar', $val);
- * });
- *
- * foreach ( $it as $val ) {
- * var_dump($val);
- * }
- *
- * @param array|object Either a plain array or another iterator
- * @param callback The function to apply to an element
- * @return object An iterator that applies the given callback(s)
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
function iterator_map( $it, $fn ) {
if ( is_array( $it ) ) {
$it = new \ArrayIterator( $it );
@@ -126,20 +128,20 @@ function iterator_map( $it, $fn ) {
return $it;
}
-/**
- * Search for file by walking up the directory tree until the first file is found or until $stop_check($dir) returns true
- * @param string|array The files (or file) to search for
- * @param string|null The directory to start searching from; defaults to CWD
- * @param callable Function which is passed the current dir each time a directory level is traversed
- * @return null|string Null if the file was not found
- */
+
+
+
+
+
+
+
function find_file_upward( $files, $dir = null, $stop_check = null ) {
$files = (array) $files;
if ( is_null( $dir ) ) {
$dir = getcwd();
}
- while ( @is_readable( $dir ) ) {
- // Stop walking up when the supplied callable returns true being passed the $dir
+ while ( is_readable( $dir ) ) {
+
if ( is_callable( $stop_check ) && call_user_func( $stop_check, $dir ) ) {
return null;
}
@@ -161,30 +163,30 @@ function find_file_upward( $files, $dir = null, $stop_check = null ) {
}
function is_path_absolute( $path ) {
- // Windows
+
if ( isset( $path[1] ) && ':' === $path[1] ) {
return true;
}
- return $path[0] === '/';
+ return '/' === $path[0];
}
-/**
- * Composes positional arguments into a command string.
- *
- * @param array
- * @return string
- */
+
+
+
+
+
+
function args_to_str( $args ) {
return ' ' . implode( ' ', array_map( 'escapeshellarg', $args ) );
}
-/**
- * Composes associative arguments into a command string.
- *
- * @param array
- * @return string
- */
+
+
+
+
+
+
function assoc_args_to_str( $assoc_args ) {
$str = '';
@@ -207,10 +209,10 @@ function assoc_args_to_str( $assoc_args ) {
return $str;
}
-/**
- * Given a template string and an arbitrary number of arguments,
- * returns the final command, with the parameters escaped.
- */
+
+
+
+
function esc_cmd( $cmd ) {
if ( func_num_args() < 2 ) {
trigger_error( 'esc_cmd() requires at least two arguments.', E_USER_WARNING );
@@ -227,12 +229,12 @@ function locate_wp_config() {
static $path;
if ( null === $path ) {
+ $path = false;
+
if ( file_exists( ABSPATH . 'wp-config.php' ) ) {
$path = ABSPATH . 'wp-config.php';
} elseif ( file_exists( ABSPATH . '../wp-config.php' ) && ! file_exists( ABSPATH . '/../wp-settings.php' ) ) {
$path = ABSPATH . '../wp-config.php';
- } else {
- $path = false;
}
if ( $path ) {
@@ -244,69 +246,71 @@ function locate_wp_config() {
}
function wp_version_compare( $since, $operator ) {
- return version_compare( str_replace( array( '-src' ), '', $GLOBALS['wp_version'] ), $since, $operator );
+ $wp_version = str_replace( '-src', '', $GLOBALS['wp_version'] );
+ $since = str_replace( '-src', '', $since );
+ return version_compare( $wp_version, $since, $operator );
}
-/**
- * Render a collection of items as an ASCII table, JSON, CSV, YAML, list of ids, or count.
- *
- * Given a collection of items with a consistent data structure:
- *
- * ```
- * $items = array(
- * array(
- * 'key' => 'foo',
- * 'value' => 'bar',
- * )
- * );
- * ```
- *
- * Render `$items` as an ASCII table:
- *
- * ```
- * WP_CLI\Utils\format_items( 'table', $items, array( 'key', 'value' ) );
- *
- * # +-----+-------+
- * # | key | value |
- * # +-----+-------+
- * # | foo | bar |
- * # +-----+-------+
- * ```
- *
- * Or render `$items` as YAML:
- *
- * ```
- * WP_CLI\Utils\format_items( 'yaml', $items, array( 'key', 'value' ) );
- *
- * # ---
- * # -
- * # key: foo
- * # value: bar
- * ```
- *
- * @access public
- * @category Output
- *
- * @param string $format Format to use: 'table', 'json', 'csv', 'yaml', 'ids', 'count'
- * @param array $items An array of items to output.
- * @param array|string $fields Named fields for each item of data. Can be array or comma-separated list.
- * @return null
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
function format_items( $format, $items, $fields ) {
$assoc_args = compact( 'format', 'fields' );
$formatter = new \WP_CLI\Formatter( $assoc_args );
$formatter->display_items( $items );
}
-/**
- * Write data as CSV to a given file.
- *
- * @access public
- *
- * @param resource $fd File descriptor
- * @param array $rows Array of rows to output
- * @param array $headers List of CSV columns (optional)
- */
+
+
+
+
+
+
+
+
+
function write_csv( $fd, $rows, $headers = array() ) {
if ( ! empty( $headers ) ) {
fputcsv( $fd, $headers );
@@ -321,13 +325,13 @@ function write_csv( $fd, $rows, $headers = array() ) {
}
}
-/**
- * Pick fields from an associative array or object.
- *
- * @param array|object Associative array or object to pick fields from
- * @param array List of fields to pick
- * @return array
- */
+
+
+
+
+
+
+
function pick_fields( $item, $fields ) {
$item = (object) $item;
@@ -340,15 +344,15 @@ function pick_fields( $item, $fields ) {
return $values;
}
-/**
- * Launch system's $EDITOR for the user to edit some text.
- *
- * @access public
- * @category Input
- *
- * @param string $content Some form of text to edit (e.g. post content)
- * @return string|bool Edited text, if file is saved from editor; false, if no change to file.
- */
+
+
+
+
+
+
+
+
+
function launch_editor_for_input( $input, $filename = 'WP-CLI' ) {
check_proc_available( 'launch_editor_for_input' );
@@ -358,9 +362,9 @@ function launch_editor_for_input( $input, $filename = 'WP-CLI' ) {
do {
$tmpfile = basename( $filename );
$tmpfile = preg_replace( '|\.[^.]*$|', '', $tmpfile );
- $tmpfile .= '-' . substr( md5( rand() ), 0, 6 );
+ $tmpfile .= '-' . substr( md5( mt_rand() ), 0, 6 );
$tmpfile = $tmpdir . $tmpfile . '.tmp';
- $fp = @fopen( $tmpfile, 'x' );
+ $fp = fopen( $tmpfile, 'xb' );
if ( ! $fp && is_writable( $tmpdir ) && file_exists( $tmpfile ) ) {
$tmpfile = '';
continue;
@@ -379,10 +383,10 @@ function launch_editor_for_input( $input, $filename = 'WP-CLI' ) {
$editor = getenv( 'EDITOR' );
if ( ! $editor ) {
+ $editor = 'vi';
+
if ( isset( $_SERVER['OS'] ) && false !== strpos( $_SERVER['OS'], 'indows' ) ) {
$editor = 'notepad';
- } else {
- $editor = 'vi';
}
}
@@ -404,21 +408,21 @@ function launch_editor_for_input( $input, $filename = 'WP-CLI' ) {
return $output;
}
-/**
- * @param string MySQL host string, as defined in wp-config.php
- * @return array
- */
+
+
+
+
function mysql_host_to_cli_args( $raw_host ) {
$assoc_args = array();
- $host_parts = explode( ':', $raw_host );
+ $host_parts = explode( ':', $raw_host );
if ( count( $host_parts ) == 2 ) {
list( $assoc_args['host'], $extra ) = $host_parts;
$extra = trim( $extra );
if ( is_numeric( $extra ) ) {
- $assoc_args['port'] = intval( $extra );
+ $assoc_args['port'] = (int) $extra;
$assoc_args['protocol'] = 'tcp';
- } elseif ( $extra !== '' ) {
+ } elseif ( '' !== $extra ) {
$assoc_args['socket'] = $extra;
}
} else {
@@ -436,7 +440,9 @@ function run_mysql_command( $cmd, $assoc_args, $descriptors = null ) {
}
if ( isset( $assoc_args['host'] ) ) {
+
$assoc_args = array_merge( $assoc_args, mysql_host_to_cli_args( $assoc_args['host'] ) );
+
}
$pass = $assoc_args['pass'];
@@ -461,11 +467,11 @@ function run_mysql_command( $cmd, $assoc_args, $descriptors = null ) {
}
}
-/**
- * Render PHP or other types of files using Mustache templates.
- *
- * IMPORTANT: Automatic HTML escaping is disabled!
- */
+
+
+
+
+
function mustache_render( $template_name, $data = array() ) {
if ( ! file_exists( $template_name ) ) {
$template_name = WP_CLI_ROOT . "/templates/$template_name";
@@ -483,34 +489,34 @@ function mustache_render( $template_name, $data = array() ) {
return $m->render( $template, $data );
}
-/**
- * Create a progress bar to display percent completion of a given operation.
- *
- * Progress bar is written to STDOUT, and disabled when command is piped. Progress
- * advances with `$progress->tick()`, and completes with `$progress->finish()`.
- * Process bar also indicates elapsed time and expected total time.
- *
- * ```
- * # `wp user generate` ticks progress bar each time a new user is created.
- * #
- * # $ wp user generate --count=500
- * # Generating users 22 % [=======> ] 0:05 / 0:23
- *
- * $progress = \WP_CLI\Utils\make_progress_bar( 'Generating users', $count );
- * for ( $i = 0; $i < $count; $i++ ) {
- * // uses wp_insert_user() to insert the user
- * $progress->tick();
- * }
- * $progress->finish();
- * ```
- *
- * @access public
- * @category Output
- *
- * @param string $message Text to display before the progress bar.
- * @param integer $count Total number of ticks to be performed.
- * @return cli\progress\Bar|WP_CLI\NoOp
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
function make_progress_bar( $message, $count ) {
if ( \cli\Shell::isPiped() ) {
return new \WP_CLI\NoOp;
@@ -529,25 +535,25 @@ function parse_url( $url ) {
return $url_parts;
}
-/**
- * Check if we're running in a Windows environment (cmd.exe).
- *
- * @return bool
- */
+
+
+
+
+
function is_windows() {
return false !== ( $test_is_windows = getenv( 'WP_CLI_TEST_IS_WINDOWS' ) ) ? (bool) $test_is_windows : strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
}
-/**
- * Replace magic constants in some PHP source code.
- *
- * @param string $source The PHP code to manipulate.
- * @param string $path The path to use instead of the magic constants
- */
+
+
+
+
+
+
function replace_path_consts( $source, $path ) {
$replacements = array(
'__FILE__' => "'$path'",
- '__DIR__' => "'" . dirname( $path ) . "'",
+ '__DIR__' => "'" . dirname( $path ) . "'",
);
$old = array_keys( $replacements );
@@ -556,33 +562,34 @@ function replace_path_consts( $source, $path ) {
return str_replace( $old, $new, $source );
}
-/**
- * Make a HTTP request to a remote URL.
- *
- * Wraps the Requests HTTP library to ensure every request includes a cert.
- *
- * ```
- * # `wp core download` verifies the hash for a downloaded WordPress archive
- *
- * $md5_response = Utils\http_request( 'GET', $download_url . '.md5' );
- * if ( 20 != substr( $md5_response->status_code, 0, 2 ) ) {
- * WP_CLI::error( "Couldn't access md5 hash for release (HTTP code {$response->status_code})" );
- * }
- * ```
- *
- * @access public
- *
- * @param string $method HTTP method (GET, POST, DELETE, etc.)
- * @param string $url URL to make the HTTP request to.
- * @param array $headers Add specific headers to the request.
- * @param array $options
- * @return object
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
function http_request( $method, $url, $data = null, $headers = array(), $options = array() ) {
$cert_path = '/rmccue/requests/library/Requests/Transport/cacert.pem';
+ $halt_on_error = ! isset( $options['halt_on_error'] ) || (bool) $options['halt_on_error'];
if ( inside_phar() ) {
- // cURL can't read Phar archives
+
$options['verify'] = extract_from_phar(
WP_CLI_VENDOR_DIR . $cert_path
);
@@ -594,61 +601,72 @@ function http_request( $method, $url, $data = null, $headers = array(), $options
}
}
if ( empty( $options['verify'] ) ) {
- WP_CLI::error( 'Cannot find SSL certificate.' );
+ $error_msg = 'Cannot find SSL certificate.';
+ if ( $halt_on_error ) {
+ WP_CLI::error( $error_msg );
+ }
+ throw new \RuntimeException( $error_msg );
}
}
try {
- $request = \Requests::request( $url, $headers, $data, $method, $options );
- return $request;
+ return \Requests::request( $url, $headers, $data, $method, $options );
} catch ( \Requests_Exception $ex ) {
- // CURLE_SSL_CACERT_BADFILE only defined for PHP >= 7.
- if ( 'curlerror' !== $ex->getType() || ! in_array( curl_errno( $ex->getData() ), array( CURLE_SSL_CONNECT_ERROR, CURLE_SSL_CERTPROBLEM, 77 /*CURLE_SSL_CACERT_BADFILE*/ ), true ) ) {
- \WP_CLI::error( sprintf( "Failed to get url '%s': %s.", $url, $ex->getMessage() ) );
+
+ if ( 'curlerror' !== $ex->getType() || ! in_array( curl_errno( $ex->getData() ), array( CURLE_SSL_CONNECT_ERROR, CURLE_SSL_CERTPROBLEM, 77 ), true ) ) {
+ $error_msg = sprintf( "Failed to get url '%s': %s.", $url, $ex->getMessage() );
+ if ( $halt_on_error ) {
+ WP_CLI::error( $error_msg );
+ }
+ throw new \RuntimeException( $error_msg, null, $ex );
}
- // Handle SSL certificate issues gracefully
+
\WP_CLI::warning( sprintf( "Re-trying without verify after failing to get verified url '%s' %s.", $url, $ex->getMessage() ) );
$options['verify'] = false;
try {
return \Requests::request( $url, $headers, $data, $method, $options );
} catch ( \Requests_Exception $ex ) {
- \WP_CLI::error( sprintf( "Failed to get non-verified url '%s' %s.", $url, $ex->getMessage() ) );
+ $error_msg = sprintf( "Failed to get non-verified url '%s' %s.", $url, $ex->getMessage() );
+ if ( $halt_on_error ) {
+ WP_CLI::error( $error_msg );
+ }
+ throw new \RuntimeException( $error_msg, null, $ex );
}
}
}
-/**
- * Increments a version string using the "x.y.z-pre" format
- *
- * Can increment the major, minor or patch number by one
- * If $new_version == "same" the version string is not changed
- * If $new_version is not a known keyword, it will be used as the new version string directly
- *
- * @param string $current_version
- * @param string $new_version
- * @return string
- */
+
+
+
+
+
+
+
+
+
+
+
function increment_version( $current_version, $new_version ) {
- // split version assuming the format is x.y.z-pre
- $current_version = explode( '-', $current_version, 2 );
+
+ $current_version = explode( '-', $current_version, 2 );
$current_version[0] = explode( '.', $current_version[0] );
switch ( $new_version ) {
case 'same':
- // do nothing
+
break;
case 'patch':
$current_version[0][2]++;
- $current_version = array( $current_version[0] ); // drop possible pre-release info
+ $current_version = array( $current_version[0] );
break;
case 'minor':
$current_version[0][1]++;
$current_version[0][2] = 0;
- $current_version = array( $current_version[0] ); // drop possible pre-release info
+ $current_version = array( $current_version[0] );
break;
case 'major':
@@ -656,30 +674,30 @@ function increment_version( $current_version, $new_version ) {
$current_version[0][1] = 0;
$current_version[0][2] = 0;
- $current_version = array( $current_version[0] ); // drop possible pre-release info
+ $current_version = array( $current_version[0] );
break;
- default: // not a keyword
+ default:
$current_version = array( array( $new_version ) );
break;
}
- // reconstruct version string
+
$current_version[0] = implode( '.', $current_version[0] );
- $current_version = implode( '-', $current_version );
+ $current_version = implode( '-', $current_version );
return $current_version;
}
-/**
- * Compare two version strings to get the named semantic version.
- *
- * @access public
- *
- * @param string $new_version
- * @param string $original_version
- * @return string $name 'major', 'minor', 'patch'
- */
+
+
+
+
+
+
+
+
+
function get_named_sem_ver( $new_version, $original_version ) {
if ( ! Comparator::greaterThan( $new_version, $original_version ) ) {
@@ -698,71 +716,73 @@ function get_named_sem_ver( $new_version, $original_version ) {
if ( ! is_null( $minor ) && Semver::satisfies( $new_version, "{$major}.{$minor}.x" ) ) {
return 'patch';
- } elseif ( Semver::satisfies( $new_version, "{$major}.x.x" ) ) {
+ }
+
+ if ( Semver::satisfies( $new_version, "{$major}.x.x" ) ) {
return 'minor';
- } else {
- return 'major';
}
+
+ return 'major';
}
-/**
- * Return the flag value or, if it's not set, the $default value.
- *
- * Because flags can be negated (e.g. --no-quiet to negate --quiet), this
- * function provides a safer alternative to using
- * `isset( $assoc_args['quiet'] )` or similar.
- *
- * @access public
- * @category Input
- *
- * @param array $assoc_args Arguments array.
- * @param string $flag Flag to get the value.
- * @param mixed $default Default value for the flag. Default: NULL
- * @return mixed
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
function get_flag_value( $assoc_args, $flag, $default = null ) {
return isset( $assoc_args[ $flag ] ) ? $assoc_args[ $flag ] : $default;
}
-/**
- * Get the home directory.
- *
- * @access public
- * @category System
- *
- * @return string
- */
+
+
+
+
+
+
+
+
function get_home_dir() {
$home = getenv( 'HOME' );
if ( ! $home ) {
- // In Windows $HOME may not be defined
+
$home = getenv( 'HOMEDRIVE' ) . getenv( 'HOMEPATH' );
}
return rtrim( $home, '/\\' );
}
-/**
- * Appends a trailing slash.
- *
- * @access public
- * @category System
- *
- * @param string $string What to add the trailing slash to.
- * @return string String with trailing slash added.
- */
+
+
+
+
+
+
+
+
+
function trailingslashit( $string ) {
return rtrim( $string, '/\\' ) . '/';
}
-/**
- * Get the system's temp directory. Warns user if it isn't writable.
- *
- * @access public
- * @category System
- *
- * @return string
- */
+
+
+
+
+
+
+
+
function get_temp_dir() {
static $temp = '';
@@ -770,35 +790,35 @@ function get_temp_dir() {
return $temp;
}
- // `sys_get_temp_dir()` introduced PHP 5.2.1.
+ $temp = '/tmp/';
+
+
if ( $try = sys_get_temp_dir() ) {
$temp = trailingslashit( $try );
} elseif ( $try = ini_get( 'upload_tmp_dir' ) ) {
$temp = trailingslashit( $try );
- } else {
- $temp = '/tmp/';
}
- if ( ! @is_writable( $temp ) ) {
+ if ( ! is_writable( $temp ) ) {
\WP_CLI::warning( "Temp directory isn't writable: {$temp}" );
}
return $temp;
}
-/**
- * Parse a SSH url for its host, port, and path.
- *
- * Similar to parse_url(), but adds support for defined SSH aliases.
- *
- * ```
- * host OR host/path/to/wordpress OR host:port/path/to/wordpress
- * ```
- *
- * @access public
- *
- * @return mixed
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
function parse_ssh_url( $url, $component = -1 ) {
preg_match( '#^((docker|docker\-compose|ssh|vagrant):)?(([^@:]+)@)?([^:/~]+)(:([\d]*))?((/|~)(.+))?$#', $url, $matches );
$bits = array();
@@ -829,31 +849,34 @@ function parse_ssh_url( $url, $component = -1 ) {
}
}
-/**
- * Report the results of the same operation against multiple resources.
- *
- * @access public
- * @category Input
- *
- * @param string $noun Resource being affected (e.g. plugin)
- * @param string $verb Type of action happening to the noun (e.g. activate)
- * @param integer $total Total number of resource being affected.
- * @param integer $successes Number of successful operations.
- * @param integer $failures Number of failures.
- */
-function report_batch_operation_results( $noun, $verb, $total, $successes, $failures ) {
+
+
+
+
+
+
+
+
+
+
+
+
+
+function report_batch_operation_results( $noun, $verb, $total, $successes, $failures, $skips = null ) {
$plural_noun = $noun . 's';
$past_tense_verb = past_tense_verb( $verb );
$past_tense_verb_upper = ucfirst( $past_tense_verb );
if ( $failures ) {
+ $failed_skipped_message = null === $skips ? '' : " ({$failures} failed" . ( $skips ? ", {$skips} skipped" : '' ) . ')';
if ( $successes ) {
- WP_CLI::error( "Only {$past_tense_verb} {$successes} of {$total} {$plural_noun}." );
+ WP_CLI::error( "Only {$past_tense_verb} {$successes} of {$total} {$plural_noun}{$failed_skipped_message}." );
} else {
- WP_CLI::error( "No {$plural_noun} {$past_tense_verb}." );
+ WP_CLI::error( "No {$plural_noun} {$past_tense_verb}{$failed_skipped_message}." );
}
} else {
- if ( $successes ) {
- WP_CLI::success( "{$past_tense_verb_upper} {$successes} of {$total} {$plural_noun}." );
+ $skipped_message = $skips ? " ({$skips} skipped)" : '';
+ if ( $successes || $skips ) {
+ WP_CLI::success( "{$past_tense_verb_upper} {$successes} of {$total} {$plural_noun}{$skipped_message}." );
} else {
$message = $total > 1 ? ucfirst( $plural_noun ) : ucfirst( $noun );
WP_CLI::success( "{$message} already {$past_tense_verb}." );
@@ -861,22 +884,22 @@ function report_batch_operation_results( $noun, $verb, $total, $successes, $fail
}
}
-/**
- * Parse a string of command line arguments into an $argv-esqe variable.
- *
- * @access public
- * @category Input
- *
- * @param string $arguments
- * @return array
- */
+
+
+
+
+
+
+
+
+
function parse_str_to_argv( $arguments ) {
preg_match_all( '/(?<=^|\s)([\'"]?)(.+?)(?<!\\\\)\1(?=$|\s)/', $arguments, $matches );
$argv = isset( $matches[0] ) ? $matches[0] : array();
$argv = array_map(
function( $arg ) {
foreach ( array( '"', "'" ) as $char ) {
- if ( $char === substr( $arg, 0, 1 ) && $char === substr( $arg, -1 ) ) {
+ if ( substr( $arg, 0, 1 ) === $char && substr( $arg, -1 ) === $char ) {
$arg = substr( $arg, 1, -1 );
break;
}
@@ -887,57 +910,58 @@ function parse_str_to_argv( $arguments ) {
return $argv;
}
-/**
- * Locale-independent version of basename()
- *
- * @access public
- *
- * @param string $path
- * @param string $suffix
- * @return string
- */
+
+
+
+
+
+
+
+
+
function basename( $path, $suffix = '' ) {
return urldecode( \basename( str_replace( array( '%2F', '%5C' ), '/', urlencode( $path ) ), $suffix ) );
}
-/**
- * Checks whether the output of the current script is a TTY or a pipe / redirect
- *
- * Returns true if STDOUT output is being redirected to a pipe or a file; false is
- * output is being sent directly to the terminal.
- *
- * If an env variable SHELL_PIPE exists, returned result depends it's
- * value. Strings like 1, 0, yes, no, that validate to booleans are accepted.
- *
- * To enable ASCII formatting even when shell is piped, use the
- * ENV variable SHELL_PIPE=0
- *
- * @access public
- *
- * @return bool
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
function isPiped() {
$shellPipe = getenv( 'SHELL_PIPE' );
- if ( $shellPipe !== false ) {
+ if ( false !== $shellPipe ) {
return filter_var( $shellPipe, FILTER_VALIDATE_BOOLEAN );
- } else {
- return (function_exists( 'posix_isatty' ) && ! posix_isatty( STDOUT ));
}
+
+ return (function_exists( 'posix_isatty' ) && ! posix_isatty( STDOUT ));
}
-/**
- * Expand within paths to their matching paths.
- *
- * Has no effect on paths which do not use glob patterns.
- *
- * @param string|array $paths Single path as a string, or an array of paths.
- * @param int $flags Optional. Flags to pass to glob. Defaults to GLOB_BRACE.
- *
- * @return array Expanded paths.
- */
+
+
+
+
+
+
+
+
+
+
function expand_globs( $paths, $flags = 'default' ) {
- // Compatibility for systems without GLOB_BRACE.
+
$glob_func = 'glob';
if ( 'default' === $flags ) {
if ( ! defined( 'GLOB_BRACE' ) || getenv( 'WP_CLI_TEST_EXPAND_GLOBS_NO_GLOB_BRACE' ) ) {
@@ -980,10 +1004,10 @@ function glob_brace( $pattern, $dummy_flags = null ) {
static $next_brace_sub;
if ( ! $next_brace_sub ) {
- // Find the end of the subpattern in a brace expression.
+
$next_brace_sub = function ( $pattern, $current ) {
- $length = strlen( $pattern );
- $depth = 0;
+ $length = strlen( $pattern );
+ $depth = 0;
while ( $current < $length ) {
if ( '\\' === $pattern[ $current ] ) {
@@ -992,9 +1016,11 @@ function glob_brace( $pattern, $dummy_flags = null ) {
}
$current++;
} else {
- if ( ( '}' === $pattern[ $current ] && $depth-- === 0 ) || ( ',' === $pattern[ $current ] && 0 === $depth ) ) {
+ if ( ( '}' === $pattern[ $current ] && 0 === $depth-- ) || ( ',' === $pattern[ $current ] && 0 === $depth ) ) {
break;
- } elseif ( '{' === $pattern[ $current++ ] ) {
+ }
+
+ if ( '{' === $pattern[ $current++ ] ) {
$depth++;
}
}
@@ -1006,7 +1032,7 @@ function glob_brace( $pattern, $dummy_flags = null ) {
$length = strlen( $pattern );
- // Find first opening brace.
+
for ( $begin = 0; $begin < $length; $begin++ ) {
if ( '\\' === $pattern[ $begin ] ) {
$begin++;
@@ -1015,14 +1041,14 @@ function glob_brace( $pattern, $dummy_flags = null ) {
}
}
- // Find comma or matching closing brace.
+
if ( null === ( $next = $next_brace_sub( $pattern, $begin + 1 ) ) ) {
return glob( $pattern );
}
$rest = $next;
- // Point `$rest` to matching closing brace.
+
while ( '}' !== $pattern[ $rest ] ) {
if ( null === ( $rest = $next_brace_sub( $pattern, $rest + 1 ) ) ) {
return glob( $pattern );
@@ -1032,13 +1058,13 @@ function glob_brace( $pattern, $dummy_flags = null ) {
$paths = array();
$p = $begin + 1;
- // For each comma-separated subpattern.
+
do {
$subpattern = substr( $pattern, 0, $begin )
. substr( $pattern, $p, $next - $p )
. substr( $pattern, $rest + 1 );
- if ( ( $result = glob_brace( $subpattern ) ) ) {
+ if ( $result = glob_brace( $subpattern ) ) {
$paths = array_merge( $paths, $result );
}
@@ -1046,30 +1072,55 @@ function glob_brace( $pattern, $dummy_flags = null ) {
break;
}
- $p = $next + 1;
+ $p = $next + 1;
$next = $next_brace_sub( $pattern, $p );
} while ( null !== $next );
return array_values( array_unique( $paths ) );
}
-/**
- * Get the closest suggestion for a mis-typed target term amongst a list of
- * options.
- *
- * Uses the Levenshtein algorithm to calculate the relative "distance" between
- * terms.
- *
- * If the "distance" to the closest term is higher than the threshold, an empty
- * string is returned.
- *
- * @param string $target Target term to get a suggestion for.
- * @param array $options Array with possible options.
- * @param int $threshold Threshold above which to return an empty string.
- *
- * @return string
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
function get_suggestion( $target, array $options, $threshold = 2 ) {
+
+ $suggestion_map = array(
+ 'check' => 'check-update',
+ 'clear' => 'flush',
+ 'decrement' => 'decr',
+ 'del' => 'delete',
+ 'directory' => 'dir',
+ 'exec' => 'eval',
+ 'exec-file' => 'eval-file',
+ 'increment' => 'incr',
+ 'language' => 'locale',
+ 'lang' => 'locale',
+ 'new' => 'create',
+ 'number' => 'count',
+ 'remove' => 'delete',
+ 'regen' => 'regenerate',
+ 'rep' => 'replace',
+ 'repl' => 'replace',
+ 'v' => 'version',
+ );
+
+ if ( array_key_exists( $target, $suggestion_map ) ) {
+ return $suggestion_map[ $target ];
+ }
+
if ( empty( $options ) ) {
return '';
}
@@ -1078,31 +1129,31 @@ function get_suggestion( $target, array $options, $threshold = 2 ) {
$levenshtein[ $option ] = $distance;
}
- // Sort known command strings by distance to user entry.
+
asort( $levenshtein );
- // Fetch the closest command string.
+
reset( $levenshtein );
$suggestion = key( $levenshtein );
- // Only return a suggestion if below a given threshold.
+
return $levenshtein[ $suggestion ] <= $threshold && $suggestion !== $target
? (string) $suggestion
: '';
}
-/**
- * Get a Phar-safe version of a path.
- *
- * For paths inside a Phar, this strips the outer filesystem's location to
- * reduce the path to what it needs to be within the Phar archive.
- *
- * Use the __FILE__ or __DIR__ constants as a starting point.
- *
- * @param string $path An absolute path that might be within a Phar.
- *
- * @return string A Phar-safe version of the path.
- */
+
+
+
+
+
+
+
+
+
+
+
+
function phar_safe_path( $path ) {
if ( ! inside_phar() ) {
@@ -1116,17 +1167,17 @@ function phar_safe_path( $path ) {
);
}
-/**
- * Check whether a given Command object is part of the bundled set of
- * commands.
- *
- * This function accepts both a fully qualified class name as a string as
- * well as an object that extends `WP_CLI\Dispatcher\CompositeCommand`.
- *
- * @param \WP_CLI\Dispatcher\CompositeCommand|string $command
- *
- * @return bool
- */
+
+
+
+
+
+
+
+
+
+
+
function is_bundled_command( $command ) {
static $classes;
@@ -1147,14 +1198,14 @@ function is_bundled_command( $command ) {
: false;
}
-/**
- * Maybe prefix command string with "/usr/bin/env".
- * Removes (if there) if Windows, adds (if not there) if not.
- *
- * @param string $command
- *
- * @return string
- */
+
+
+
+
+
+
+
+
function force_env_on_nix_systems( $command ) {
$env_prefix = '/usr/bin/env ';
$env_prefix_len = strlen( $env_prefix );
@@ -1170,14 +1221,14 @@ function force_env_on_nix_systems( $command ) {
return $command;
}
-/**
- * Check that `proc_open()` and `proc_close()` haven't been disabled.
- *
- * @param string $context Optional. If set will appear in error message. Default null.
- * @param bool $return Optional. If set will return false rather than error out. Default false.
- *
- * @return bool
- */
+
+
+
+
+
+
+
+
function check_proc_available( $context = null, $return = false ) {
if ( ! function_exists( 'proc_open' ) || ! function_exists( 'proc_close' ) ) {
if ( $return ) {
@@ -1193,13 +1244,13 @@ function check_proc_available( $context = null, $return = false ) {
return true;
}
-/**
- * Returns past tense of verb, with limited accuracy. Only regular verbs catered for, apart from "reset".
- *
- * @param string $verb Verb to return past tense of.
- *
- * @return string
- */
+
+
+
+
+
+
+
function past_tense_verb( $verb ) {
static $irregular = array(
'reset' => 'reset',
@@ -1213,7 +1264,7 @@ function past_tense_verb( $verb ) {
} elseif ( 'y' === $last && ! preg_match( '/[aeiou]y$/', $verb ) ) {
$verb = substr( $verb, 0, -1 ) . 'i';
} elseif ( preg_match( '/^[^aeiou]*[aeiou][^aeiouhwxy]$/', $verb ) ) {
- // Rule of thumb that most (all?) one-voweled regular verbs ending in vowel + consonant (excluding "h", "w", "x", "y") double their final consonant - misses many cases (eg "submit").
+
$verb .= $last;
}
return $verb . 'ed';
diff --git a/features/extra/no-mail.php b/features/extra/no-mail.php
index de7a4227..baaf4ccb 100644
--- a/features/extra/no-mail.php
+++ b/features/extra/no-mail.php
@@ -1,7 +1,7 @@
<?php
function wp_mail( $to ) {
- // Log for testing purposes
+
WP_CLI::log( "WP-CLI test suite: Sent email to {$to}." );
}
diff --git a/features/steps/given.php b/features/steps/given.php
index f2eae113..167b1799 100644
--- a/features/steps/given.php
+++ b/features/steps/given.php
@@ -1,8 +1,8 @@
<?php
use Behat\Gherkin\Node\PyStringNode,
- Behat\Gherkin\Node\TableNode,
- WP_CLI\Process;
+Behat\Gherkin\Node\TableNode,
+WP_CLI\Process;
$steps->Given( '/^an empty directory$/',
function ( $world ) {
@@ -21,7 +21,7 @@ $steps->Given( '/^an? (empty|non-existent) ([^\s]+) directory$/',
}
$world->remove_dir( $dir );
if ( 'empty' === $empty_or_nonexistent ) {
- mkdir( $dir, 0777, true /*recursive*/ );
+ mkdir( $dir, 0777, true );
}
}
);
@@ -38,7 +38,7 @@ $steps->Given( '/^an? ([^\s]+) file:$/',
$full_path = $world->variables['RUN_DIR'] . "/$path";
$dir = dirname( $full_path );
if ( ! file_exists( $dir ) ) {
- mkdir( $dir, 0777, true /*recursive*/ );
+ mkdir( $dir, 0777, true );
}
file_put_contents( $full_path, $content );
}
@@ -131,7 +131,7 @@ $steps->Given( '/^download:$/',
foreach ( $table->getHash() as $row ) {
$path = $world->replace_variables( $row['path'] );
if ( file_exists( $path ) ) {
- // assume it's the same file and skip re-download
+
continue;
}
diff --git a/features/steps/then.php b/features/steps/then.php
index aa1f3f67..40efac0e 100644
--- a/features/steps/then.php
+++ b/features/steps/then.php
@@ -1,7 +1,7 @@
<?php
use Behat\Gherkin\Node\PyStringNode,
- Behat\Gherkin\Node\TableNode;
+Behat\Gherkin\Node\TableNode;
$steps->Then( '/^the return code should be (\d+)$/',
function ( $world, $return_code ) {
@@ -42,7 +42,7 @@ $steps->Then( '/^(STDOUT|STDERR) should not be a number$/',
$steps->Then( '/^STDOUT should be a table containing rows:$/',
function ( $world, TableNode $expected ) {
- $output = $world->result->stdout;
+ $output = $world->result->stdout;
$actual_rows = explode( "\n", rtrim( $output, "\n" ) );
$expected_rows = array();
@@ -56,7 +56,7 @@ $steps->Then( '/^STDOUT should be a table containing rows:$/',
$steps->Then( '/^STDOUT should end with a table containing rows:$/',
function ( $world, TableNode $expected ) {
- $output = $world->result->stdout;
+ $output = $world->result->stdout;
$actual_rows = explode( "\n", rtrim( $output, "\n" ) );
$expected_rows = array();
@@ -153,13 +153,13 @@ $steps->Then( '/^(STDOUT|STDERR) should be a version string (<|<=|>|>=|==|=|!=|<
throw new Exception( $world->result );
}
}
-);
+);
$steps->Then( '/^the (.+) (file|directory) should (exist|not exist|be:|contain:|not contain:)$/',
function ( $world, $path, $type, $action, $expected = null ) {
$path = $world->replace_variables( $path );
- // If it's a relative path, make it relative to the current test dir
+
if ( '/' !== $path[0] )
$path = $world->variables['RUN_DIR'] . "/$path";
@@ -200,6 +200,25 @@ $steps->Then( '/^the (.+) (file|directory) should (exist|not exist|be:|contain:|
}
);
+$steps->Then( '/^the contents of the (.+) file should match (((\/.+\/)|(#.+#))([a-z]+)?)$/',
+ function ( $world, $path, $expected ) {
+ $path = $world->replace_variables( $path );
+
+ if ( '/' !== $path[0] ) {
+ $path = $world->variables['RUN_DIR'] . "/$path";
+ }
+ $contents = file_get_contents( $path );
+ assertRegExp( $expected, $contents );
+ }
+);
+
+$steps->Then( '/^(STDOUT|STDERR) should match (((\/.+\/)|(#.+#))([a-z]+)?)$/',
+ function ( $world, $stream, $expected ) {
+ $stream = strtolower( $stream );
+ assertRegExp( $expected, $world->result->$stream );
+ }
+);
+
$steps->Then( '/^an email should (be sent|not be sent)$/', function( $world, $expected ) {
if ( 'be sent' === $expected ) {
assertNotEquals( 0, $world->email_sends );
diff --git a/features/steps/when.php b/features/steps/when.php
index afe3f7a0..835fe9d8 100644
--- a/features/steps/when.php
+++ b/features/steps/when.php
@@ -1,8 +1,8 @@
<?php
use Behat\Gherkin\Node\PyStringNode,
- Behat\Gherkin\Node\TableNode,
- WP_CLI\Process;
+Behat\Gherkin\Node\TableNode,
+WP_CLI\Process;
function invoke_proc( $proc, $mode ) {
$map = array(
diff --git a/utils/behat-tags.php b/utils/behat-tags.php
index ee51fc91..4d09bc79 100644
--- a/utils/behat-tags.php
+++ b/utils/behat-tags.php
@@ -1,17 +1,17 @@
<?php
-/**
- * Generate a list of tags to skip during the test run.
- *
- * Require a minimum version of WordPress:
- *
- * @require-wp-4.0
- * Scenario: Core translation CRUD
- *
- * Then use in bash script:
- *
- * BEHAT_TAGS=$(php behat-tags.php)
- * vendor/bin/behat --format progress $BEHAT_TAGS
- */
+
+
+
+
+
+
+
+
+
+
+
+
+
function version_tags( $prefix, $current, $operator = '<' ) {
if ( ! $current )
@@ -32,8 +32,8 @@ function version_tags( $prefix, $current, $operator = '<' ) {
}
$wp_version_reqs = array();
-// Only apply @require-wp tags when WP_VERSION isn't 'latest' or 'nightly'
-// 'latest' and 'nightly' are expected to work with all features
+
+
if ( ! in_array( getenv( 'WP_VERSION' ), array( 'latest', 'nightly', 'trunk' ), true ) ) {
$wp_version_reqs = version_tags( 'require-wp', getenv( 'WP_VERSION' ), '<' );
}
@@ -44,13 +44,13 @@ $skip_tags = array_merge(
version_tags( 'less-than-php', PHP_VERSION, '>' )
);
-# Skip Github API tests by default because of rate limiting. See https://github.com/wp-cli/wp-cli/issues/1612
+
$skip_tags[] = '@github-api';
-# Skip tests known to be broken.
+
$skip_tags[] = '@broken';
-# Require PHP extension, eg 'imagick'.
+
function extension_tags() {
$extension_tags = array();
exec( "grep '@require-extension-[A-Za-z_]*' -h -o features/*.feature | uniq", $extension_tags );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment