mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2026-07-01 15:50:09 +00:00
Move PHPUnit tests into a tests/phpunit directory.
wp-tests-config.php can/should reside in the root of a develop checkout. `phpunit` should be run from the root. see #25088. git-svn-id: https://develop.svn.wordpress.org/trunk@25165 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
135
tests/phpunit/includes/bootstrap.php
Normal file
135
tests/phpunit/includes/bootstrap.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* Installs WordPress for running the tests and loads WordPress and the test libraries
|
||||
*/
|
||||
|
||||
|
||||
$config_file_path = dirname( dirname( __FILE__ ) );
|
||||
if ( ! file_exists( $config_file_path . '/wp-tests-config.php' ) ) {
|
||||
// Support the config file from the root of the develop repository.
|
||||
if ( basename( $config_file_path ) === 'phpunit' && basename( dirname( $config_file_path ) ) === 'tests' )
|
||||
$config_file_path = dirname( dirname( $config_file_path ) );
|
||||
}
|
||||
$config_file_path .= '/wp-tests-config.php';
|
||||
|
||||
/*
|
||||
* Globalize some WordPress variables, because PHPUnit loads this file inside a function
|
||||
* See: https://github.com/sebastianbergmann/phpunit/issues/325
|
||||
*/
|
||||
global $wpdb, $current_site, $current_blog, $wp_rewrite, $shortcode_tags, $wp, $phpmailer;
|
||||
|
||||
if ( !is_readable( $config_file_path ) ) {
|
||||
die( "ERROR: wp-tests-config.php is missing! Please use wp-tests-config-sample.php to create a config file.\n" );
|
||||
}
|
||||
require_once $config_file_path;
|
||||
|
||||
define( 'DIR_TESTDATA', dirname( __FILE__ ) . '/../data' );
|
||||
|
||||
if ( ! defined( 'WP_TESTS_FORCE_KNOWN_BUGS' ) )
|
||||
define( 'WP_TESTS_FORCE_KNOWN_BUGS', false );
|
||||
|
||||
// Cron tries to make an HTTP request to the blog, which always fails, because tests are run in CLI mode only
|
||||
define( 'DISABLE_WP_CRON', true );
|
||||
|
||||
define( 'WP_MEMORY_LIMIT', -1 );
|
||||
define( 'WP_MAX_MEMORY_LIMIT', -1 );
|
||||
|
||||
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
|
||||
$_SERVER['HTTP_HOST'] = WP_TESTS_DOMAIN;
|
||||
$PHP_SELF = $GLOBALS['PHP_SELF'] = $_SERVER['PHP_SELF'] = '/index.php';
|
||||
|
||||
if ( "1" == getenv( 'WP_MULTISITE' ) ||
|
||||
( defined( 'WP_TESTS_MULTISITE') && WP_TESTS_MULTISITE ) ) {
|
||||
$multisite = true;
|
||||
} else {
|
||||
$multisite = false;
|
||||
}
|
||||
|
||||
// Override the PHPMailer
|
||||
require_once( dirname( __FILE__ ) . '/mock-mailer.php' );
|
||||
$phpmailer = new MockPHPMailer();
|
||||
|
||||
system( WP_PHP_BINARY . ' ' . escapeshellarg( dirname( __FILE__ ) . '/install.php' ) . ' ' . escapeshellarg( $config_file_path ) . ' ' . $multisite );
|
||||
|
||||
if ( $multisite ) {
|
||||
echo "Running as multisite..." . PHP_EOL;
|
||||
define( 'MULTISITE', true );
|
||||
define( 'SUBDOMAIN_INSTALL', false );
|
||||
define( 'DOMAIN_CURRENT_SITE', WP_TESTS_DOMAIN );
|
||||
define( 'PATH_CURRENT_SITE', '/' );
|
||||
define( 'SITE_ID_CURRENT_SITE', 1 );
|
||||
define( 'BLOG_ID_CURRENT_SITE', 1 );
|
||||
$GLOBALS['base'] = '/';
|
||||
} else {
|
||||
echo "Running as single site... To run multisite, use -c multisite.xml" . PHP_EOL;
|
||||
}
|
||||
unset( $multisite );
|
||||
|
||||
require_once dirname( __FILE__ ) . '/functions.php';
|
||||
|
||||
// Preset WordPress options defined in bootstrap file.
|
||||
// Used to activate themes, plugins, as well as other settings.
|
||||
if(isset($GLOBALS['wp_tests_options'])) {
|
||||
function wp_tests_options( $value ) {
|
||||
$key = substr( current_filter(), strlen( 'pre_option_' ) );
|
||||
return $GLOBALS['wp_tests_options'][$key];
|
||||
}
|
||||
|
||||
foreach ( array_keys( $GLOBALS['wp_tests_options'] ) as $key ) {
|
||||
tests_add_filter( 'pre_option_'.$key, 'wp_tests_options' );
|
||||
}
|
||||
}
|
||||
|
||||
// Load WordPress
|
||||
require_once ABSPATH . '/wp-settings.php';
|
||||
|
||||
// Delete any default posts & related data
|
||||
_delete_all_posts();
|
||||
|
||||
require dirname( __FILE__ ) . '/testcase.php';
|
||||
require dirname( __FILE__ ) . '/testcase-xmlrpc.php';
|
||||
require dirname( __FILE__ ) . '/testcase-ajax.php';
|
||||
require dirname( __FILE__ ) . '/exceptions.php';
|
||||
require dirname( __FILE__ ) . '/utils.php';
|
||||
|
||||
/**
|
||||
* A child class of the PHP test runner.
|
||||
*
|
||||
* Not actually used as a runner. Rather, used to access the protected
|
||||
* longOptions property, to parse the arguments passed to the script.
|
||||
*
|
||||
* If it is determined that phpunit was called with a --group that corresponds
|
||||
* to an @ticket annotation (such as `phpunit --group 12345` for bugs marked
|
||||
* as #WP12345), then it is assumed that known bugs should not be skipped.
|
||||
*
|
||||
* If WP_TESTS_FORCE_KNOWN_BUGS is already set in wp-tests-config.php, then
|
||||
* how you call phpunit has no effect.
|
||||
*/
|
||||
class WP_PHPUnit_TextUI_Command extends PHPUnit_TextUI_Command {
|
||||
function __construct( $argv ) {
|
||||
$options = PHPUnit_Util_Getopt::getopt(
|
||||
$argv,
|
||||
'd:c:hv',
|
||||
array_keys( $this->longOptions )
|
||||
);
|
||||
$ajax_message = true;
|
||||
foreach ( $options[0] as $option ) {
|
||||
switch ( $option[0] ) {
|
||||
case '--exclude-group' :
|
||||
$ajax_message = false;
|
||||
continue 2;
|
||||
case '--group' :
|
||||
$groups = explode( ',', $option[1] );
|
||||
foreach ( $groups as $group ) {
|
||||
if ( is_numeric( $group ) || preg_match( '/^(UT|Plugin)\d+$/', $group ) )
|
||||
WP_UnitTestCase::forceTicket( $group );
|
||||
}
|
||||
$ajax_message = ! in_array( 'ajax', $groups );
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if ( $ajax_message )
|
||||
echo "Not running ajax tests... To execute these, use --group ajax." . PHP_EOL;
|
||||
}
|
||||
}
|
||||
new WP_PHPUnit_TextUI_Command( $_SERVER['argv'] );
|
||||
33
tests/phpunit/includes/exceptions.php
Normal file
33
tests/phpunit/includes/exceptions.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
class WP_Tests_Exception extends PHPUnit_Framework_Exception {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* General exception for wp_die()
|
||||
*/
|
||||
class WPDieException extends Exception {}
|
||||
|
||||
/**
|
||||
* Exception for cases of wp_die(), for ajax tests.
|
||||
* This means there was an error (no output, and a call to wp_die)
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Unit Tests
|
||||
* @since 3.4.0
|
||||
*/
|
||||
class WPAjaxDieStopException extends WPDieException {}
|
||||
|
||||
/**
|
||||
* Exception for cases of wp_die(), for ajax tests.
|
||||
* This means execution of the ajax function should be halted, but the unit
|
||||
* test can continue. The function finished normally and there was not an
|
||||
* error (output happened, but wp_die was called to end execution) This is
|
||||
* used with WP_Ajax_Response::send
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Unit Tests
|
||||
* @since 3.4.0
|
||||
*/
|
||||
class WPAjaxDieContinueException extends WPDieException {}
|
||||
344
tests/phpunit/includes/factory.php
Normal file
344
tests/phpunit/includes/factory.php
Normal file
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
|
||||
class WP_UnitTest_Factory {
|
||||
|
||||
/**
|
||||
* @var WP_UnitTest_Factory_For_Post
|
||||
*/
|
||||
public $post;
|
||||
|
||||
/**
|
||||
* @var WP_UnitTest_Factory_For_Attachment
|
||||
*/
|
||||
public $attachment;
|
||||
|
||||
/**
|
||||
* @var WP_UnitTest_Factory_For_Comment
|
||||
*/
|
||||
public $comment;
|
||||
|
||||
/**
|
||||
* @var WP_UnitTest_Factory_For_User
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* @var WP_UnitTest_Factory_For_Term
|
||||
*/
|
||||
public $term;
|
||||
|
||||
/**
|
||||
* @var WP_UnitTest_Factory_For_Term
|
||||
*/
|
||||
public $category;
|
||||
|
||||
/**
|
||||
* @var WP_UnitTest_Factory_For_Term
|
||||
*/
|
||||
public $tag;
|
||||
|
||||
/**
|
||||
* @var WP_UnitTest_Factory_For_Blog
|
||||
*/
|
||||
public $blog;
|
||||
|
||||
function __construct() {
|
||||
$this->post = new WP_UnitTest_Factory_For_Post( $this );
|
||||
$this->attachment = new WP_UnitTest_Factory_For_Attachment( $this );
|
||||
$this->comment = new WP_UnitTest_Factory_For_Comment( $this );
|
||||
$this->user = new WP_UnitTest_Factory_For_User( $this );
|
||||
$this->term = new WP_UnitTest_Factory_For_Term( $this );
|
||||
$this->category = new WP_UnitTest_Factory_For_Term( $this, 'category' );
|
||||
$this->tag = new WP_UnitTest_Factory_For_Term( $this, 'post_tag' );
|
||||
if ( is_multisite() )
|
||||
$this->blog = new WP_UnitTest_Factory_For_Blog( $this );
|
||||
}
|
||||
}
|
||||
|
||||
class WP_UnitTest_Factory_For_Post extends WP_UnitTest_Factory_For_Thing {
|
||||
|
||||
function __construct( $factory = null ) {
|
||||
parent::__construct( $factory );
|
||||
$this->default_generation_definitions = array(
|
||||
'post_status' => 'publish',
|
||||
'post_title' => new WP_UnitTest_Generator_Sequence( 'Post title %s' ),
|
||||
'post_content' => new WP_UnitTest_Generator_Sequence( 'Post content %s' ),
|
||||
'post_excerpt' => new WP_UnitTest_Generator_Sequence( 'Post excerpt %s' ),
|
||||
'post_type' => 'post'
|
||||
);
|
||||
}
|
||||
|
||||
function create_object( $args ) {
|
||||
return wp_insert_post( $args );
|
||||
}
|
||||
|
||||
function update_object( $post_id, $fields ) {
|
||||
$fields['ID'] = $post_id;
|
||||
return wp_update_post( $fields );
|
||||
}
|
||||
|
||||
function get_object_by_id( $post_id ) {
|
||||
return get_post( $post_id );
|
||||
}
|
||||
}
|
||||
|
||||
class WP_UnitTest_Factory_For_Attachment extends WP_UnitTest_Factory_For_Post {
|
||||
|
||||
function create_object( $file, $parent = 0, $args = array() ) {
|
||||
return wp_insert_attachment( $args, $file, $parent );
|
||||
}
|
||||
}
|
||||
|
||||
class WP_UnitTest_Factory_For_User extends WP_UnitTest_Factory_For_Thing {
|
||||
|
||||
function __construct( $factory = null ) {
|
||||
parent::__construct( $factory );
|
||||
$this->default_generation_definitions = array(
|
||||
'user_login' => new WP_UnitTest_Generator_Sequence( 'User %s' ),
|
||||
'user_pass' => 'password',
|
||||
'user_email' => new WP_UnitTest_Generator_Sequence( 'user_%s@example.org' ),
|
||||
);
|
||||
}
|
||||
|
||||
function create_object( $args ) {
|
||||
return wp_insert_user( $args );
|
||||
}
|
||||
|
||||
function update_object( $user_id, $fields ) {
|
||||
$fields['ID'] = $user_id;
|
||||
return wp_update_user( $fields );
|
||||
}
|
||||
|
||||
function get_object_by_id( $user_id ) {
|
||||
return new WP_User( $user_id );
|
||||
}
|
||||
}
|
||||
|
||||
class WP_UnitTest_Factory_For_Comment extends WP_UnitTest_Factory_For_Thing {
|
||||
|
||||
function __construct( $factory = null ) {
|
||||
parent::__construct( $factory );
|
||||
$this->default_generation_definitions = array(
|
||||
'comment_author' => new WP_UnitTest_Generator_Sequence( 'Commenter %s' ),
|
||||
'comment_author_url' => new WP_UnitTest_Generator_Sequence( 'http://example.com/%s/' ),
|
||||
'comment_approved' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
function create_object( $args ) {
|
||||
return wp_insert_comment( $this->addslashes_deep( $args ) );
|
||||
}
|
||||
|
||||
function update_object( $comment_id, $fields ) {
|
||||
$fields['comment_ID'] = $comment_id;
|
||||
return wp_update_comment( $this->addslashes_deep( $fields ) );
|
||||
}
|
||||
|
||||
function create_post_comments( $post_id, $count = 1, $args = array(), $generation_definitions = null ) {
|
||||
$args['comment_post_ID'] = $post_id;
|
||||
return $this->create_many( $count, $args, $generation_definitions );
|
||||
}
|
||||
|
||||
function get_object_by_id( $comment_id ) {
|
||||
return get_comment( $comment_id );
|
||||
}
|
||||
}
|
||||
|
||||
class WP_UnitTest_Factory_For_Blog extends WP_UnitTest_Factory_For_Thing {
|
||||
|
||||
function __construct( $factory = null ) {
|
||||
global $current_site, $base;
|
||||
parent::__construct( $factory );
|
||||
$this->default_generation_definitions = array(
|
||||
'domain' => $current_site->domain,
|
||||
'path' => new WP_UnitTest_Generator_Sequence( $base . 'testpath%s' ),
|
||||
'title' => new WP_UnitTest_Generator_Sequence( 'Site %s' ),
|
||||
'site_id' => $current_site->id,
|
||||
);
|
||||
}
|
||||
|
||||
function create_object( $args ) {
|
||||
$meta = isset( $args['meta'] ) ? $args['meta'] : array();
|
||||
$user_id = isset( $args['user_id'] ) ? $args['user_id'] : get_current_user_id();
|
||||
return wpmu_create_blog( $args['domain'], $args['path'], $args['title'], $user_id, $meta, $args['site_id'] );
|
||||
}
|
||||
|
||||
function update_object( $blog_id, $fields ) {}
|
||||
|
||||
function get_object_by_id( $blog_id ) {
|
||||
return get_blog_details( $blog_id, false );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class WP_UnitTest_Factory_For_Term extends WP_UnitTest_Factory_For_Thing {
|
||||
|
||||
private $taxonomy;
|
||||
const DEFAULT_TAXONOMY = 'post_tag';
|
||||
|
||||
function __construct( $factory = null, $taxonomy = null ) {
|
||||
parent::__construct( $factory );
|
||||
$this->taxonomy = $taxonomy ? $taxonomy : self::DEFAULT_TAXONOMY;
|
||||
$this->default_generation_definitions = array(
|
||||
'name' => new WP_UnitTest_Generator_Sequence( 'Term %s' ),
|
||||
'taxonomy' => $this->taxonomy,
|
||||
'description' => new WP_UnitTest_Generator_Sequence( 'Term description %s' ),
|
||||
);
|
||||
}
|
||||
|
||||
function create_object( $args ) {
|
||||
$args = array_merge( array( 'taxonomy' => $this->taxonomy ), $args );
|
||||
$term_id_pair = wp_insert_term( $args['name'], $args['taxonomy'], $args );
|
||||
if ( is_wp_error( $term_id_pair ) )
|
||||
return $term_id_pair;
|
||||
return $term_id_pair['term_id'];
|
||||
}
|
||||
|
||||
function update_object( $term, $fields ) {
|
||||
$fields = array_merge( array( 'taxonomy' => $this->taxonomy ), $fields );
|
||||
if ( is_object( $term ) )
|
||||
$taxonomy = $term->taxonomy;
|
||||
$term_id_pair = wp_update_term( $term, $taxonomy, $fields );
|
||||
return $term_id_pair['term_id'];
|
||||
}
|
||||
|
||||
function add_post_terms( $post_id, $terms, $taxonomy, $append = true ) {
|
||||
return wp_set_post_terms( $post_id, $terms, $taxonomy, $append );
|
||||
}
|
||||
|
||||
function get_object_by_id( $term_id ) {
|
||||
return get_term( $term_id, $this->taxonomy );
|
||||
}
|
||||
}
|
||||
|
||||
abstract class WP_UnitTest_Factory_For_Thing {
|
||||
|
||||
var $default_generation_definitions;
|
||||
var $factory;
|
||||
|
||||
/**
|
||||
* Creates a new factory, which will create objects of a specific Thing
|
||||
*
|
||||
* @param object $factory Global factory that can be used to create other objects on the system
|
||||
* @param array $default_generation_definitions Defines what default values should the properties of the object have. The default values
|
||||
* can be generators -- an object with next() method. There are some default generators: {@link WP_UnitTest_Generator_Sequence},
|
||||
* {@link WP_UnitTest_Generator_Locale_Name}, {@link WP_UnitTest_Factory_Callback_After_Create}.
|
||||
*/
|
||||
function __construct( $factory, $default_generation_definitions = array() ) {
|
||||
$this->factory = $factory;
|
||||
$this->default_generation_definitions = $default_generation_definitions;
|
||||
}
|
||||
|
||||
abstract function create_object( $args );
|
||||
abstract function update_object( $object, $fields );
|
||||
|
||||
function create( $args = array(), $generation_definitions = null ) {
|
||||
if ( is_null( $generation_definitions ) )
|
||||
$generation_definitions = $this->default_generation_definitions;
|
||||
|
||||
$generated_args = $this->generate_args( $args, $generation_definitions, $callbacks );
|
||||
$created = $this->create_object( $generated_args );
|
||||
if ( !$created || is_wp_error( $created ) )
|
||||
return $created;
|
||||
|
||||
if ( $callbacks ) {
|
||||
$updated_fields = $this->apply_callbacks( $callbacks, $created );
|
||||
$save_result = $this->update_object( $created, $updated_fields );
|
||||
if ( !$save_result || is_wp_error( $save_result ) )
|
||||
return $save_result;
|
||||
}
|
||||
return $created;
|
||||
}
|
||||
|
||||
function create_and_get( $args = array(), $generation_definitions = null ) {
|
||||
$object_id = $this->create( $args, $generation_definitions );
|
||||
return $this->get_object_by_id( $object_id );
|
||||
}
|
||||
|
||||
abstract function get_object_by_id( $object_id );
|
||||
|
||||
function create_many( $count, $args = array(), $generation_definitions = null ) {
|
||||
$results = array();
|
||||
for ( $i = 0; $i < $count; $i++ ) {
|
||||
$results[] = $this->create( $args, $generation_definitions );
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
function generate_args( $args = array(), $generation_definitions = null, &$callbacks = null ) {
|
||||
$callbacks = array();
|
||||
if ( is_null( $generation_definitions ) )
|
||||
$generation_definitions = $this->default_generation_definitions;
|
||||
|
||||
foreach( array_keys( $generation_definitions ) as $field_name ) {
|
||||
if ( !isset( $args[$field_name] ) ) {
|
||||
$generator = $generation_definitions[$field_name];
|
||||
if ( is_scalar( $generator ) )
|
||||
$args[$field_name] = $generator;
|
||||
elseif ( is_object( $generator ) && method_exists( $generator, 'call' ) ) {
|
||||
$callbacks[$field_name] = $generator;
|
||||
} elseif ( is_object( $generator ) )
|
||||
$args[$field_name] = $generator->next();
|
||||
else
|
||||
return new WP_Error( 'invalid_argument', 'Factory default value should be either a scalar or an generator object.' );
|
||||
}
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
function apply_callbacks( $callbacks, $created ) {
|
||||
$updated_fields = array();
|
||||
foreach( $callbacks as $field_name => $generator ) {
|
||||
$updated_fields[$field_name] = $generator->call( $created );
|
||||
}
|
||||
return $updated_fields;
|
||||
}
|
||||
|
||||
function callback( $function ) {
|
||||
return new WP_UnitTest_Factory_Callback_After_Create( $function );
|
||||
}
|
||||
|
||||
function addslashes_deep($value) {
|
||||
if ( is_array( $value ) ) {
|
||||
$value = array_map( array( $this, 'addslashes_deep' ), $value );
|
||||
} elseif ( is_object( $value ) ) {
|
||||
$vars = get_object_vars( $value );
|
||||
foreach ($vars as $key=>$data) {
|
||||
$value->{$key} = $this->addslashes_deep( $data );
|
||||
}
|
||||
} elseif ( is_string( $value ) ) {
|
||||
$value = addslashes( $value );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WP_UnitTest_Generator_Sequence {
|
||||
var $next;
|
||||
var $template_string;
|
||||
|
||||
function __construct( $template_string = '%s', $start = 1 ) {
|
||||
$this->next = $start;
|
||||
$this->template_string = $template_string;
|
||||
}
|
||||
|
||||
function next() {
|
||||
$generated = sprintf( $this->template_string , $this->next );
|
||||
$this->next++;
|
||||
return $generated;
|
||||
}
|
||||
}
|
||||
|
||||
class WP_UnitTest_Factory_Callback_After_Create {
|
||||
var $callback;
|
||||
|
||||
function __construct( $callback ) {
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
function call( $object ) {
|
||||
return call_user_func( $this->callback, $object );
|
||||
}
|
||||
}
|
||||
44
tests/phpunit/includes/functions.php
Normal file
44
tests/phpunit/includes/functions.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
// For adding hooks before loading WP
|
||||
function tests_add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
|
||||
global $wp_filter, $merged_filters;
|
||||
|
||||
$idx = _test_filter_build_unique_id($tag, $function_to_add, $priority);
|
||||
$wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
|
||||
unset( $merged_filters[ $tag ] );
|
||||
return true;
|
||||
}
|
||||
|
||||
function _test_filter_build_unique_id($tag, $function, $priority) {
|
||||
global $wp_filter;
|
||||
static $filter_id_count = 0;
|
||||
|
||||
if ( is_string($function) )
|
||||
return $function;
|
||||
|
||||
if ( is_object($function) ) {
|
||||
// Closures are currently implemented as objects
|
||||
$function = array( $function, '' );
|
||||
} else {
|
||||
$function = (array) $function;
|
||||
}
|
||||
|
||||
if (is_object($function[0]) ) {
|
||||
return spl_object_hash($function[0]) . $function[1];
|
||||
} else if ( is_string($function[0]) ) {
|
||||
// Static Calling
|
||||
return $function[0].$function[1];
|
||||
}
|
||||
}
|
||||
|
||||
function _delete_all_posts() {
|
||||
global $wpdb;
|
||||
|
||||
$all_posts = $wpdb->get_col("SELECT ID from {$wpdb->posts}");
|
||||
if ($all_posts) {
|
||||
foreach ($all_posts as $id)
|
||||
wp_delete_post( $id, true );
|
||||
}
|
||||
}
|
||||
|
||||
67
tests/phpunit/includes/install.php
Normal file
67
tests/phpunit/includes/install.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Installs WordPress for the purpose of the unit-tests
|
||||
*
|
||||
* @todo Reuse the init/load code in init.php
|
||||
*/
|
||||
error_reporting( E_ALL & ~E_DEPRECATED & ~E_STRICT );
|
||||
|
||||
$config_file_path = $argv[1];
|
||||
$multisite = ! empty( $argv[2] );
|
||||
|
||||
define( 'WP_INSTALLING', true );
|
||||
require_once $config_file_path;
|
||||
require_once dirname( __FILE__ ) . '/functions.php';
|
||||
|
||||
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
|
||||
$_SERVER['HTTP_HOST'] = WP_TESTS_DOMAIN;
|
||||
$PHP_SELF = $GLOBALS['PHP_SELF'] = $_SERVER['PHP_SELF'] = '/index.php';
|
||||
|
||||
require_once ABSPATH . '/wp-settings.php';
|
||||
|
||||
require_once ABSPATH . '/wp-admin/includes/upgrade.php';
|
||||
require_once ABSPATH . '/wp-includes/wp-db.php';
|
||||
|
||||
define( 'WP_TESTS_VERSION_FILE', ABSPATH . '.wp-tests-version' );
|
||||
|
||||
$wpdb->suppress_errors();
|
||||
$installed = $wpdb->get_var( "SELECT option_value FROM $wpdb->options WHERE option_name = 'siteurl'" );
|
||||
$wpdb->suppress_errors( false );
|
||||
|
||||
$hash = get_option( 'db_version' ) . ' ' . (int) $multisite . ' ' . sha1_file( $config_file_path );
|
||||
|
||||
if ( $installed && file_exists( WP_TESTS_VERSION_FILE ) && file_get_contents( WP_TESTS_VERSION_FILE ) == $hash )
|
||||
return;
|
||||
|
||||
$wpdb->query( 'SET storage_engine = INNODB' );
|
||||
$wpdb->select( DB_NAME, $wpdb->dbh );
|
||||
|
||||
echo "Installing..." . PHP_EOL;
|
||||
|
||||
foreach ( $wpdb->tables() as $table => $prefixed_table ) {
|
||||
$wpdb->query( "DROP TABLE IF EXISTS $prefixed_table" );
|
||||
}
|
||||
|
||||
foreach ( $wpdb->tables( 'ms_global' ) as $table => $prefixed_table ) {
|
||||
$wpdb->query( "DROP TABLE IF EXISTS $prefixed_table" );
|
||||
|
||||
// We need to create references to ms global tables.
|
||||
if ( $multisite )
|
||||
$wpdb->$table = $prefixed_table;
|
||||
}
|
||||
|
||||
wp_install( WP_TESTS_TITLE, 'admin', WP_TESTS_EMAIL, true, null, 'password' );
|
||||
|
||||
if ( $multisite ) {
|
||||
echo "Installing network..." . PHP_EOL;
|
||||
|
||||
define( 'WP_INSTALLING_NETWORK', true );
|
||||
|
||||
$title = WP_TESTS_TITLE . ' Network';
|
||||
$subdomain_install = false;
|
||||
|
||||
install_network();
|
||||
populate_network( 1, WP_TESTS_DOMAIN, WP_TESTS_EMAIL, $title, '/', $subdomain_install );
|
||||
}
|
||||
|
||||
file_put_contents( WP_TESTS_VERSION_FILE, $hash );
|
||||
226
tests/phpunit/includes/mock-fs.php
Normal file
226
tests/phpunit/includes/mock-fs.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
class WP_Filesystem_MockFS extends WP_Filesystem_Base {
|
||||
private $cwd;
|
||||
|
||||
// Holds a array of objects which contain an array of objects, etc.
|
||||
private $fs = null;
|
||||
|
||||
// Holds a array of /path/to/file.php and /path/to/dir/ map to an object in $fs above
|
||||
// a fast more efficient way of determining if a path exists, and access to that node
|
||||
private $fs_map = array();
|
||||
|
||||
public $verbose = false; // Enable to debug WP_Filesystem_Base::find_folder() / etc.
|
||||
public $errors = array();
|
||||
public $method = 'MockFS';
|
||||
|
||||
function __construct() {}
|
||||
|
||||
function connect() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy of core's function, but accepts a path.
|
||||
function abspath( $path = false ) {
|
||||
if ( ! $path )
|
||||
$path = ABSPATH;
|
||||
$folder = $this->find_folder( $path );
|
||||
|
||||
// Perhaps the FTP folder is rooted at the WordPress install, Check for wp-includes folder in root, Could have some false positives, but rare.
|
||||
if ( ! $folder && $this->is_dir('/wp-includes') )
|
||||
$folder = '/';
|
||||
return $folder;
|
||||
}
|
||||
|
||||
// Mock FS specific functions:
|
||||
|
||||
/**
|
||||
* Sets initial filesystem environment and/or clears the current environment.
|
||||
* Can also be passed the initial filesystem to be setup which is passed to self::setfs()
|
||||
*/
|
||||
function init( $paths = '', $home_dir = '/' ) {
|
||||
$this->fs = new MockFS_Directory_Node( '/' );
|
||||
$this->fs_map = array(
|
||||
'/' => $this->fs,
|
||||
);
|
||||
$this->cache = array(); // Used by find_folder() and friends
|
||||
$this->cwd = isset( $this->fs_map[ $home_dir ] ) ? $this->fs_map[ $home_dir ] : '/';
|
||||
$this->setfs( $paths );
|
||||
}
|
||||
|
||||
/**
|
||||
* "Bulk Loads" a filesystem into the internal virtual filesystem
|
||||
*/
|
||||
function setfs( $paths ) {
|
||||
if ( ! is_array($paths) )
|
||||
$paths = explode( "\n", $paths );
|
||||
|
||||
$paths = array_filter( array_map( 'trim', $paths ) );
|
||||
|
||||
foreach ( $paths as $path ) {
|
||||
// Allow for comments
|
||||
if ( '#' == $path[0] )
|
||||
continue;
|
||||
|
||||
// Directories
|
||||
if ( '/' == $path[ strlen($path) -1 ] )
|
||||
$this->mkdir( $path );
|
||||
else // Files (with dummy content for now)
|
||||
$this->put_contents( $path, 'This is a test file' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates a filesystem "node"
|
||||
*/
|
||||
private function locate_node( $path ) {
|
||||
return isset( $this->fs_map[ $path ] ) ? $this->fs_map[ $path ] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates a filesystem node for the parent of the given item
|
||||
*/
|
||||
private function locate_parent_node( $path ) {
|
||||
return $this->locate_node( trailingslashit( dirname( $path ) ) );
|
||||
}
|
||||
|
||||
// Here starteth the WP_Filesystem functions.
|
||||
|
||||
function mkdir( $path, /* Optional args are ignored */ $chmod = false, $chown = false, $chgrp = false ) {
|
||||
$path = trailingslashit( $path );
|
||||
|
||||
$parent_node = $this->locate_parent_node( $path );
|
||||
if ( ! $parent_node ) {
|
||||
$this->mkdir( dirname( $path ) );
|
||||
$parent_node = $this->locate_parent_node( $path );
|
||||
if ( ! $parent_node )
|
||||
return false;
|
||||
}
|
||||
|
||||
$node = new MockFS_Directory_Node( $path );
|
||||
|
||||
$parent_node->children[ $node->name ] = $node;
|
||||
$this->fs_map[ $path ] = $node;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function put_contents( $path, $contents = '', $mode = null ) {
|
||||
if ( ! $this->is_dir( dirname( $path ) ) )
|
||||
$this->mkdir( dirname( $path ) );
|
||||
|
||||
$parent = $this->locate_parent_node( $path );
|
||||
$new_file = new MockFS_File_Node( $path, $contents );
|
||||
|
||||
$parent->children[ $new_file->name ] = $new_file;
|
||||
$this->fs_map[ $path ] = $new_file;
|
||||
}
|
||||
|
||||
function get_contents( $file ) {
|
||||
if ( ! $this->is_file( $file ) )
|
||||
return false;
|
||||
return $this->fs_map[ $file ]->contents;
|
||||
}
|
||||
|
||||
function cwd() {
|
||||
return $this->cwd->path;
|
||||
}
|
||||
|
||||
function chdir( $path ) {
|
||||
if ( ! isset( $this->fs_map[ $path ] ) )
|
||||
return false;
|
||||
|
||||
$this->cwd = $this->fs_map[ $path ];
|
||||
return true;
|
||||
}
|
||||
|
||||
function exists( $path ) {
|
||||
return isset( $this->fs_map[ $path ] ) || isset( $this->fs_map[ trailingslashit( $path ) ] );
|
||||
}
|
||||
|
||||
function is_file( $file ) {
|
||||
return isset( $this->fs_map[ $file ] ) && $this->fs_map[ $file ]->is_file();
|
||||
}
|
||||
|
||||
function is_dir( $path ) {
|
||||
$path = trailingslashit( $path );
|
||||
|
||||
return isset( $this->fs_map[ $path ] ) && $this->fs_map[ $path ]->is_dir();
|
||||
}
|
||||
|
||||
function dirlist( $path = '.', $include_hidden = true, $recursive = false ) {
|
||||
|
||||
if ( empty( $path ) || '.' == $path )
|
||||
$path = $this->cwd();
|
||||
|
||||
if ( ! $this->exists( $path ) )
|
||||
return false;
|
||||
|
||||
$limit_file = false;
|
||||
if ( $this->is_file( $path ) ) {
|
||||
$limit_file = $this->locate_node( $path )->name;
|
||||
$path = dirname( $path ) . '/';
|
||||
}
|
||||
|
||||
$ret = array();
|
||||
foreach ( $this->fs_map[ $path ]->children as $entry ) {
|
||||
if ( '.' == $entry->name || '..' == $entry->name )
|
||||
continue;
|
||||
|
||||
if ( ! $include_hidden && '.' == $entry->name )
|
||||
continue;
|
||||
|
||||
if ( $limit_file && $entry->name != $limit_file )
|
||||
continue;
|
||||
|
||||
$struc = array();
|
||||
$struc['name'] = $entry->name;
|
||||
$struc['type'] = $entry->type;
|
||||
|
||||
if ( 'd' == $struc['type'] ) {
|
||||
if ( $recursive )
|
||||
$struc['files'] = $this->dirlist( trailingslashit( $path ) . trailingslashit( $struc['name'] ), $include_hidden, $recursive );
|
||||
else
|
||||
$struc['files'] = array();
|
||||
}
|
||||
|
||||
$ret[ $entry->name ] = $struc;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MockFS_Node {
|
||||
public $name; // The "name" of the entry, does not include a slash (exception, root)
|
||||
public $type; // The type of the entry 'f' for file, 'd' for Directory
|
||||
public $path; // The full path to the entry.
|
||||
|
||||
function __construct( $path ) {
|
||||
$this->path = $path;
|
||||
$this->name = basename( $path );
|
||||
}
|
||||
|
||||
function is_file() {
|
||||
return $this->type == 'f';
|
||||
}
|
||||
|
||||
function is_dir() {
|
||||
return $this->type == 'd';
|
||||
}
|
||||
}
|
||||
|
||||
class MockFS_Directory_Node extends MockFS_Node {
|
||||
public $type = 'd';
|
||||
public $children = array(); // The child nodes of this directory
|
||||
}
|
||||
|
||||
class MockFS_File_Node extends MockFS_Node {
|
||||
public $type = 'f';
|
||||
public $contents = ''; // The contents of the file
|
||||
|
||||
function __construct( $path, $contents = '' ) {
|
||||
parent::__construct( $path );
|
||||
$this->contents = $contents;
|
||||
}
|
||||
}
|
||||
43
tests/phpunit/includes/mock-image-editor.php
Normal file
43
tests/phpunit/includes/mock-image-editor.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
if (class_exists( 'WP_Image_Editor' ) ) :
|
||||
|
||||
class WP_Image_Editor_Mock extends WP_Image_Editor {
|
||||
|
||||
public static $load_return = true;
|
||||
public static $test_return = true;
|
||||
public static $save_return = array();
|
||||
|
||||
public function load() {
|
||||
return self::$load_return;
|
||||
}
|
||||
public static function test() {
|
||||
return self::$test_return;
|
||||
}
|
||||
public static function supports_mime_type( $mime_type ) {
|
||||
return true;
|
||||
}
|
||||
public function resize( $max_w, $max_h, $crop = false ) {
|
||||
|
||||
}
|
||||
public function multi_resize( $sizes ) {
|
||||
|
||||
}
|
||||
public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
|
||||
|
||||
}
|
||||
public function rotate( $angle ) {
|
||||
|
||||
}
|
||||
public function flip( $horz, $vert ) {
|
||||
|
||||
}
|
||||
public function save( $destfilename = null, $mime_type = null ) {
|
||||
return self::$save_return;
|
||||
}
|
||||
public function stream( $mime_type = null ) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
||||
26
tests/phpunit/includes/mock-mailer.php
Normal file
26
tests/phpunit/includes/mock-mailer.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
require_once( ABSPATH . '/wp-includes/class-phpmailer.php' );
|
||||
|
||||
class MockPHPMailer extends PHPMailer {
|
||||
var $mock_sent = array();
|
||||
|
||||
// override the Send function so it doesn't actually send anything
|
||||
function Send() {
|
||||
try {
|
||||
if ( ! $this->PreSend() )
|
||||
return false;
|
||||
|
||||
$this->mock_sent[] = array(
|
||||
'to' => $this->to,
|
||||
'cc' => $this->cc,
|
||||
'bcc' => $this->bcc,
|
||||
'header' => $this->MIMEHeader,
|
||||
'body' => $this->MIMEBody,
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch ( phpmailerException $e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
182
tests/phpunit/includes/testcase-ajax.php
Normal file
182
tests/phpunit/includes/testcase-ajax.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
/**
|
||||
* Ajax test cases
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage UnitTests
|
||||
* @since 3.4.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ajax test case class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage UnitTests
|
||||
* @since 3.4.0
|
||||
*/
|
||||
abstract class WP_Ajax_UnitTestCase extends WP_UnitTestCase {
|
||||
|
||||
/**
|
||||
* Last AJAX response. This is set via echo -or- wp_die.
|
||||
* @var type
|
||||
*/
|
||||
protected $_last_response = '';
|
||||
|
||||
/**
|
||||
* List of ajax actions called via POST
|
||||
* @var type
|
||||
*/
|
||||
protected $_core_actions_get = array( 'fetch-list', 'ajax-tag-search', 'wp-compression-test', 'imgedit-preview', 'oembed_cache' );
|
||||
|
||||
/**
|
||||
* Saved error reporting level
|
||||
* @var int
|
||||
*/
|
||||
protected $_error_level = 0;
|
||||
|
||||
/**
|
||||
* List of ajax actions called via GET
|
||||
* @var type
|
||||
*/
|
||||
protected $_core_actions_post = array(
|
||||
'oembed_cache', 'image-editor', 'delete-comment', 'delete-tag', 'delete-link',
|
||||
'delete-meta', 'delete-post', 'trash-post', 'untrash-post', 'delete-page', 'dim-comment',
|
||||
'add-link-category', 'add-tag', 'get-tagcloud', 'get-comments', 'replyto-comment',
|
||||
'edit-comment', 'add-menu-item', 'add-meta', 'add-user', 'autosave', 'closed-postboxes',
|
||||
'hidden-columns', 'update-welcome-panel', 'menu-get-metabox', 'wp-link-ajax',
|
||||
'menu-locations-save', 'menu-quick-search', 'meta-box-order', 'get-permalink',
|
||||
'sample-permalink', 'inline-save', 'inline-save-tax', 'find_posts', 'widgets-order',
|
||||
'save-widget', 'set-post-thumbnail', 'date_format', 'time_format', 'wp-fullscreen-save-post',
|
||||
'wp-remove-post-lock', 'dismiss-wp-pointer', 'nopriv_autosave'
|
||||
);
|
||||
|
||||
/**
|
||||
* Set up the test fixture.
|
||||
* Override wp_die(), pretend to be ajax, and suppres E_WARNINGs
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Register the core actions
|
||||
foreach ( array_merge( $this->_core_actions_get, $this->_core_actions_post ) as $action )
|
||||
if ( function_exists( 'wp_ajax_' . str_replace( '-', '_', $action ) ) )
|
||||
add_action( 'wp_ajax_' . $action, 'wp_ajax_' . str_replace( '-', '_', $action ), 1 );
|
||||
|
||||
add_filter( 'wp_die_ajax_handler', array( $this, 'getDieHandler' ), 1, 1 );
|
||||
if ( !defined( 'DOING_AJAX' ) )
|
||||
define( 'DOING_AJAX', true );
|
||||
set_current_screen( 'ajax' );
|
||||
|
||||
// Clear logout cookies
|
||||
add_action( 'clear_auth_cookie', array( $this, 'logout' ) );
|
||||
|
||||
// Suppress warnings from "Cannot modify header information - headers already sent by"
|
||||
$this->_error_level = error_reporting();
|
||||
error_reporting( $this->_error_level & ~E_WARNING );
|
||||
|
||||
// Make some posts
|
||||
$this->factory->post->create_many( 5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tear down the test fixture.
|
||||
* Reset $_POST, remove the wp_die() override, restore error reporting
|
||||
*/
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
$_POST = array();
|
||||
$_GET = array();
|
||||
unset( $GLOBALS['post'] );
|
||||
unset( $GLOBALS['comment'] );
|
||||
remove_filter( 'wp_die_ajax_handler', array( $this, 'getDieHandler' ), 1, 1 );
|
||||
remove_action( 'clear_auth_cookie', array( $this, 'logout' ) );
|
||||
error_reporting( $this->_error_level );
|
||||
set_current_screen( 'front' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear login cookies, unset the current user
|
||||
*/
|
||||
public function logout() {
|
||||
unset( $GLOBALS['current_user'] );
|
||||
$cookies = array(AUTH_COOKIE, SECURE_AUTH_COOKIE, LOGGED_IN_COOKIE, USER_COOKIE, PASS_COOKIE);
|
||||
foreach ( $cookies as $c )
|
||||
unset( $_COOKIE[$c] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return our callback handler
|
||||
* @return callback
|
||||
*/
|
||||
public function getDieHandler() {
|
||||
return array( $this, 'dieHandler' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for wp_die()
|
||||
* Save the output for analysis, stop execution by throwing an exception.
|
||||
* Error conditions (no output, just die) will throw <code>WPAjaxDieStopException( $message )</code>
|
||||
* You can test for this with:
|
||||
* <code>
|
||||
* $this->setExpectedException( 'WPAjaxDieStopException', 'something contained in $message' );
|
||||
* </code>
|
||||
* Normal program termination (wp_die called at then end of output) will throw <code>WPAjaxDieContinueException( $message )</code>
|
||||
* You can test for this with:
|
||||
* <code>
|
||||
* $this->setExpectedException( 'WPAjaxDieContinueException', 'something contained in $message' );
|
||||
* </code>
|
||||
* @param string $message
|
||||
*/
|
||||
public function dieHandler( $message ) {
|
||||
$this->_last_response .= ob_get_clean();
|
||||
ob_end_clean();
|
||||
if ( '' === $this->_last_response ) {
|
||||
if ( is_scalar( $message) ) {
|
||||
throw new WPAjaxDieStopException( (string) $message );
|
||||
} else {
|
||||
throw new WPAjaxDieStopException( '0' );
|
||||
}
|
||||
} else {
|
||||
throw new WPAjaxDieContinueException( $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between user roles
|
||||
* E.g. administrator, editor, author, contributor, subscriber
|
||||
* @param string $role
|
||||
*/
|
||||
protected function _setRole( $role ) {
|
||||
$post = $_POST;
|
||||
$user_id = $this->factory->user->create( array( 'role' => $role ) );
|
||||
wp_set_current_user( $user_id );
|
||||
$_POST = array_merge($_POST, $post);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimic the ajax handling of admin-ajax.php
|
||||
* Capture the output via output buffering, and if there is any, store
|
||||
* it in $this->_last_message.
|
||||
* @param string $action
|
||||
*/
|
||||
protected function _handleAjax($action) {
|
||||
|
||||
// Start output buffering
|
||||
ini_set( 'implicit_flush', false );
|
||||
ob_start();
|
||||
|
||||
// Build the request
|
||||
$_POST['action'] = $action;
|
||||
$_GET['action'] = $action;
|
||||
$_REQUEST = array_merge( $_POST, $_GET );
|
||||
|
||||
// Call the hooks
|
||||
do_action( 'admin_init' );
|
||||
do_action( 'wp_ajax_' . $_REQUEST['action'], null );
|
||||
|
||||
// Save the output
|
||||
$buffer = ob_get_clean();
|
||||
if ( !empty( $buffer ) )
|
||||
$this->_last_response = $buffer;
|
||||
}
|
||||
}
|
||||
30
tests/phpunit/includes/testcase-xmlrpc.php
Normal file
30
tests/phpunit/includes/testcase-xmlrpc.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
include_once(ABSPATH . 'wp-admin/includes/admin.php');
|
||||
include_once(ABSPATH . WPINC . '/class-IXR.php');
|
||||
include_once(ABSPATH . WPINC . '/class-wp-xmlrpc-server.php');
|
||||
|
||||
class WP_XMLRPC_UnitTestCase extends WP_UnitTestCase {
|
||||
protected $myxmlrpcserver;
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
|
||||
|
||||
$this->myxmlrpcserver = new wp_xmlrpc_server();
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
remove_filter( 'pre_option_enable_xmlrpc', '__return_true' );
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
protected function make_user_by_role( $role ) {
|
||||
return $this->factory->user->create( array(
|
||||
'user_login' => $role,
|
||||
'user_pass' => $role,
|
||||
'role' => $role
|
||||
));
|
||||
}
|
||||
}
|
||||
227
tests/phpunit/includes/testcase.php
Normal file
227
tests/phpunit/includes/testcase.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
require_once dirname( __FILE__ ) . '/factory.php';
|
||||
require_once dirname( __FILE__ ) . '/trac.php';
|
||||
|
||||
class WP_UnitTestCase extends PHPUnit_Framework_TestCase {
|
||||
|
||||
protected static $forced_tickets = array();
|
||||
|
||||
/**
|
||||
* @var WP_UnitTest_Factory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
function setUp() {
|
||||
set_time_limit(0);
|
||||
|
||||
global $wpdb;
|
||||
$wpdb->suppress_errors = false;
|
||||
$wpdb->show_errors = true;
|
||||
$wpdb->db_connect();
|
||||
ini_set('display_errors', 1 );
|
||||
$this->factory = new WP_UnitTest_Factory;
|
||||
$this->clean_up_global_scope();
|
||||
$this->start_transaction();
|
||||
add_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) );
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
global $wpdb;
|
||||
$wpdb->query( 'ROLLBACK' );
|
||||
remove_filter( 'dbdelta_create_queries', array( $this, '_create_temporary_tables' ) );
|
||||
remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
|
||||
remove_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) );
|
||||
}
|
||||
|
||||
function clean_up_global_scope() {
|
||||
$_GET = array();
|
||||
$_POST = array();
|
||||
$this->flush_cache();
|
||||
}
|
||||
|
||||
function flush_cache() {
|
||||
global $wp_object_cache;
|
||||
$wp_object_cache->group_ops = array();
|
||||
$wp_object_cache->stats = array();
|
||||
$wp_object_cache->memcache_debug = array();
|
||||
$wp_object_cache->cache = array();
|
||||
if ( method_exists( $wp_object_cache, '__remoteset' ) ) {
|
||||
$wp_object_cache->__remoteset();
|
||||
}
|
||||
wp_cache_flush();
|
||||
wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'site-transient', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache' ) );
|
||||
wp_cache_add_non_persistent_groups( array( 'comment', 'counts', 'plugins' ) );
|
||||
}
|
||||
|
||||
function start_transaction() {
|
||||
global $wpdb;
|
||||
$wpdb->query( 'SET autocommit = 0;' );
|
||||
$wpdb->query( 'START TRANSACTION;' );
|
||||
add_filter( 'dbdelta_create_queries', array( $this, '_create_temporary_tables' ) );
|
||||
add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
|
||||
}
|
||||
|
||||
function _create_temporary_tables( $queries ) {
|
||||
return str_replace( 'CREATE TABLE', 'CREATE TEMPORARY TABLE', $queries );
|
||||
}
|
||||
|
||||
function _drop_temporary_tables( $query ) {
|
||||
if ( 'DROP TABLE' === substr( $query, 0, 10 ) )
|
||||
return 'DROP TEMPORARY TABLE ' . substr( $query, 10 );
|
||||
return $query;
|
||||
}
|
||||
|
||||
function get_wp_die_handler( $handler ) {
|
||||
return array( $this, 'wp_die_handler' );
|
||||
}
|
||||
|
||||
function wp_die_handler( $message ) {
|
||||
throw new WPDieException( $message );
|
||||
}
|
||||
|
||||
function assertWPError( $actual, $message = '' ) {
|
||||
$this->assertInstanceOf( 'WP_Error', $actual, $message );
|
||||
}
|
||||
|
||||
function assertEqualFields( $object, $fields ) {
|
||||
foreach( $fields as $field_name => $field_value ) {
|
||||
if ( $object->$field_name != $field_value ) {
|
||||
$this->fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertDiscardWhitespace( $expected, $actual ) {
|
||||
$this->assertEquals( preg_replace( '/\s*/', '', $expected ), preg_replace( '/\s*/', '', $actual ) );
|
||||
}
|
||||
|
||||
function assertEqualSets( $expected, $actual ) {
|
||||
$this->assertEquals( array(), array_diff( $expected, $actual ) );
|
||||
$this->assertEquals( array(), array_diff( $actual, $expected ) );
|
||||
}
|
||||
|
||||
function go_to( $url ) {
|
||||
// note: the WP and WP_Query classes like to silently fetch parameters
|
||||
// from all over the place (globals, GET, etc), which makes it tricky
|
||||
// to run them more than once without very carefully clearing everything
|
||||
$_GET = $_POST = array();
|
||||
foreach (array('query_string', 'id', 'postdata', 'authordata', 'day', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages', 'pagenow') as $v) {
|
||||
if ( isset( $GLOBALS[$v] ) ) unset( $GLOBALS[$v] );
|
||||
}
|
||||
$parts = parse_url($url);
|
||||
if (isset($parts['scheme'])) {
|
||||
$req = $parts['path'];
|
||||
if (isset($parts['query'])) {
|
||||
$req .= '?' . $parts['query'];
|
||||
// parse the url query vars into $_GET
|
||||
parse_str($parts['query'], $_GET);
|
||||
}
|
||||
} else {
|
||||
$req = $url;
|
||||
}
|
||||
if ( ! isset( $parts['query'] ) ) {
|
||||
$parts['query'] = '';
|
||||
}
|
||||
|
||||
$_SERVER['REQUEST_URI'] = $req;
|
||||
unset($_SERVER['PATH_INFO']);
|
||||
|
||||
$this->flush_cache();
|
||||
unset($GLOBALS['wp_query'], $GLOBALS['wp_the_query']);
|
||||
$GLOBALS['wp_the_query'] = new WP_Query();
|
||||
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
|
||||
$GLOBALS['wp'] = new WP();
|
||||
|
||||
// clean out globals to stop them polluting wp and wp_query
|
||||
foreach ($GLOBALS['wp']->public_query_vars as $v) {
|
||||
unset($GLOBALS[$v]);
|
||||
}
|
||||
foreach ($GLOBALS['wp']->private_query_vars as $v) {
|
||||
unset($GLOBALS[$v]);
|
||||
}
|
||||
|
||||
$GLOBALS['wp']->main($parts['query']);
|
||||
}
|
||||
|
||||
protected function checkRequirements() {
|
||||
parent::checkRequirements();
|
||||
if ( WP_TESTS_FORCE_KNOWN_BUGS )
|
||||
return;
|
||||
$tickets = PHPUnit_Util_Test::getTickets( get_class( $this ), $this->getName( false ) );
|
||||
foreach ( $tickets as $ticket ) {
|
||||
if ( is_numeric( $ticket ) ) {
|
||||
$this->knownWPBug( $ticket );
|
||||
} elseif ( 'UT' == substr( $ticket, 0, 2 ) ) {
|
||||
$ticket = substr( $ticket, 2 );
|
||||
if ( $ticket && is_numeric( $ticket ) )
|
||||
$this->knownUTBug( $ticket );
|
||||
} elseif ( 'Plugin' == substr( $ticket, 0, 6 ) ) {
|
||||
$ticket = substr( $ticket, 6 );
|
||||
if ( $ticket && is_numeric( $ticket ) )
|
||||
$this->knownPluginBug( $ticket );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the current test if there is an open WordPress ticket with id $ticket_id
|
||||
*/
|
||||
function knownWPBug( $ticket_id ) {
|
||||
if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( $ticket_id, self::$forced_tickets ) )
|
||||
return;
|
||||
if ( ! TracTickets::isTracTicketClosed( 'http://core.trac.wordpress.org', $ticket_id ) )
|
||||
$this->markTestSkipped( sprintf( 'WordPress Ticket #%d is not fixed', $ticket_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the current test if there is an open unit tests ticket with id $ticket_id
|
||||
*/
|
||||
function knownUTBug( $ticket_id ) {
|
||||
if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( 'UT' . $ticket_id, self::$forced_tickets ) )
|
||||
return;
|
||||
if ( ! TracTickets::isTracTicketClosed( 'http://unit-tests.trac.wordpress.org', $ticket_id ) )
|
||||
$this->markTestSkipped( sprintf( 'Unit Tests Ticket #%d is not fixed', $ticket_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the current test if there is an open plugin ticket with id $ticket_id
|
||||
*/
|
||||
function knownPluginBug( $ticket_id ) {
|
||||
if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( 'Plugin' . $ticket_id, self::$forced_tickets ) )
|
||||
return;
|
||||
if ( ! TracTickets::isTracTicketClosed( 'http://plugins.trac.wordpress.org', $ticket_id ) )
|
||||
$this->markTestSkipped( sprintf( 'WordPress Plugin Ticket #%d is not fixed', $ticket_id ) );
|
||||
}
|
||||
|
||||
public static function forceTicket( $ticket ) {
|
||||
self::$forced_tickets[] = $ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define constants after including files.
|
||||
*/
|
||||
function prepareTemplate( Text_Template $template ) {
|
||||
$template->setVar( array( 'constants' => '' ) );
|
||||
$template->setVar( array( 'wp_constants' => PHPUnit_Util_GlobalState::getConstantsAsString() ) );
|
||||
parent::prepareTemplate( $template );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of a temporary file
|
||||
*/
|
||||
function temp_filename() {
|
||||
$tmp_dir = '';
|
||||
$dirs = array( 'TMP', 'TMPDIR', 'TEMP' );
|
||||
foreach( $dirs as $dir )
|
||||
if ( isset( $_ENV[$dir] ) && !empty( $_ENV[$dir] ) ) {
|
||||
$tmp_dir = $dir;
|
||||
break;
|
||||
}
|
||||
if ( empty( $tmp_dir ) ) {
|
||||
$tmp_dir = '/tmp';
|
||||
}
|
||||
$tmp_dir = realpath( $dir );
|
||||
return tempnam( $tmp_dir, 'wpunit' );
|
||||
}
|
||||
}
|
||||
54
tests/phpunit/includes/trac.php
Normal file
54
tests/phpunit/includes/trac.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
class TracTickets {
|
||||
/**
|
||||
* When open tickets for a Trac install is requested, the results are stored here.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $trac_ticket_cache = array();
|
||||
|
||||
/**
|
||||
* Checks if track ticket #$ticket_id is resolved
|
||||
*
|
||||
* @return bool|null true if the ticket is resolved, false if not resolved, null on error
|
||||
*/
|
||||
public static function isTracTicketClosed( $trac_url, $ticket_id ) {
|
||||
if ( ! isset( self::$trac_ticket_cache[ $trac_url ] ) ) {
|
||||
// In case you're running the tests offline, keep track of open tickets.
|
||||
$file = DIR_TESTDATA . '/.trac-ticket-cache.' . str_replace( array( 'http://', 'https://', '/' ), array( '', '', '-' ), rtrim( $trac_url, '/' ) );
|
||||
$tickets = @file_get_contents( $trac_url . '/query?status=%21closed&format=csv&col=id' );
|
||||
// Check if our HTTP request failed.
|
||||
if ( false === $tickets ) {
|
||||
if ( file_exists( $file ) ) {
|
||||
register_shutdown_function( array( 'TracTickets', 'usingLocalCache' ) );
|
||||
$tickets = file_get_contents( $file );
|
||||
} else {
|
||||
register_shutdown_function( array( 'TracTickets', 'forcingKnownBugs' ) );
|
||||
self::$trac_ticket_cache[ $trac_url ] = array();
|
||||
return true; // Assume the ticket is closed, which means it gets run.
|
||||
}
|
||||
} else {
|
||||
$tickets = substr( $tickets, 2 ); // remove 'id' column header
|
||||
$tickets = trim( $tickets );
|
||||
file_put_contents( $file, $tickets );
|
||||
}
|
||||
$tickets = explode( "\r\n", $tickets );
|
||||
self::$trac_ticket_cache[ $trac_url ] = $tickets;
|
||||
}
|
||||
|
||||
return ! in_array( $ticket_id, self::$trac_ticket_cache[ $trac_url ] );
|
||||
}
|
||||
|
||||
public static function usingLocalCache() {
|
||||
echo PHP_EOL . "\x1b[0m\x1b[30;43m\x1b[2K";
|
||||
echo 'INFO: Trac was inaccessible, so a local ticket status cache was used.' . PHP_EOL;
|
||||
echo "\x1b[0m\x1b[2K";
|
||||
}
|
||||
|
||||
public static function forcingKnownBugs() {
|
||||
echo PHP_EOL . "\x1b[0m\x1b[37;41m\x1b[2K";
|
||||
echo "ERROR: Trac was inaccessible, so known bugs weren't able to be skipped." . PHP_EOL;
|
||||
echo "\x1b[0m\x1b[2K";
|
||||
}
|
||||
}
|
||||
365
tests/phpunit/includes/utils.php
Normal file
365
tests/phpunit/includes/utils.php
Normal file
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
|
||||
// misc help functions and utilities
|
||||
|
||||
function rand_str($len=32) {
|
||||
return substr(md5(uniqid(rand())), 0, $len);
|
||||
}
|
||||
|
||||
// strip leading and trailing whitespace from each line in the string
|
||||
function strip_ws($txt) {
|
||||
$lines = explode("\n", $txt);
|
||||
$result = array();
|
||||
foreach ($lines as $line)
|
||||
if (trim($line))
|
||||
$result[] = trim($line);
|
||||
|
||||
return trim(join("\n", $result));
|
||||
}
|
||||
|
||||
// helper class for testing code that involves actions and filters
|
||||
// typical use:
|
||||
// $ma = new MockAction();
|
||||
// add_action('foo', array(&$ma, 'action'));
|
||||
class MockAction {
|
||||
var $events;
|
||||
var $debug;
|
||||
|
||||
function MockAction($debug=0) {
|
||||
$this->reset();
|
||||
$this->debug = $debug;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
$this->events = array();
|
||||
}
|
||||
|
||||
function current_filter() {
|
||||
if (is_callable('current_filter'))
|
||||
return current_filter();
|
||||
global $wp_actions;
|
||||
return end($wp_actions);
|
||||
}
|
||||
|
||||
function action($arg) {
|
||||
if ($this->debug) dmp(__FUNCTION__, $this->current_filter());
|
||||
$args = func_get_args();
|
||||
$this->events[] = array('action' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args);
|
||||
return $arg;
|
||||
}
|
||||
|
||||
function action2($arg) {
|
||||
if ($this->debug) dmp(__FUNCTION__, $this->current_filter());
|
||||
|
||||
$args = func_get_args();
|
||||
$this->events[] = array('action' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args);
|
||||
return $arg;
|
||||
}
|
||||
|
||||
function filter($arg) {
|
||||
if ($this->debug) dmp(__FUNCTION__, $this->current_filter());
|
||||
|
||||
$args = func_get_args();
|
||||
$this->events[] = array('filter' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args);
|
||||
return $arg;
|
||||
}
|
||||
|
||||
function filter2($arg) {
|
||||
if ($this->debug) dmp(__FUNCTION__, $this->current_filter());
|
||||
|
||||
$args = func_get_args();
|
||||
$this->events[] = array('filter' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args);
|
||||
return $arg;
|
||||
}
|
||||
|
||||
function filter_append($arg) {
|
||||
if ($this->debug) dmp(__FUNCTION__, $this->current_filter());
|
||||
|
||||
$args = func_get_args();
|
||||
$this->events[] = array('filter' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args);
|
||||
return $arg . '_append';
|
||||
}
|
||||
|
||||
function filterall($tag, $arg=NULL) {
|
||||
// this one doesn't return the result, so it's safe to use with the new 'all' filter
|
||||
if ($this->debug) dmp(__FUNCTION__, $this->current_filter());
|
||||
|
||||
$args = func_get_args();
|
||||
$this->events[] = array('filter' => __FUNCTION__, 'tag'=>$tag, 'args'=>array_slice($args, 1));
|
||||
}
|
||||
|
||||
// return a list of all the actions, tags and args
|
||||
function get_events() {
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
// return a count of the number of times the action was called since the last reset
|
||||
function get_call_count($tag='') {
|
||||
if ($tag) {
|
||||
$count = 0;
|
||||
foreach ($this->events as $e)
|
||||
if ($e['action'] == $tag)
|
||||
++$count;
|
||||
return $count;
|
||||
}
|
||||
return count($this->events);
|
||||
}
|
||||
|
||||
// return an array of the tags that triggered calls to this action
|
||||
function get_tags() {
|
||||
$out = array();
|
||||
foreach ($this->events as $e) {
|
||||
$out[] = $e['tag'];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
// return an array of args passed in calls to this action
|
||||
function get_args() {
|
||||
$out = array();
|
||||
foreach ($this->events as $e)
|
||||
$out[] = $e['args'];
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
// convert valid xml to an array tree structure
|
||||
// kinda lame but it works with a default php 4 install
|
||||
class testXMLParser {
|
||||
var $xml;
|
||||
var $data = array();
|
||||
|
||||
function testXMLParser($in) {
|
||||
$this->xml = xml_parser_create();
|
||||
xml_set_object($this->xml, $this);
|
||||
xml_parser_set_option($this->xml,XML_OPTION_CASE_FOLDING, 0);
|
||||
xml_set_element_handler($this->xml, array(&$this, 'startHandler'), array(&$this, 'endHandler'));
|
||||
xml_set_character_data_handler($this->xml, array(&$this, 'dataHandler'));
|
||||
$this->parse($in);
|
||||
}
|
||||
|
||||
function parse($in) {
|
||||
$parse = xml_parse($this->xml, $in, sizeof($in));
|
||||
if (!$parse) {
|
||||
trigger_error(sprintf("XML error: %s at line %d",
|
||||
xml_error_string(xml_get_error_code($this->xml)),
|
||||
xml_get_current_line_number($this->xml)), E_USER_ERROR);
|
||||
xml_parser_free($this->xml);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function startHandler($parser, $name, $attributes) {
|
||||
$data['name'] = $name;
|
||||
if ($attributes) { $data['attributes'] = $attributes; }
|
||||
$this->data[] = $data;
|
||||
}
|
||||
|
||||
function dataHandler($parser, $data) {
|
||||
$index = count($this->data) - 1;
|
||||
@$this->data[$index]['content'] .= $data;
|
||||
}
|
||||
|
||||
function endHandler($parser, $name) {
|
||||
if (count($this->data) > 1) {
|
||||
$data = array_pop($this->data);
|
||||
$index = count($this->data) - 1;
|
||||
$this->data[$index]['child'][] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function xml_to_array($in) {
|
||||
$p = new testXMLParser($in);
|
||||
return $p->data;
|
||||
}
|
||||
|
||||
function xml_find($tree /*, $el1, $el2, $el3, .. */) {
|
||||
$a = func_get_args();
|
||||
$a = array_slice($a, 1);
|
||||
$n = count($a);
|
||||
$out = array();
|
||||
|
||||
if ($n < 1)
|
||||
return $out;
|
||||
|
||||
for ($i=0; $i<count($tree); $i++) {
|
||||
# echo "checking '{$tree[$i][name]}' == '{$a[0]}'\n";
|
||||
# var_dump($tree[$i]['name'], $a[0]);
|
||||
if ($tree[$i]['name'] == $a[0]) {
|
||||
# echo "n == {$n}\n";
|
||||
if ($n == 1)
|
||||
$out[] = $tree[$i];
|
||||
else {
|
||||
$subtree =& $tree[$i]['child'];
|
||||
$call_args = array($subtree);
|
||||
$call_args = array_merge($call_args, array_slice($a, 1));
|
||||
$out = array_merge($out, call_user_func_array('xml_find', $call_args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
function xml_join_atts($atts) {
|
||||
$a = array();
|
||||
foreach ($atts as $k=>$v)
|
||||
$a[] = $k.'="'.$v.'"';
|
||||
return join(' ', $a);
|
||||
}
|
||||
|
||||
function xml_array_dumbdown(&$data) {
|
||||
$out = array();
|
||||
|
||||
foreach (array_keys($data) as $i) {
|
||||
$name = $data[$i]['name'];
|
||||
if (!empty($data[$i]['attributes']))
|
||||
$name .= ' '.xml_join_atts($data[$i]['attributes']);
|
||||
|
||||
if (!empty($data[$i]['child'])) {
|
||||
$out[$name][] = xml_array_dumbdown($data[$i]['child']);
|
||||
}
|
||||
else
|
||||
$out[$name] = $data[$i]['content'];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
function dmp() {
|
||||
$args = func_get_args();
|
||||
|
||||
foreach ($args as $thing)
|
||||
echo (is_scalar($thing) ? strval($thing) : var_export($thing, true)), "\n";
|
||||
}
|
||||
|
||||
function dmp_filter($a) {
|
||||
dmp($a);
|
||||
return $a;
|
||||
}
|
||||
|
||||
function get_echo($callable, $args = array()) {
|
||||
ob_start();
|
||||
call_user_func_array($callable, $args);
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
// recursively generate some quick assertEquals tests based on an array
|
||||
function gen_tests_array($name, $array) {
|
||||
$out = array();
|
||||
foreach ($array as $k=>$v) {
|
||||
if (is_numeric($k))
|
||||
$index = strval($k);
|
||||
else
|
||||
$index = "'".addcslashes($k, "\n\r\t'\\")."'";
|
||||
|
||||
if (is_string($v)) {
|
||||
$out[] = '$this->assertEquals( \'' . addcslashes($v, "\n\r\t'\\") . '\', $'.$name.'['.$index.'] );';
|
||||
}
|
||||
elseif (is_numeric($v)) {
|
||||
$out[] = '$this->assertEquals( ' . $v . ', $'.$name.'['.$index.'] );';
|
||||
}
|
||||
elseif (is_array($v)) {
|
||||
$out[] = gen_tests_array("{$name}[{$index}]", $v);
|
||||
}
|
||||
}
|
||||
return join("\n", $out)."\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to create objects by yourself
|
||||
*/
|
||||
class MockClass {};
|
||||
|
||||
/**
|
||||
* Drops all tables from the WordPress database
|
||||
*/
|
||||
function drop_tables() {
|
||||
global $wpdb;
|
||||
$tables = $wpdb->get_col('SHOW TABLES;');
|
||||
foreach ($tables as $table)
|
||||
$wpdb->query("DROP TABLE IF EXISTS {$table}");
|
||||
}
|
||||
|
||||
function print_backtrace() {
|
||||
$bt = debug_backtrace();
|
||||
echo "Backtrace:\n";
|
||||
$i = 0;
|
||||
foreach ($bt as $stack) {
|
||||
echo ++$i, ": ";
|
||||
if ( isset($stack['class']) )
|
||||
echo $stack['class'].'::';
|
||||
if ( isset($stack['function']) )
|
||||
echo $stack['function'].'() ';
|
||||
echo "line {$stack[line]} in {$stack[file]}\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// mask out any input fields matching the given name
|
||||
function mask_input_value($in, $name='_wpnonce') {
|
||||
return preg_replace('@<input([^>]*) name="'.preg_quote($name).'"([^>]*) value="[^>]*" />@', '<input$1 name="'.preg_quote($name).'"$2 value="***" />', $in);
|
||||
}
|
||||
|
||||
$GLOBALS['_wp_die_disabled'] = false;
|
||||
function _wp_die_handler( $message, $title = '', $args = array() ) {
|
||||
if ( !$GLOBALS['_wp_die_disabled'] ) {
|
||||
_default_wp_die_handler( $message, $title, $args );
|
||||
} else {
|
||||
//Ignore at our peril
|
||||
}
|
||||
}
|
||||
|
||||
function _disable_wp_die() {
|
||||
$GLOBALS['_wp_die_disabled'] = true;
|
||||
}
|
||||
|
||||
function _enable_wp_die() {
|
||||
$GLOBALS['_wp_die_disabled'] = false;
|
||||
}
|
||||
|
||||
function _wp_die_handler_filter() {
|
||||
return '_wp_die_handler';
|
||||
}
|
||||
|
||||
if ( !function_exists( 'str_getcsv' ) ) {
|
||||
function str_getcsv( $input, $delimiter = ',', $enclosure = '"', $escape = "\\" ) {
|
||||
$fp = fopen( 'php://temp/', 'r+' );
|
||||
fputs( $fp, $input );
|
||||
rewind( $fp );
|
||||
$data = fgetcsv( $fp, strlen( $input ), $delimiter, $enclosure );
|
||||
fclose( $fp );
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
function _rmdir( $path ) {
|
||||
if ( in_array(basename( $path ), array( '.', '..' ) ) ) {
|
||||
return;
|
||||
} elseif ( is_file( $path ) ) {
|
||||
unlink( $path );
|
||||
} elseif ( is_dir( $path ) ) {
|
||||
foreach ( scandir( $path ) as $file )
|
||||
_rmdir( $path . '/' . $file );
|
||||
rmdir( $path );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the post type and its taxonomy associations.
|
||||
*/
|
||||
function _unregister_post_type( $cpt_name ) {
|
||||
unset( $GLOBALS['wp_post_types'][ $cpt_name ] );
|
||||
unset( $GLOBALS['_wp_post_type_features'][ $cpt_name ] );
|
||||
|
||||
foreach ( $GLOBALS['wp_taxonomies'] as $taxonomy ) {
|
||||
if ( false !== $key = array_search( $cpt_name, $taxonomy->object_type ) ) {
|
||||
unset( $taxonomy->object_type[$key] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _unregister_taxonomy( $taxonomy_name ) {
|
||||
unset( $GLOBALS['wp_taxonomies'][$taxonomy_name] );
|
||||
}
|
||||
216
tests/phpunit/includes/wp-profiler.php
Normal file
216
tests/phpunit/includes/wp-profiler.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
A simple manually-instrumented profiler for WordPress.
|
||||
|
||||
This records basic execution time, and a summary of the actions and SQL queries run within each block.
|
||||
|
||||
start() and stop() must be called in pairs, for example:
|
||||
|
||||
function something_to_profile() {
|
||||
wppf_start(__FUNCTION__);
|
||||
do_stuff();
|
||||
wppf_stop();
|
||||
}
|
||||
|
||||
Multiple profile blocks are permitted, and they may be nested.
|
||||
|
||||
*/
|
||||
|
||||
class WPProfiler {
|
||||
var $stack;
|
||||
var $profile;
|
||||
|
||||
// constructor
|
||||
function WPProfiler() {
|
||||
$this->stack = array();
|
||||
$this->profile = array();
|
||||
}
|
||||
|
||||
function start($name) {
|
||||
$time = $this->microtime();
|
||||
|
||||
if (!$this->stack) {
|
||||
// log all actions and filters
|
||||
add_filter('all', array(&$this, 'log_filter'));
|
||||
}
|
||||
|
||||
// reset the wpdb queries log, storing it on the profile stack if necessary
|
||||
global $wpdb;
|
||||
if ($this->stack) {
|
||||
$this->stack[count($this->stack)-1]['queries'] = $wpdb->queries;
|
||||
}
|
||||
$wpdb->queries = array();
|
||||
|
||||
global $wp_object_cache;
|
||||
|
||||
$this->stack[] = array(
|
||||
'start' => $time,
|
||||
'name' => $name,
|
||||
'cache_cold_hits' => $wp_object_cache->cold_cache_hits,
|
||||
'cache_warm_hits' => $wp_object_cache->warm_cache_hits,
|
||||
'cache_misses' => $wp_object_cache->cache_misses,
|
||||
'cache_dirty_objects' => $this->_dirty_objects_count($wp_object_cache->dirty_objects),
|
||||
'actions' => array(),
|
||||
'filters' => array(),
|
||||
'queries' => array(),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function stop() {
|
||||
$item = array_pop($this->stack);
|
||||
$time = $this->microtime($item['start']);
|
||||
$name = $item['name'];
|
||||
|
||||
global $wpdb;
|
||||
$item['queries'] = $wpdb->queries;
|
||||
global $wp_object_cache;
|
||||
|
||||
$cache_dirty_count = $this->_dirty_objects_count($wp_object_cache->dirty_objects);
|
||||
$cache_dirty_delta = $this->array_sub($cache_dirty_count, $item['cache_dirty_objects']);
|
||||
|
||||
if (isset($this->profile[$name])) {
|
||||
$this->profile[$name]['time'] += $time;
|
||||
$this->profile[$name]['calls'] ++;
|
||||
$this->profile[$name]['cache_cold_hits'] += ($wp_object_cache->cold_cache_hits - $item['cache_cold_hits']);
|
||||
$this->profile[$name]['cache_warm_hits'] += ($wp_object_cache->warm_cache_hits - $item['cache_warm_hits']);
|
||||
$this->profile[$name]['cache_misses'] += ($wp_object_cache->cache_misses - $item['cache_misses']);
|
||||
$this->profile[$name]['cache_dirty_objects'] = array_add( $this->profile[$name]['cache_dirty_objects'], $cache_dirty_delta) ;
|
||||
$this->profile[$name]['actions'] = array_add( $this->profile[$name]['actions'], $item['actions'] );
|
||||
$this->profile[$name]['filters'] = array_add( $this->profile[$name]['filters'], $item['filters'] );
|
||||
$this->profile[$name]['queries'] = array_add( $this->profile[$name]['queries'], $item['queries'] );
|
||||
#$this->_query_summary($item['queries'], $this->profile[$name]['queries']);
|
||||
|
||||
}
|
||||
else {
|
||||
$queries = array();
|
||||
$this->_query_summary($item['queries'], $queries);
|
||||
$this->profile[$name] = array(
|
||||
'time' => $time,
|
||||
'calls' => 1,
|
||||
'cache_cold_hits' => ($wp_object_cache->cold_cache_hits - $item['cache_cold_hits']),
|
||||
'cache_warm_hits' => ($wp_object_cache->warm_cache_hits - $item['cache_warm_hits']),
|
||||
'cache_misses' => ($wp_object_cache->cache_misses - $item['cache_misses']),
|
||||
'cache_dirty_objects' => $cache_dirty_delta,
|
||||
'actions' => $item['actions'],
|
||||
'filters' => $item['filters'],
|
||||
# 'queries' => $item['queries'],
|
||||
'queries' => $queries,
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->stack) {
|
||||
remove_filter('all', array(&$this, 'log_filter'));
|
||||
}
|
||||
}
|
||||
|
||||
function microtime($since = 0.0) {
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
return (float)$sec + (float)$usec - $since;
|
||||
}
|
||||
|
||||
function log_filter($tag) {
|
||||
if ($this->stack) {
|
||||
global $wp_actions;
|
||||
if ($tag == end($wp_actions))
|
||||
@$this->stack[count($this->stack)-1]['actions'][$tag] ++;
|
||||
else
|
||||
@$this->stack[count($this->stack)-1]['filters'][$tag] ++;
|
||||
}
|
||||
return $arg;
|
||||
}
|
||||
|
||||
function log_action($tag) {
|
||||
if ($this->stack)
|
||||
@$this->stack[count($this->stack)-1]['actions'][$tag] ++;
|
||||
}
|
||||
|
||||
function _current_action() {
|
||||
global $wp_actions;
|
||||
return $wp_actions[count($wp_actions)-1];
|
||||
}
|
||||
|
||||
function results() {
|
||||
return $this->profile;
|
||||
}
|
||||
|
||||
function _query_summary($queries, &$out) {
|
||||
foreach ($queries as $q) {
|
||||
$sql = $q[0];
|
||||
$sql = preg_replace('/(WHERE \w+ =) \d+/', '$1 x', $sql);
|
||||
$sql = preg_replace('/(WHERE \w+ =) \'\[-\w]+\'/', '$1 \'xxx\'', $sql);
|
||||
|
||||
@$out[$sql] ++;
|
||||
}
|
||||
asort($out);
|
||||
return;
|
||||
}
|
||||
|
||||
function _query_count($queries) {
|
||||
// this requires the savequeries patch at http://trac.wordpress.org/ticket/5218
|
||||
$out = array();
|
||||
foreach ($queries as $q) {
|
||||
if (empty($q[2]))
|
||||
@$out['unknown'] ++;
|
||||
else
|
||||
@$out[$q[2]] ++;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
function _dirty_objects_count($dirty_objects) {
|
||||
$out = array();
|
||||
foreach (array_keys($dirty_objects) as $group)
|
||||
$out[$group] = count($dirty_objects[$group]);
|
||||
return $out;
|
||||
}
|
||||
|
||||
function array_add($a, $b) {
|
||||
$out = $a;
|
||||
foreach (array_keys($b) as $key)
|
||||
if (array_key_exists($key, $out))
|
||||
$out[$key] += $b[$key];
|
||||
else
|
||||
$out[$key] = $b[$key];
|
||||
return $out;
|
||||
}
|
||||
|
||||
function array_sub($a, $b) {
|
||||
$out = $a;
|
||||
foreach (array_keys($b) as $key)
|
||||
if (array_key_exists($key, $b))
|
||||
$out[$key] -= $b[$key];
|
||||
return $out;
|
||||
}
|
||||
|
||||
function print_summary() {
|
||||
$results = $this->results();
|
||||
|
||||
printf("\nname calls time action filter warm cold misses dirty\n");
|
||||
foreach ($results as $name=>$stats) {
|
||||
printf("%24.24s %6d %6.4f %6d %6d %6d %6d %6d %6d\n", $name, $stats['calls'], $stats['time'], array_sum($stats['actions']), array_sum($stats['filters']), $stats['cache_warm_hits'], $stats['cache_cold_hits'], $stats['cache_misses'], array_sum($stats['cache_dirty_objects']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
global $wppf;
|
||||
$wppf = new WPProfiler();
|
||||
|
||||
function wppf_start($name) {
|
||||
$GLOBALS['wppf']->start($name);
|
||||
}
|
||||
|
||||
function wppf_stop() {
|
||||
$GLOBALS['wppf']->stop();
|
||||
}
|
||||
|
||||
function wppf_results() {
|
||||
return $GLOBALS['wppf']->results();
|
||||
}
|
||||
|
||||
function wppf_print_summary() {
|
||||
$GLOBALS['wppf']->print_summary();
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user