1 | <?php |
---|
2 | |
---|
3 | require_once dirname( __FILE__ ) . '/factory.php'; |
---|
4 | require_once dirname( __FILE__ ) . '/trac.php'; |
---|
5 | |
---|
6 | /** |
---|
7 | * Defines a basic fixture to run multiple tests. |
---|
8 | * |
---|
9 | * Resets the state of the WordPress installation before and after every test. |
---|
10 | * |
---|
11 | * Includes utility functions and assertions useful for testing WordPress. |
---|
12 | * |
---|
13 | * All WordPress unit tests should inherit from this class. |
---|
14 | */ |
---|
15 | class WP_UnitTestCase extends PHPUnit_Framework_TestCase { |
---|
16 | |
---|
17 | protected static $forced_tickets = array(); |
---|
18 | protected $expected_deprecated = array(); |
---|
19 | protected $caught_deprecated = array(); |
---|
20 | protected $expected_doing_it_wrong = array(); |
---|
21 | protected $caught_doing_it_wrong = array(); |
---|
22 | |
---|
23 | protected static $hooks_saved = array(); |
---|
24 | protected static $ignore_files; |
---|
25 | |
---|
26 | function __isset( $name ) { |
---|
27 | return 'factory' === $name; |
---|
28 | } |
---|
29 | |
---|
30 | function __get( $name ) { |
---|
31 | if ( 'factory' === $name ) { |
---|
32 | return self::factory(); |
---|
33 | } |
---|
34 | } |
---|
35 | |
---|
36 | protected static function factory() { |
---|
37 | static $factory = null; |
---|
38 | if ( ! $factory ) { |
---|
39 | $factory = new WP_UnitTest_Factory(); |
---|
40 | } |
---|
41 | return $factory; |
---|
42 | } |
---|
43 | |
---|
44 | public static function get_called_class() { |
---|
45 | if ( function_exists( 'get_called_class' ) ) { |
---|
46 | return get_called_class(); |
---|
47 | } |
---|
48 | |
---|
49 | // PHP 5.2 only |
---|
50 | $backtrace = debug_backtrace(); |
---|
51 | // [0] WP_UnitTestCase::get_called_class() |
---|
52 | // [1] WP_UnitTestCase::setUpBeforeClass() |
---|
53 | if ( 'call_user_func' === $backtrace[2]['function'] ) { |
---|
54 | return $backtrace[2]['args'][0][0]; |
---|
55 | } |
---|
56 | return $backtrace[2]['class']; |
---|
57 | } |
---|
58 | |
---|
59 | public static function setUpBeforeClass() { |
---|
60 | parent::setUpBeforeClass(); |
---|
61 | |
---|
62 | $c = self::get_called_class(); |
---|
63 | if ( ! method_exists( $c, 'wpSetUpBeforeClass' ) ) { |
---|
64 | return; |
---|
65 | } |
---|
66 | |
---|
67 | call_user_func( array( $c, 'wpSetUpBeforeClass' ), self::factory() ); |
---|
68 | |
---|
69 | self::commit_transaction(); |
---|
70 | } |
---|
71 | |
---|
72 | public static function tearDownAfterClass() { |
---|
73 | parent::tearDownAfterClass(); |
---|
74 | |
---|
75 | $c = self::get_called_class(); |
---|
76 | if ( ! method_exists( $c, 'wpTearDownAfterClass' ) ) { |
---|
77 | return; |
---|
78 | } |
---|
79 | |
---|
80 | call_user_func( array( $c, 'wpTearDownAfterClass' ) ); |
---|
81 | |
---|
82 | self::commit_transaction(); |
---|
83 | } |
---|
84 | |
---|
85 | function setUp() { |
---|
86 | set_time_limit(0); |
---|
87 | |
---|
88 | if ( ! self::$ignore_files ) { |
---|
89 | self::$ignore_files = $this->scan_user_uploads(); |
---|
90 | } |
---|
91 | |
---|
92 | if ( ! self::$hooks_saved ) { |
---|
93 | $this->_backup_hooks(); |
---|
94 | } |
---|
95 | |
---|
96 | global $wpdb, $wp_rewrite; |
---|
97 | $wpdb->suppress_errors = false; |
---|
98 | $wpdb->show_errors = true; |
---|
99 | $wpdb->db_connect(); |
---|
100 | ini_set('display_errors', 1 ); |
---|
101 | $this->clean_up_global_scope(); |
---|
102 | |
---|
103 | /* |
---|
104 | * When running core tests, ensure that post types and taxonomies |
---|
105 | * are reset for each test. We skip this step for non-core tests, |
---|
106 | * given the large number of plugins that register post types and |
---|
107 | * taxonomies at 'init'. |
---|
108 | */ |
---|
109 | if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) { |
---|
110 | $this->reset_post_types(); |
---|
111 | $this->reset_taxonomies(); |
---|
112 | $this->reset_post_statuses(); |
---|
113 | $this->reset__SERVER(); |
---|
114 | |
---|
115 | if ( $wp_rewrite->permalink_structure ) { |
---|
116 | $this->set_permalink_structure( '' ); |
---|
117 | } |
---|
118 | } |
---|
119 | |
---|
120 | $this->start_transaction(); |
---|
121 | $this->expectDeprecated(); |
---|
122 | add_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) ); |
---|
123 | } |
---|
124 | |
---|
125 | /** |
---|
126 | * Detect post-test failure conditions. |
---|
127 | * |
---|
128 | * We use this method to detect expectedDeprecated and expectedIncorrectUsage annotations. |
---|
129 | * |
---|
130 | * @since 4.2.0 |
---|
131 | */ |
---|
132 | protected function assertPostConditions() { |
---|
133 | $this->expectedDeprecated(); |
---|
134 | } |
---|
135 | |
---|
136 | /** |
---|
137 | * After a test method runs, reset any state in WordPress the test method might have changed. |
---|
138 | */ |
---|
139 | function tearDown() { |
---|
140 | global $wpdb, $wp_query, $wp, $post; |
---|
141 | $wpdb->query( 'ROLLBACK' ); |
---|
142 | if ( is_multisite() ) { |
---|
143 | while ( ms_is_switched() ) { |
---|
144 | restore_current_blog(); |
---|
145 | } |
---|
146 | } |
---|
147 | $wp_query = new WP_Query(); |
---|
148 | $wp = new WP(); |
---|
149 | $post = null; |
---|
150 | remove_theme_support( 'html5' ); |
---|
151 | remove_filter( 'query', array( $this, '_create_temporary_tables' ) ); |
---|
152 | remove_filter( 'query', array( $this, '_drop_temporary_tables' ) ); |
---|
153 | remove_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) ); |
---|
154 | $this->_restore_hooks(); |
---|
155 | wp_set_current_user( 0 ); |
---|
156 | } |
---|
157 | |
---|
158 | function clean_up_global_scope() { |
---|
159 | $_GET = array(); |
---|
160 | $_POST = array(); |
---|
161 | $this->flush_cache(); |
---|
162 | } |
---|
163 | |
---|
164 | /** |
---|
165 | * Unregister existing post types and register defaults. |
---|
166 | * |
---|
167 | * Run before each test in order to clean up the global scope, in case |
---|
168 | * a test forgets to unregister a post type on its own, or fails before |
---|
169 | * it has a chance to do so. |
---|
170 | */ |
---|
171 | protected function reset_post_types() { |
---|
172 | foreach ( get_post_types() as $pt ) { |
---|
173 | _unregister_post_type( $pt ); |
---|
174 | } |
---|
175 | create_initial_post_types(); |
---|
176 | } |
---|
177 | |
---|
178 | /** |
---|
179 | * Unregister existing taxonomies and register defaults. |
---|
180 | * |
---|
181 | * Run before each test in order to clean up the global scope, in case |
---|
182 | * a test forgets to unregister a taxonomy on its own, or fails before |
---|
183 | * it has a chance to do so. |
---|
184 | */ |
---|
185 | protected function reset_taxonomies() { |
---|
186 | foreach ( get_taxonomies() as $tax ) { |
---|
187 | _unregister_taxonomy( $tax ); |
---|
188 | } |
---|
189 | create_initial_taxonomies(); |
---|
190 | } |
---|
191 | |
---|
192 | /** |
---|
193 | * Unregister non-built-in post statuses. |
---|
194 | */ |
---|
195 | protected function reset_post_statuses() { |
---|
196 | foreach ( get_post_stati( array( '_builtin' => false ) ) as $post_status ) { |
---|
197 | _unregister_post_status( $post_status ); |
---|
198 | } |
---|
199 | } |
---|
200 | |
---|
201 | /** |
---|
202 | * Reset `$_SERVER` variables |
---|
203 | */ |
---|
204 | protected function reset__SERVER() { |
---|
205 | tests_reset__SERVER(); |
---|
206 | } |
---|
207 | |
---|
208 | /** |
---|
209 | * Saves the action and filter-related globals so they can be restored later. |
---|
210 | * |
---|
211 | * Stores $merged_filters, $wp_actions, $wp_current_filter, and $wp_filter |
---|
212 | * on a class variable so they can be restored on tearDown() using _restore_hooks(). |
---|
213 | * |
---|
214 | * @global array $merged_filters |
---|
215 | * @global array $wp_actions |
---|
216 | * @global array $wp_current_filter |
---|
217 | * @global array $wp_filter |
---|
218 | * @return void |
---|
219 | */ |
---|
220 | protected function _backup_hooks() { |
---|
221 | $globals = array( 'merged_filters', 'wp_actions', 'wp_current_filter', 'wp_filter' ); |
---|
222 | foreach ( $globals as $key ) { |
---|
223 | self::$hooks_saved[ $key ] = $GLOBALS[ $key ]; |
---|
224 | } |
---|
225 | } |
---|
226 | |
---|
227 | /** |
---|
228 | * Restores the hook-related globals to their state at setUp() |
---|
229 | * so that future tests aren't affected by hooks set during this last test. |
---|
230 | * |
---|
231 | * @global array $merged_filters |
---|
232 | * @global array $wp_actions |
---|
233 | * @global array $wp_current_filter |
---|
234 | * @global array $wp_filter |
---|
235 | * @return void |
---|
236 | */ |
---|
237 | protected function _restore_hooks() { |
---|
238 | $globals = array( 'merged_filters', 'wp_actions', 'wp_current_filter', 'wp_filter' ); |
---|
239 | foreach ( $globals as $key ) { |
---|
240 | if ( isset( self::$hooks_saved[ $key ] ) ) { |
---|
241 | $GLOBALS[ $key ] = self::$hooks_saved[ $key ]; |
---|
242 | } |
---|
243 | } |
---|
244 | } |
---|
245 | |
---|
246 | function flush_cache() { |
---|
247 | global $wp_object_cache; |
---|
248 | $wp_object_cache->group_ops = array(); |
---|
249 | $wp_object_cache->stats = array(); |
---|
250 | $wp_object_cache->memcache_debug = array(); |
---|
251 | $wp_object_cache->cache = array(); |
---|
252 | if ( method_exists( $wp_object_cache, '__remoteset' ) ) { |
---|
253 | $wp_object_cache->__remoteset(); |
---|
254 | } |
---|
255 | wp_cache_flush(); |
---|
256 | 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' ) ); |
---|
257 | wp_cache_add_non_persistent_groups( array( 'comment', 'counts', 'plugins' ) ); |
---|
258 | } |
---|
259 | |
---|
260 | function start_transaction() { |
---|
261 | global $wpdb; |
---|
262 | $wpdb->query( 'SET autocommit = 0;' ); |
---|
263 | $wpdb->query( 'START TRANSACTION;' ); |
---|
264 | add_filter( 'query', array( $this, '_create_temporary_tables' ) ); |
---|
265 | add_filter( 'query', array( $this, '_drop_temporary_tables' ) ); |
---|
266 | } |
---|
267 | |
---|
268 | /** |
---|
269 | * Commit the queries in a transaction. |
---|
270 | * |
---|
271 | * @since 4.1.0 |
---|
272 | */ |
---|
273 | public static function commit_transaction() { |
---|
274 | global $wpdb; |
---|
275 | $wpdb->query( 'COMMIT;' ); |
---|
276 | } |
---|
277 | |
---|
278 | function _create_temporary_tables( $query ) { |
---|
279 | if ( 'CREATE TABLE' === substr( trim( $query ), 0, 12 ) ) |
---|
280 | return substr_replace( trim( $query ), 'CREATE TEMPORARY TABLE', 0, 12 ); |
---|
281 | return $query; |
---|
282 | } |
---|
283 | |
---|
284 | function _drop_temporary_tables( $query ) { |
---|
285 | if ( 'DROP TABLE' === substr( trim( $query ), 0, 10 ) ) |
---|
286 | return substr_replace( trim( $query ), 'DROP TEMPORARY TABLE', 0, 10 ); |
---|
287 | return $query; |
---|
288 | } |
---|
289 | |
---|
290 | function get_wp_die_handler( $handler ) { |
---|
291 | return array( $this, 'wp_die_handler' ); |
---|
292 | } |
---|
293 | |
---|
294 | function wp_die_handler( $message ) { |
---|
295 | if ( ! is_scalar( $message ) ) { |
---|
296 | $message = '0'; |
---|
297 | } |
---|
298 | |
---|
299 | throw new WPDieException( $message ); |
---|
300 | } |
---|
301 | |
---|
302 | function expectDeprecated() { |
---|
303 | $annotations = $this->getAnnotations(); |
---|
304 | foreach ( array( 'class', 'method' ) as $depth ) { |
---|
305 | if ( ! empty( $annotations[ $depth ]['expectedDeprecated'] ) ) |
---|
306 | $this->expected_deprecated = array_merge( $this->expected_deprecated, $annotations[ $depth ]['expectedDeprecated'] ); |
---|
307 | if ( ! empty( $annotations[ $depth ]['expectedIncorrectUsage'] ) ) |
---|
308 | $this->expected_doing_it_wrong = array_merge( $this->expected_doing_it_wrong, $annotations[ $depth ]['expectedIncorrectUsage'] ); |
---|
309 | } |
---|
310 | add_action( 'deprecated_function_run', array( $this, 'deprecated_function_run' ) ); |
---|
311 | add_action( 'deprecated_argument_run', array( $this, 'deprecated_function_run' ) ); |
---|
312 | add_action( 'doing_it_wrong_run', array( $this, 'doing_it_wrong_run' ) ); |
---|
313 | add_action( 'deprecated_function_trigger_error', '__return_false' ); |
---|
314 | add_action( 'deprecated_argument_trigger_error', '__return_false' ); |
---|
315 | add_action( 'doing_it_wrong_trigger_error', '__return_false' ); |
---|
316 | } |
---|
317 | |
---|
318 | function expectedDeprecated() { |
---|
319 | $errors = array(); |
---|
320 | |
---|
321 | $not_caught_deprecated = array_diff( $this->expected_deprecated, $this->caught_deprecated ); |
---|
322 | foreach ( $not_caught_deprecated as $not_caught ) { |
---|
323 | $errors[] = "Failed to assert that $not_caught triggered a deprecated notice"; |
---|
324 | } |
---|
325 | |
---|
326 | $unexpected_deprecated = array_diff( $this->caught_deprecated, $this->expected_deprecated ); |
---|
327 | foreach ( $unexpected_deprecated as $unexpected ) { |
---|
328 | $errors[] = "Unexpected deprecated notice for $unexpected"; |
---|
329 | } |
---|
330 | |
---|
331 | $not_caught_doing_it_wrong = array_diff( $this->expected_doing_it_wrong, $this->caught_doing_it_wrong ); |
---|
332 | foreach ( $not_caught_doing_it_wrong as $not_caught ) { |
---|
333 | $errors[] = "Failed to assert that $not_caught triggered an incorrect usage notice"; |
---|
334 | } |
---|
335 | |
---|
336 | $unexpected_doing_it_wrong = array_diff( $this->caught_doing_it_wrong, $this->expected_doing_it_wrong ); |
---|
337 | foreach ( $unexpected_doing_it_wrong as $unexpected ) { |
---|
338 | $errors[] = "Unexpected incorrect usage notice for $unexpected"; |
---|
339 | } |
---|
340 | |
---|
341 | if ( ! empty( $errors ) ) { |
---|
342 | $this->fail( implode( "\n", $errors ) ); |
---|
343 | } |
---|
344 | } |
---|
345 | |
---|
346 | /** |
---|
347 | * Declare an expected `_deprecated_function()` or `_deprecated_argument()` call from within a test. |
---|
348 | * |
---|
349 | * @since 4.2.0 |
---|
350 | * |
---|
351 | * @param string $deprecated Name of the function, method, class, or argument that is deprecated. Must match |
---|
352 | * first parameter of the `_deprecated_function()` or `_deprecated_argument()` call. |
---|
353 | */ |
---|
354 | public function setExpectedDeprecated( $deprecated ) { |
---|
355 | array_push( $this->expected_deprecated, $deprecated ); |
---|
356 | } |
---|
357 | |
---|
358 | /** |
---|
359 | * Declare an expected `_doing_it_wrong()` call from within a test. |
---|
360 | * |
---|
361 | * @since 4.2.0 |
---|
362 | * |
---|
363 | * @param string $deprecated Name of the function, method, or class that appears in the first argument of the |
---|
364 | * source `_doing_it_wrong()` call. |
---|
365 | */ |
---|
366 | public function setExpectedIncorrectUsage( $doing_it_wrong ) { |
---|
367 | array_push( $this->expected_doing_it_wrong, $doing_it_wrong ); |
---|
368 | } |
---|
369 | |
---|
370 | function deprecated_function_run( $function ) { |
---|
371 | if ( ! in_array( $function, $this->caught_deprecated ) ) |
---|
372 | $this->caught_deprecated[] = $function; |
---|
373 | } |
---|
374 | |
---|
375 | function doing_it_wrong_run( $function ) { |
---|
376 | if ( ! in_array( $function, $this->caught_doing_it_wrong ) ) |
---|
377 | $this->caught_doing_it_wrong[] = $function; |
---|
378 | } |
---|
379 | |
---|
380 | function assertWPError( $actual, $message = '' ) { |
---|
381 | $this->assertInstanceOf( 'WP_Error', $actual, $message ); |
---|
382 | } |
---|
383 | |
---|
384 | function assertNotWPError( $actual, $message = '' ) { |
---|
385 | if ( is_wp_error( $actual ) && '' === $message ) { |
---|
386 | $message = $actual->get_error_message(); |
---|
387 | } |
---|
388 | $this->assertNotInstanceOf( 'WP_Error', $actual, $message ); |
---|
389 | } |
---|
390 | |
---|
391 | function assertEqualFields( $object, $fields ) { |
---|
392 | foreach( $fields as $field_name => $field_value ) { |
---|
393 | if ( $object->$field_name != $field_value ) { |
---|
394 | $this->fail(); |
---|
395 | } |
---|
396 | } |
---|
397 | } |
---|
398 | |
---|
399 | function assertDiscardWhitespace( $expected, $actual ) { |
---|
400 | $this->assertEquals( preg_replace( '/\s*/', '', $expected ), preg_replace( '/\s*/', '', $actual ) ); |
---|
401 | } |
---|
402 | |
---|
403 | function assertEqualSets( $expected, $actual ) { |
---|
404 | sort( $expected ); |
---|
405 | sort( $actual ); |
---|
406 | $this->assertEquals( $expected, $actual ); |
---|
407 | } |
---|
408 | |
---|
409 | function assertEqualSetsWithIndex( $expected, $actual ) { |
---|
410 | ksort( $expected ); |
---|
411 | ksort( $actual ); |
---|
412 | $this->assertEquals( $expected, $actual ); |
---|
413 | } |
---|
414 | |
---|
415 | /** |
---|
416 | * Modify WordPress's query internals as if a given URL has been requested. |
---|
417 | * |
---|
418 | * @param string $url The URL for the request. |
---|
419 | */ |
---|
420 | function go_to( $url ) { |
---|
421 | // note: the WP and WP_Query classes like to silently fetch parameters |
---|
422 | // from all over the place (globals, GET, etc), which makes it tricky |
---|
423 | // to run them more than once without very carefully clearing everything |
---|
424 | $_GET = $_POST = array(); |
---|
425 | foreach (array('query_string', 'id', 'postdata', 'authordata', 'day', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages', 'pagenow') as $v) { |
---|
426 | if ( isset( $GLOBALS[$v] ) ) unset( $GLOBALS[$v] ); |
---|
427 | } |
---|
428 | $parts = parse_url($url); |
---|
429 | if (isset($parts['scheme'])) { |
---|
430 | $req = isset( $parts['path'] ) ? $parts['path'] : ''; |
---|
431 | if (isset($parts['query'])) { |
---|
432 | $req .= '?' . $parts['query']; |
---|
433 | // parse the url query vars into $_GET |
---|
434 | parse_str($parts['query'], $_GET); |
---|
435 | } |
---|
436 | } else { |
---|
437 | $req = $url; |
---|
438 | } |
---|
439 | if ( ! isset( $parts['query'] ) ) { |
---|
440 | $parts['query'] = ''; |
---|
441 | } |
---|
442 | |
---|
443 | $_SERVER['REQUEST_URI'] = $req; |
---|
444 | unset($_SERVER['PATH_INFO']); |
---|
445 | |
---|
446 | $this->flush_cache(); |
---|
447 | unset($GLOBALS['wp_query'], $GLOBALS['wp_the_query']); |
---|
448 | $GLOBALS['wp_the_query'] = new WP_Query(); |
---|
449 | $GLOBALS['wp_query'] = $GLOBALS['wp_the_query']; |
---|
450 | |
---|
451 | $public_query_vars = $GLOBALS['wp']->public_query_vars; |
---|
452 | $private_query_vars = $GLOBALS['wp']->private_query_vars; |
---|
453 | |
---|
454 | $GLOBALS['wp'] = new WP(); |
---|
455 | $GLOBALS['wp']->public_query_vars = $public_query_vars; |
---|
456 | $GLOBALS['wp']->private_query_vars = $private_query_vars; |
---|
457 | |
---|
458 | _cleanup_query_vars(); |
---|
459 | |
---|
460 | $GLOBALS['wp']->main($parts['query']); |
---|
461 | } |
---|
462 | |
---|
463 | protected function checkRequirements() { |
---|
464 | parent::checkRequirements(); |
---|
465 | |
---|
466 | // Core tests no longer check against open Trac tickets, but others using WP_UnitTestCase may do so. |
---|
467 | if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) { |
---|
468 | return; |
---|
469 | } |
---|
470 | |
---|
471 | if ( WP_TESTS_FORCE_KNOWN_BUGS ) |
---|
472 | return; |
---|
473 | $tickets = PHPUnit_Util_Test::getTickets( get_class( $this ), $this->getName( false ) ); |
---|
474 | foreach ( $tickets as $ticket ) { |
---|
475 | if ( is_numeric( $ticket ) ) { |
---|
476 | $this->knownWPBug( $ticket ); |
---|
477 | } elseif ( 'UT' == substr( $ticket, 0, 2 ) ) { |
---|
478 | $ticket = substr( $ticket, 2 ); |
---|
479 | if ( $ticket && is_numeric( $ticket ) ) |
---|
480 | $this->knownUTBug( $ticket ); |
---|
481 | } elseif ( 'Plugin' == substr( $ticket, 0, 6 ) ) { |
---|
482 | $ticket = substr( $ticket, 6 ); |
---|
483 | if ( $ticket && is_numeric( $ticket ) ) |
---|
484 | $this->knownPluginBug( $ticket ); |
---|
485 | } |
---|
486 | } |
---|
487 | } |
---|
488 | |
---|
489 | /** |
---|
490 | * Skips the current test if there is an open WordPress ticket with id $ticket_id |
---|
491 | */ |
---|
492 | function knownWPBug( $ticket_id ) { |
---|
493 | if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( $ticket_id, self::$forced_tickets ) ) |
---|
494 | return; |
---|
495 | if ( ! TracTickets::isTracTicketClosed( 'https://core.trac.wordpress.org', $ticket_id ) ) |
---|
496 | $this->markTestSkipped( sprintf( 'WordPress Ticket #%d is not fixed', $ticket_id ) ); |
---|
497 | } |
---|
498 | |
---|
499 | /** |
---|
500 | * Skips the current test if there is an open unit tests ticket with id $ticket_id |
---|
501 | */ |
---|
502 | function knownUTBug( $ticket_id ) { |
---|
503 | if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( 'UT' . $ticket_id, self::$forced_tickets ) ) |
---|
504 | return; |
---|
505 | if ( ! TracTickets::isTracTicketClosed( 'https://unit-tests.trac.wordpress.org', $ticket_id ) ) |
---|
506 | $this->markTestSkipped( sprintf( 'Unit Tests Ticket #%d is not fixed', $ticket_id ) ); |
---|
507 | } |
---|
508 | |
---|
509 | /** |
---|
510 | * Skips the current test if there is an open plugin ticket with id $ticket_id |
---|
511 | */ |
---|
512 | function knownPluginBug( $ticket_id ) { |
---|
513 | if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( 'Plugin' . $ticket_id, self::$forced_tickets ) ) |
---|
514 | return; |
---|
515 | if ( ! TracTickets::isTracTicketClosed( 'https://plugins.trac.wordpress.org', $ticket_id ) ) |
---|
516 | $this->markTestSkipped( sprintf( 'WordPress Plugin Ticket #%d is not fixed', $ticket_id ) ); |
---|
517 | } |
---|
518 | |
---|
519 | public static function forceTicket( $ticket ) { |
---|
520 | self::$forced_tickets[] = $ticket; |
---|
521 | } |
---|
522 | |
---|
523 | /** |
---|
524 | * Define constants after including files. |
---|
525 | */ |
---|
526 | function prepareTemplate( Text_Template $template ) { |
---|
527 | $template->setVar( array( 'constants' => '' ) ); |
---|
528 | $template->setVar( array( 'wp_constants' => PHPUnit_Util_GlobalState::getConstantsAsString() ) ); |
---|
529 | parent::prepareTemplate( $template ); |
---|
530 | } |
---|
531 | |
---|
532 | /** |
---|
533 | * Returns the name of a temporary file |
---|
534 | */ |
---|
535 | function temp_filename() { |
---|
536 | $tmp_dir = ''; |
---|
537 | $dirs = array( 'TMP', 'TMPDIR', 'TEMP' ); |
---|
538 | foreach( $dirs as $dir ) |
---|
539 | if ( isset( $_ENV[$dir] ) && !empty( $_ENV[$dir] ) ) { |
---|
540 | $tmp_dir = $dir; |
---|
541 | break; |
---|
542 | } |
---|
543 | if ( empty( $tmp_dir ) ) { |
---|
544 | $tmp_dir = '/tmp'; |
---|
545 | } |
---|
546 | $tmp_dir = realpath( $tmp_dir ); |
---|
547 | return tempnam( $tmp_dir, 'wpunit' ); |
---|
548 | } |
---|
549 | |
---|
550 | /** |
---|
551 | * Check each of the WP_Query is_* functions/properties against expected boolean value. |
---|
552 | * |
---|
553 | * Any properties that are listed by name as parameters will be expected to be true; any others are |
---|
554 | * expected to be false. For example, assertQueryTrue('is_single', 'is_feed') means is_single() |
---|
555 | * and is_feed() must be true and everything else must be false to pass. |
---|
556 | * |
---|
557 | * @param string $prop,... Any number of WP_Query properties that are expected to be true for the current request. |
---|
558 | */ |
---|
559 | function assertQueryTrue(/* ... */) { |
---|
560 | global $wp_query; |
---|
561 | $all = array( |
---|
562 | 'is_404', |
---|
563 | 'is_admin', |
---|
564 | 'is_archive', |
---|
565 | 'is_attachment', |
---|
566 | 'is_author', |
---|
567 | 'is_category', |
---|
568 | 'is_comment_feed', |
---|
569 | 'is_date', |
---|
570 | 'is_day', |
---|
571 | 'is_embed', |
---|
572 | 'is_feed', |
---|
573 | 'is_home', |
---|
574 | 'is_month', |
---|
575 | 'is_page', |
---|
576 | 'is_paged', |
---|
577 | 'is_post_type_archive', |
---|
578 | 'is_posts_page', |
---|
579 | 'is_preview', |
---|
580 | 'is_robots', |
---|
581 | 'is_search', |
---|
582 | 'is_single', |
---|
583 | 'is_singular', |
---|
584 | 'is_tag', |
---|
585 | 'is_tax', |
---|
586 | 'is_time', |
---|
587 | 'is_trackback', |
---|
588 | 'is_year', |
---|
589 | ); |
---|
590 | $true = func_get_args(); |
---|
591 | |
---|
592 | $passed = true; |
---|
593 | $not_false = $not_true = array(); // properties that were not set to expected values |
---|
594 | |
---|
595 | foreach ( $all as $query_thing ) { |
---|
596 | $result = is_callable( $query_thing ) ? call_user_func( $query_thing ) : $wp_query->$query_thing; |
---|
597 | |
---|
598 | if ( in_array( $query_thing, $true ) ) { |
---|
599 | if ( ! $result ) { |
---|
600 | array_push( $not_true, $query_thing ); |
---|
601 | $passed = false; |
---|
602 | } |
---|
603 | } else if ( $result ) { |
---|
604 | array_push( $not_false, $query_thing ); |
---|
605 | $passed = false; |
---|
606 | } |
---|
607 | } |
---|
608 | |
---|
609 | $message = ''; |
---|
610 | if ( count($not_true) ) |
---|
611 | $message .= implode( $not_true, ', ' ) . ' is expected to be true. '; |
---|
612 | if ( count($not_false) ) |
---|
613 | $message .= implode( $not_false, ', ' ) . ' is expected to be false.'; |
---|
614 | $this->assertTrue( $passed, $message ); |
---|
615 | } |
---|
616 | |
---|
617 | function unlink( $file ) { |
---|
618 | $exists = is_file( $file ); |
---|
619 | if ( $exists && ! in_array( $file, self::$ignore_files ) ) { |
---|
620 | //error_log( $file ); |
---|
621 | unlink( $file ); |
---|
622 | } elseif ( ! $exists ) { |
---|
623 | $this->fail( "Trying to delete a file that doesn't exist: $file" ); |
---|
624 | } |
---|
625 | } |
---|
626 | |
---|
627 | function rmdir( $path ) { |
---|
628 | $files = $this->files_in_dir( $path ); |
---|
629 | foreach ( $files as $file ) { |
---|
630 | if ( ! in_array( $file, self::$ignore_files ) ) { |
---|
631 | $this->unlink( $file ); |
---|
632 | } |
---|
633 | } |
---|
634 | } |
---|
635 | |
---|
636 | function remove_added_uploads() { |
---|
637 | // Remove all uploads. |
---|
638 | $uploads = wp_upload_dir(); |
---|
639 | $this->rmdir( $uploads['basedir'] ); |
---|
640 | } |
---|
641 | |
---|
642 | function files_in_dir( $dir ) { |
---|
643 | $files = array(); |
---|
644 | |
---|
645 | $iterator = new RecursiveDirectoryIterator( $dir ); |
---|
646 | $objects = new RecursiveIteratorIterator( $iterator ); |
---|
647 | foreach ( $objects as $name => $object ) { |
---|
648 | if ( is_file( $name ) ) { |
---|
649 | $files[] = $name; |
---|
650 | } |
---|
651 | } |
---|
652 | |
---|
653 | return $files; |
---|
654 | } |
---|
655 | |
---|
656 | function scan_user_uploads() { |
---|
657 | static $files = array(); |
---|
658 | if ( ! empty( $files ) ) { |
---|
659 | return $files; |
---|
660 | } |
---|
661 | |
---|
662 | $uploads = wp_upload_dir(); |
---|
663 | $files = $this->files_in_dir( $uploads['basedir'] ); |
---|
664 | return $files; |
---|
665 | } |
---|
666 | |
---|
667 | function delete_folders( $path ) { |
---|
668 | $this->matched_dirs = array(); |
---|
669 | if ( ! is_dir( $path ) ) { |
---|
670 | return; |
---|
671 | } |
---|
672 | |
---|
673 | $this->scandir( $path ); |
---|
674 | foreach ( array_reverse( $this->matched_dirs ) as $dir ) { |
---|
675 | rmdir( $dir ); |
---|
676 | } |
---|
677 | rmdir( $path ); |
---|
678 | } |
---|
679 | |
---|
680 | function scandir( $dir ) { |
---|
681 | foreach ( scandir( $dir ) as $path ) { |
---|
682 | if ( 0 !== strpos( $path, '.' ) && is_dir( $dir . '/' . $path ) ) { |
---|
683 | $this->matched_dirs[] = $dir . '/' . $path; |
---|
684 | $this->scandir( $dir . '/' . $path ); |
---|
685 | } |
---|
686 | } |
---|
687 | } |
---|
688 | |
---|
689 | /** |
---|
690 | * Helper to Convert a microtime string into a float |
---|
691 | */ |
---|
692 | protected function _microtime_to_float($microtime ){ |
---|
693 | $time_array = explode( ' ', $microtime ); |
---|
694 | return array_sum( $time_array ); |
---|
695 | } |
---|
696 | |
---|
697 | /** |
---|
698 | * Multisite-agnostic way to delete a user from the database. |
---|
699 | * |
---|
700 | * @since 4.3.0 |
---|
701 | */ |
---|
702 | public static function delete_user( $user_id ) { |
---|
703 | if ( is_multisite() ) { |
---|
704 | return wpmu_delete_user( $user_id ); |
---|
705 | } else { |
---|
706 | return wp_delete_user( $user_id ); |
---|
707 | } |
---|
708 | } |
---|
709 | |
---|
710 | /** |
---|
711 | * Utility method that resets permalinks and flushes rewrites. |
---|
712 | * |
---|
713 | * @since 4.4.0 |
---|
714 | * |
---|
715 | * @global WP_Rewrite $wp_rewrite |
---|
716 | * |
---|
717 | * @param string $structure Optional. Permalink structure to set. Default empty. |
---|
718 | */ |
---|
719 | public function set_permalink_structure( $structure = '' ) { |
---|
720 | global $wp_rewrite; |
---|
721 | |
---|
722 | $wp_rewrite->init(); |
---|
723 | $wp_rewrite->set_permalink_structure( $structure ); |
---|
724 | $wp_rewrite->flush_rules(); |
---|
725 | } |
---|
726 | |
---|
727 | function _make_attachment($upload, $parent_post_id = 0) { |
---|
728 | $type = ''; |
---|
729 | if ( !empty($upload['type']) ) { |
---|
730 | $type = $upload['type']; |
---|
731 | } else { |
---|
732 | $mime = wp_check_filetype( $upload['file'] ); |
---|
733 | if ($mime) |
---|
734 | $type = $mime['type']; |
---|
735 | } |
---|
736 | |
---|
737 | $attachment = array( |
---|
738 | 'post_title' => basename( $upload['file'] ), |
---|
739 | 'post_content' => '', |
---|
740 | 'post_type' => 'attachment', |
---|
741 | 'post_parent' => $parent_post_id, |
---|
742 | 'post_mime_type' => $type, |
---|
743 | 'guid' => $upload[ 'url' ], |
---|
744 | ); |
---|
745 | |
---|
746 | // Save the data |
---|
747 | $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $parent_post_id ); |
---|
748 | wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); |
---|
749 | return $id; |
---|
750 | } |
---|
751 | } |
---|