Completed
Pull Request — master (#2)
by Stephen
13:19
created

WP_UnitTestCase::expectedDeprecated()   D

Complexity

Conditions 9
Paths 32

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 19
nc 32
nop 0
dl 0
loc 31
rs 4.909
c 0
b 0
f 0
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
		global $wpdb;
61
62
		$wpdb->suppress_errors = false;
63
		$wpdb->show_errors = true;
64
		$wpdb->db_connect();
65
		ini_set('display_errors', 1 );
66
67
		parent::setUpBeforeClass();
68
69
		$c = self::get_called_class();
70
		if ( ! method_exists( $c, 'wpSetUpBeforeClass' ) ) {
71
			self::commit_transaction();
72
			return;
73
		}
74
75
		call_user_func( array( $c, 'wpSetUpBeforeClass' ), self::factory() );
76
77
		self::commit_transaction();
78
	}
79
80
	public static function tearDownAfterClass() {
81
		parent::tearDownAfterClass();
82
83
		_delete_all_data();
84
		self::flush_cache();
85
86
		$c = self::get_called_class();
87
		if ( ! method_exists( $c, 'wpTearDownAfterClass' ) ) {
88
			self::commit_transaction();
89
			return;
90
		}
91
92
		call_user_func( array( $c, 'wpTearDownAfterClass' ) );
93
94
		self::commit_transaction();
95
	}
96
97
	function setUp() {
98
		set_time_limit(0);
99
100
		if ( ! self::$ignore_files ) {
101
			self::$ignore_files = $this->scan_user_uploads();
102
		}
103
104
		if ( ! self::$hooks_saved ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression self::$hooks_saved of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
105
			$this->_backup_hooks();
106
		}
107
108
		global $wp_rewrite;
109
110
		$this->clean_up_global_scope();
111
112
		/*
113
		 * When running core tests, ensure that post types and taxonomies
114
		 * are reset for each test. We skip this step for non-core tests,
115
		 * given the large number of plugins that register post types and
116
		 * taxonomies at 'init'.
117
		 */
118
		if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) {
119
			$this->reset_post_types();
120
			$this->reset_taxonomies();
121
			$this->reset_post_statuses();
122
			$this->reset__SERVER();
123
124
			if ( $wp_rewrite->permalink_structure ) {
125
				$this->set_permalink_structure( '' );
126
			}
127
		}
128
129
		$this->start_transaction();
130
		$this->expectDeprecated();
131
		add_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) );
132
	}
133
134
	/**
135
	 * Detect post-test failure conditions.
136
	 *
137
	 * We use this method to detect expectedDeprecated and expectedIncorrectUsage annotations.
138
	 *
139
	 * @since 4.2.0
140
	 */
141
	protected function assertPostConditions() {
142
		$this->expectedDeprecated();
143
	}
144
145
	/**
146
	 * After a test method runs, reset any state in WordPress the test method might have changed.
147
	 */
148
	function tearDown() {
149
		global $wpdb, $wp_query, $wp;
150
		$wpdb->query( 'ROLLBACK' );
151
		if ( is_multisite() ) {
152
			while ( ms_is_switched() ) {
153
				restore_current_blog();
154
			}
155
		}
156
		$wp_query = new WP_Query();
157
		$wp = new WP();
158
159
		// Reset globals related to the post loop and `setup_postdata()`.
160
		$post_globals = array( 'post', 'id', 'authordata', 'currentday', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages' );
161
		foreach ( $post_globals as $global ) {
162
			$GLOBALS[ $global ] = null;
163
		}
164
165
		remove_theme_support( 'html5' );
166
		remove_filter( 'query', array( $this, '_create_temporary_tables' ) );
167
		remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
168
		remove_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) );
169
		$this->_restore_hooks();
170
		wp_set_current_user( 0 );
171
	}
172
173
	function clean_up_global_scope() {
174
		$_GET = array();
175
		$_POST = array();
176
		self::flush_cache();
177
	}
178
179
	/**
180
	 * Allow tests to be skipped on some automated runs
181
	 *
182
	 * For test runs on Travis for something other than trunk/master 
183
	 * we want to skip tests that only need to run for master.
184
	 */
185
	public function skipOnAutomatedBranches() {
186
		// gentenv can be disabled
187
		if ( ! function_exists( 'getenv' ) ) {
188
			return false;
189
		}
190
191
		// https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
192
		$travis_branch       = getenv( 'TRAVIS_BRANCH' );
193
		$travis_pull_request = getenv( 'TRAVIS_PULL_REQUEST' );
194
195
		if ( false !== $travis_pull_request && 'master' !== $travis_branch ) {
196
			$this->markTestSkipped( 'For automated test runs, this test is only run on trunk/master' );
197
		}
198
	}
199
200
	/**
201
	 * Allow tests to be skipped when Multisite is not in use.
202
	 *
203
	 * Use in conjunction with the ms-required group.
204
	 */
205
	public function skipWithoutMultisite() {
206
		if ( ! is_multisite() ) {
207
			$this->markTestSkipped( 'Test only runs on Multisite' );
208
		}
209
	}
210
211
	/**
212
	 * Allow tests to be skipped when Multisite is in use.
213
	 *
214
	 * Use in conjunction with the ms-excluded group.
215
	 */
216
	public function skipWithMultisite() {
217
		if ( is_multisite() ) {
218
			$this->markTestSkipped( 'Test does not run on Multisite' );
219
		}
220
	}
221
222
	/**
223
	 * Unregister existing post types and register defaults.
224
	 *
225
	 * Run before each test in order to clean up the global scope, in case
226
	 * a test forgets to unregister a post type on its own, or fails before
227
	 * it has a chance to do so.
228
	 */
229
	protected function reset_post_types() {
230
		foreach ( get_post_types( array(), 'objects' ) as $pt ) {
231
			if ( empty( $pt->tests_no_auto_unregister ) ) {
232
				_unregister_post_type( $pt->name );
233
			}
234
		}
235
		create_initial_post_types();
236
	}
237
238
	/**
239
	 * Unregister existing taxonomies and register defaults.
240
	 *
241
	 * Run before each test in order to clean up the global scope, in case
242
	 * a test forgets to unregister a taxonomy on its own, or fails before
243
	 * it has a chance to do so.
244
	 */
245
	protected function reset_taxonomies() {
246
		foreach ( get_taxonomies() as $tax ) {
247
			_unregister_taxonomy( $tax );
248
		}
249
		create_initial_taxonomies();
250
	}
251
252
	/**
253
	 * Unregister non-built-in post statuses.
254
	 */
255
	protected function reset_post_statuses() {
256
		foreach ( get_post_stati( array( '_builtin' => false ) ) as $post_status ) {
257
			_unregister_post_status( $post_status );
258
		}
259
	}
260
261
	/**
262
	 * Reset `$_SERVER` variables
263
	 */
264
	protected function reset__SERVER() {
265
		tests_reset__SERVER();
266
	}
267
268
	/**
269
	 * Saves the action and filter-related globals so they can be restored later.
270
	 *
271
	 * Stores $merged_filters, $wp_actions, $wp_current_filter, and $wp_filter
272
	 * on a class variable so they can be restored on tearDown() using _restore_hooks().
273
	 *
274
	 * @global array $merged_filters
275
	 * @global array $wp_actions
276
	 * @global array $wp_current_filter
277
	 * @global array $wp_filter
278
	 * @return void
279
	 */
280
	protected function _backup_hooks() {
281
		$globals = array( 'wp_actions', 'wp_current_filter' );
282
		foreach ( $globals as $key ) {
283
			self::$hooks_saved[ $key ] = $GLOBALS[ $key ];
284
		}
285
		self::$hooks_saved['wp_filter'] = array();
286
		foreach ( $GLOBALS['wp_filter'] as $hook_name => $hook_object ) {
287
			self::$hooks_saved['wp_filter'][ $hook_name ] = clone $hook_object;
288
		}
289
	}
290
291
	/**
292
	 * Restores the hook-related globals to their state at setUp()
293
	 * so that future tests aren't affected by hooks set during this last test.
294
	 *
295
	 * @global array $merged_filters
296
	 * @global array $wp_actions
297
	 * @global array $wp_current_filter
298
	 * @global array $wp_filter
299
	 * @return void
300
	 */
301
	protected function _restore_hooks() {
302
		$globals = array( 'wp_actions', 'wp_current_filter' );
303
		foreach ( $globals as $key ) {
304
			if ( isset( self::$hooks_saved[ $key ] ) ) {
305
				$GLOBALS[ $key ] = self::$hooks_saved[ $key ];
306
			}
307
		}
308
		if ( isset( self::$hooks_saved['wp_filter'] ) ) {
309
			$GLOBALS['wp_filter'] = array();
310
			foreach ( self::$hooks_saved['wp_filter'] as $hook_name => $hook_object ) {
311
				$GLOBALS['wp_filter'][ $hook_name ] = clone $hook_object;
312
			}
313
		}
314
	}
315
316
	static function flush_cache() {
317
		global $wp_object_cache;
318
		$wp_object_cache->group_ops = array();
319
		$wp_object_cache->stats = array();
320
		$wp_object_cache->memcache_debug = array();
321
		$wp_object_cache->cache = array();
322
		if ( method_exists( $wp_object_cache, '__remoteset' ) ) {
323
			$wp_object_cache->__remoteset();
324
		}
325
		wp_cache_flush();
326
		wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details' ) );
327
		wp_cache_add_non_persistent_groups( array( 'comment', 'counts', 'plugins' ) );
328
	}
329
330
	function start_transaction() {
331
		global $wpdb;
332
		$wpdb->query( 'SET autocommit = 0;' );
333
		$wpdb->query( 'START TRANSACTION;' );
334
		add_filter( 'query', array( $this, '_create_temporary_tables' ) );
335
		add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
336
	}
337
338
	/**
339
	 * Commit the queries in a transaction.
340
	 *
341
	 * @since 4.1.0
342
	 */
343
	public static function commit_transaction() {
344
		global $wpdb;
345
		$wpdb->query( 'COMMIT;' );
346
	}
347
348
	function _create_temporary_tables( $query ) {
349
		if ( 'CREATE TABLE' === substr( trim( $query ), 0, 12 ) )
350
			return substr_replace( trim( $query ), 'CREATE TEMPORARY TABLE', 0, 12 );
351
		return $query;
352
	}
353
354
	function _drop_temporary_tables( $query ) {
355
		if ( 'DROP TABLE' === substr( trim( $query ), 0, 10 ) )
356
			return substr_replace( trim( $query ), 'DROP TEMPORARY TABLE', 0, 10 );
357
		return $query;
358
	}
359
360
	function get_wp_die_handler( $handler ) {
361
		return array( $this, 'wp_die_handler' );
362
	}
363
364
	function wp_die_handler( $message ) {
365
		if ( ! is_scalar( $message ) ) {
366
			$message = '0';
367
		}
368
369
		throw new WPDieException( $message );
370
	}
371
372
	function expectDeprecated() {
373
		$annotations = $this->getAnnotations();
374
		foreach ( array( 'class', 'method' ) as $depth ) {
375
			if ( ! empty( $annotations[ $depth ]['expectedDeprecated'] ) )
376
				$this->expected_deprecated = array_merge( $this->expected_deprecated, $annotations[ $depth ]['expectedDeprecated'] );
377
			if ( ! empty( $annotations[ $depth ]['expectedIncorrectUsage'] ) )
378
				$this->expected_doing_it_wrong = array_merge( $this->expected_doing_it_wrong, $annotations[ $depth ]['expectedIncorrectUsage'] );
379
		}
380
		add_action( 'deprecated_function_run', array( $this, 'deprecated_function_run' ) );
381
		add_action( 'deprecated_argument_run', array( $this, 'deprecated_function_run' ) );
382
		add_action( 'deprecated_hook_run', array( $this, 'deprecated_function_run' ) );
383
		add_action( 'doing_it_wrong_run', array( $this, 'doing_it_wrong_run' ) );
384
		add_action( 'deprecated_function_trigger_error', '__return_false' );
385
		add_action( 'deprecated_argument_trigger_error', '__return_false' );
386
		add_action( 'deprecated_hook_trigger_error',     '__return_false' );
387
		add_action( 'doing_it_wrong_trigger_error',      '__return_false' );
388
	}
389
390
	function expectedDeprecated() {
391
		$errors = array();
392
393
		$not_caught_deprecated = array_diff( $this->expected_deprecated, $this->caught_deprecated );
394
		foreach ( $not_caught_deprecated as $not_caught ) {
395
			$errors[] = "Failed to assert that $not_caught triggered a deprecated notice";
396
		}
397
398
		$unexpected_deprecated = array_diff( $this->caught_deprecated, $this->expected_deprecated );
399
		foreach ( $unexpected_deprecated as $unexpected ) {
400
			$errors[] = "Unexpected deprecated notice for $unexpected";
401
		}
402
403
		$not_caught_doing_it_wrong = array_diff( $this->expected_doing_it_wrong, $this->caught_doing_it_wrong );
404
		foreach ( $not_caught_doing_it_wrong as $not_caught ) {
405
			$errors[] = "Failed to assert that $not_caught triggered an incorrect usage notice";
406
		}
407
408
		$unexpected_doing_it_wrong = array_diff( $this->caught_doing_it_wrong, $this->expected_doing_it_wrong );
409
		foreach ( $unexpected_doing_it_wrong as $unexpected ) {
410
			$errors[] = "Unexpected incorrect usage notice for $unexpected";
411
		}
412
413
		// Perform an assertion, but only if there are expected or unexpected deprecated calls or wrongdoings
414
		if ( ! empty( $this->expected_deprecated ) ||
415
			! empty( $this->expected_doing_it_wrong ) ||
416
			! empty( $this->caught_deprecated ) ||
417
			! empty( $this->caught_doing_it_wrong ) ) {
418
			$this->assertEmpty( $errors, implode( "\n", $errors ) );
419
		}
420
	}
421
422
	/**
423
	 * Declare an expected `_deprecated_function()` or `_deprecated_argument()` call from within a test.
424
	 *
425
	 * @since 4.2.0
426
	 *
427
	 * @param string $deprecated Name of the function, method, class, or argument that is deprecated. Must match
428
	 *                           first parameter of the `_deprecated_function()` or `_deprecated_argument()` call.
429
	 */
430
	public function setExpectedDeprecated( $deprecated ) {
431
		array_push( $this->expected_deprecated, $deprecated );
432
	}
433
434
	/**
435
	 * Declare an expected `_doing_it_wrong()` call from within a test.
436
	 *
437
	 * @since 4.2.0
438
	 *
439
	 * @param string $deprecated Name of the function, method, or class that appears in the first argument of the
0 ignored issues
show
Bug introduced by
There is no parameter named $deprecated. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
440
	 *                           source `_doing_it_wrong()` call.
441
	 */
442
	public function setExpectedIncorrectUsage( $doing_it_wrong ) {
443
		array_push( $this->expected_doing_it_wrong, $doing_it_wrong );
444
	}
445
446
	/**
447
	 * PHPUnit 6+ compatibility shim.
448
	 *
449
	 * @param mixed      $exception
450
	 * @param string     $message
451
	 * @param int|string $code
452
	 */
453
	public function setExpectedException( $exception, $message = '', $code = null ) {
454
		if ( method_exists( 'PHPUnit_Framework_TestCase', 'setExpectedException' ) ) {
455
			parent::setExpectedException( $exception, $message, $code );
456
		} else {
457
			$this->expectException( $exception );
458
			if ( '' !== $message ) {
459
				$this->expectExceptionMessage( $message );
460
			}
461
			if ( null !== $code ) {
462
				$this->expectExceptionCode( $code );
463
			}
464
		}
465
	}
466
467
	function deprecated_function_run( $function ) {
468
		if ( ! in_array( $function, $this->caught_deprecated ) )
469
			$this->caught_deprecated[] = $function;
470
	}
471
472
	function doing_it_wrong_run( $function ) {
473
		if ( ! in_array( $function, $this->caught_doing_it_wrong ) )
474
			$this->caught_doing_it_wrong[] = $function;
475
	}
476
477
	function assertWPError( $actual, $message = '' ) {
478
		$this->assertInstanceOf( 'WP_Error', $actual, $message );
479
	}
480
481
	function assertNotWPError( $actual, $message = '' ) {
482
		if ( is_wp_error( $actual ) && '' === $message ) {
483
			$message = $actual->get_error_message();
484
		}
485
		$this->assertNotInstanceOf( 'WP_Error', $actual, $message );
486
	}
487
488
	function assertIXRError( $actual, $message = '' ) {
489
		$this->assertInstanceOf( 'IXR_Error', $actual, $message );
490
	}
491
492
	function assertNotIXRError( $actual, $message = '' ) {
493
		if ( $actual instanceof IXR_Error && '' === $message ) {
494
			$message = $actual->message;
495
		}
496
		$this->assertNotInstanceOf( 'IXR_Error', $actual, $message );
497
	}
498
499
	function assertEqualFields( $object, $fields ) {
500
		foreach( $fields as $field_name => $field_value ) {
501
			if ( $object->$field_name != $field_value ) {
502
				$this->fail();
503
			}
504
		}
505
	}
506
507
	function assertDiscardWhitespace( $expected, $actual ) {
508
		$this->assertEquals( preg_replace( '/\s*/', '', $expected ), preg_replace( '/\s*/', '', $actual ) );
509
	}
510
511
	function assertEqualSets( $expected, $actual ) {
512
		sort( $expected );
513
		sort( $actual );
514
		$this->assertEquals( $expected, $actual );
515
	}
516
517
	function assertEqualSetsWithIndex( $expected, $actual ) {
518
		ksort( $expected );
519
		ksort( $actual );
520
		$this->assertEquals( $expected, $actual );
521
	}
522
523
	/**
524
	 * Asserts that the given variable is a multidimensional array, and that all arrays are non-empty.
525
	 *
526
	 * @param array $array
527
	 */
528
	function assertNonEmptyMultidimensionalArray( $array ) {
529
		$this->assertTrue( is_array( $array ) );
530
		$this->assertNotEmpty( $array );
531
532
		foreach( $array as $sub_array ) {
533
			$this->assertTrue( is_array( $sub_array ) );
534
			$this->assertNotEmpty( $sub_array );
535
		}
536
	}
537
538
	/**
539
	 * Asserts that a condition is not false.
540
	 *
541
	 * @param bool   $condition
542
	 * @param string $message
543
	 *
544
	 * @throws PHPUnit_Framework_AssertionFailedError
545
	 */
546
	public static function assertNotFalse( $condition, $message = '' ) {
547
		self::assertThat( $condition, self::logicalNot( self::isFalse() ), $message );
548
	}
549
550
	/**
551
	 * Modify WordPress's query internals as if a given URL has been requested.
552
	 *
553
	 * @param string $url The URL for the request.
554
	 */
555
	function go_to( $url ) {
556
		// note: the WP and WP_Query classes like to silently fetch parameters
557
		// from all over the place (globals, GET, etc), which makes it tricky
558
		// to run them more than once without very carefully clearing everything
559
		$_GET = $_POST = array();
560
		foreach (array('query_string', 'id', 'postdata', 'authordata', 'day', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages', 'pagenow') as $v) {
561
			if ( isset( $GLOBALS[$v] ) ) unset( $GLOBALS[$v] );
562
		}
563
		$parts = parse_url($url);
564
		if (isset($parts['scheme'])) {
565
			$req = isset( $parts['path'] ) ? $parts['path'] : '';
566
			if (isset($parts['query'])) {
567
				$req .= '?' . $parts['query'];
568
				// parse the url query vars into $_GET
569
				parse_str($parts['query'], $_GET);
570
			}
571
		} else {
572
			$req = $url;
573
		}
574
		if ( ! isset( $parts['query'] ) ) {
575
			$parts['query'] = '';
576
		}
577
578
		$_SERVER['REQUEST_URI'] = $req;
579
		unset($_SERVER['PATH_INFO']);
580
581
		self::flush_cache();
582
		unset($GLOBALS['wp_query'], $GLOBALS['wp_the_query']);
583
		$GLOBALS['wp_the_query'] = new WP_Query();
584
		$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
585
586
		$public_query_vars  = $GLOBALS['wp']->public_query_vars;
587
		$private_query_vars = $GLOBALS['wp']->private_query_vars;
588
589
		$GLOBALS['wp'] = new WP();
590
		$GLOBALS['wp']->public_query_vars  = $public_query_vars;
591
		$GLOBALS['wp']->private_query_vars = $private_query_vars;
592
593
		_cleanup_query_vars();
594
595
		$GLOBALS['wp']->main($parts['query']);
596
	}
597
598
	protected function checkRequirements() {
599
		parent::checkRequirements();
600
601
		$annotations = $this->getAnnotations();
602
603
		if ( ! empty( $annotations['group'] ) ) {
604
			if ( in_array( 'ms-required', $annotations['group'], true ) ) {
605
				$this->skipWithoutMultisite();
606
			}
607
			if ( in_array( 'ms-excluded', $annotations['group'], true ) ) {
608
				$this->skipWithMultisite();
609
			}
610
		}
611
612
		// Core tests no longer check against open Trac tickets, but others using WP_UnitTestCase may do so.
613
		if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) {
614
			return;
615
		}
616
617
		if ( WP_TESTS_FORCE_KNOWN_BUGS )
618
			return;
619
		$tickets = PHPUnit_Util_Test::getTickets( get_class( $this ), $this->getName( false ) );
0 ignored issues
show
Bug introduced by
Consider using $this->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
620
		foreach ( $tickets as $ticket ) {
621
			if ( is_numeric( $ticket ) ) {
622
				$this->knownWPBug( $ticket );
623
			} elseif ( 'Plugin' == substr( $ticket, 0, 6 ) ) {
624
				$ticket = substr( $ticket, 6 );
625
				if ( $ticket && is_numeric( $ticket ) )
626
					$this->knownPluginBug( $ticket );
627
			}
628
		}
629
	}
630
631
	/**
632
	 * Skips the current test if there is an open WordPress ticket with id $ticket_id
633
	 */
634
	function knownWPBug( $ticket_id ) {
635
		if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( $ticket_id, self::$forced_tickets ) )
636
			return;
637
		if ( ! TracTickets::isTracTicketClosed( 'https://core.trac.wordpress.org', $ticket_id ) )
0 ignored issues
show
Bug Best Practice introduced by
The expression \TracTickets::isTracTick...press.org', $ticket_id) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
638
			$this->markTestSkipped( sprintf( 'WordPress Ticket #%d is not fixed', $ticket_id ) );
639
	}
640
641
	/**
642
	 * @deprecated No longer used since the unit test Trac was merged into Core's.
643
	 */
644
	function knownUTBug( $ticket_id ) {
645
		return;
646
	}
647
648
	/**
649
	 * Skips the current test if there is an open plugin ticket with id $ticket_id
650
	 */
651
	function knownPluginBug( $ticket_id ) {
652
		if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( 'Plugin' . $ticket_id, self::$forced_tickets ) )
653
			return;
654
		if ( ! TracTickets::isTracTicketClosed( 'https://plugins.trac.wordpress.org', $ticket_id ) )
0 ignored issues
show
Bug Best Practice introduced by
The expression \TracTickets::isTracTick...press.org', $ticket_id) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
655
			$this->markTestSkipped( sprintf( 'WordPress Plugin Ticket #%d is not fixed', $ticket_id ) );
656
	}
657
658
	public static function forceTicket( $ticket ) {
659
		self::$forced_tickets[] = $ticket;
660
	}
661
662
	/**
663
	 * Define constants after including files.
664
	 */
665
	function prepareTemplate( Text_Template $template ) {
666
		$template->setVar( array( 'constants' => '' ) );
667
		$template->setVar( array( 'wp_constants' => PHPUnit_Util_GlobalState::getConstantsAsString() ) );
668
		parent::prepareTemplate( $template );
669
	}
670
671
	/**
672
	 * Returns the name of a temporary file
673
	 */
674
	function temp_filename() {
675
		$tmp_dir = '';
676
		$dirs = array( 'TMP', 'TMPDIR', 'TEMP' );
677
		foreach( $dirs as $dir )
678
			if ( isset( $_ENV[$dir] ) && !empty( $_ENV[$dir] ) ) {
679
				$tmp_dir = $dir;
680
				break;
681
			}
682
		if ( empty( $tmp_dir ) ) {
683
			$tmp_dir = '/tmp';
684
		}
685
		$tmp_dir = realpath( $tmp_dir );
686
		return tempnam( $tmp_dir, 'wpunit' );
687
	}
688
689
	/**
690
	 * Check each of the WP_Query is_* functions/properties against expected boolean value.
691
	 *
692
	 * Any properties that are listed by name as parameters will be expected to be true; all others are
693
	 * expected to be false. For example, assertQueryTrue('is_single', 'is_feed') means is_single()
694
	 * and is_feed() must be true and everything else must be false to pass.
695
	 *
696
	 * @param string $prop,... Any number of WP_Query properties that are expected to be true for the current request.
0 ignored issues
show
Bug introduced by
There is no parameter named $prop,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
697
	 */
698
	function assertQueryTrue(/* ... */) {
699
		global $wp_query;
700
		$all = array(
701
			'is_404',
702
			'is_admin',
703
			'is_archive',
704
			'is_attachment',
705
			'is_author',
706
			'is_category',
707
			'is_comment_feed',
708
			'is_date',
709
			'is_day',
710
			'is_embed',
711
			'is_feed',
712
			'is_front_page',
713
			'is_home',
714
			'is_month',
715
			'is_page',
716
			'is_paged',
717
			'is_post_type_archive',
718
			'is_posts_page',
719
			'is_preview',
720
			'is_robots',
721
			'is_search',
722
			'is_single',
723
			'is_singular',
724
			'is_tag',
725
			'is_tax',
726
			'is_time',
727
			'is_trackback',
728
			'is_year',
729
		);
730
		$true = func_get_args();
731
732
		foreach ( $true as $true_thing ) {
733
			$this->assertContains( $true_thing, $all, "Unknown conditional: {$true_thing}." );
734
		}
735
736
		$passed = true;
737
		$message = '';
738
739
		foreach ( $all as $query_thing ) {
740
			$result = is_callable( $query_thing ) ? call_user_func( $query_thing ) : $wp_query->$query_thing;
741
742
			if ( in_array( $query_thing, $true ) ) {
743
				if ( ! $result ) {
744
					$message .= $query_thing . ' is false but is expected to be true. ' . PHP_EOL;
745
					$passed = false;
746
				}
747
			} else if ( $result ) {
748
				$message .= $query_thing . ' is true but is expected to be false. ' . PHP_EOL;
749
				$passed = false;
750
			}
751
		}
752
753
		if ( ! $passed ) {
754
			$this->fail( $message );
755
		}
756
	}
757
758
	function unlink( $file ) {
759
		$exists = is_file( $file );
760
		if ( $exists && ! in_array( $file, self::$ignore_files ) ) {
761
			//error_log( $file );
762
			unlink( $file );
763
		} elseif ( ! $exists ) {
764
			$this->fail( "Trying to delete a file that doesn't exist: $file" );
765
		}
766
	}
767
768
	function rmdir( $path ) {
769
		$files = $this->files_in_dir( $path );
770
		foreach ( $files as $file ) {
771
			if ( ! in_array( $file, self::$ignore_files ) ) {
772
				$this->unlink( $file );
773
			}
774
		}
775
	}
776
777
	function remove_added_uploads() {
778
		// Remove all uploads.
779
		$uploads = wp_upload_dir();
780
		$this->rmdir( $uploads['basedir'] );
781
	}
782
783
	function files_in_dir( $dir ) {
784
		$files = array();
785
786
		$iterator = new RecursiveDirectoryIterator( $dir );
787
		$objects = new RecursiveIteratorIterator( $iterator );
788
		foreach ( $objects as $name => $object ) {
789
			if ( is_file( $name ) ) {
790
				$files[] = $name;
791
			}
792
		}
793
794
		return $files;
795
	}
796
797
	function scan_user_uploads() {
798
		static $files = array();
799
		if ( ! empty( $files ) ) {
800
			return $files;
801
		}
802
803
		$uploads = wp_upload_dir();
804
		$files = $this->files_in_dir( $uploads['basedir'] );
805
		return $files;
806
	}
807
808
	function delete_folders( $path ) {
809
		$this->matched_dirs = array();
810
		if ( ! is_dir( $path ) ) {
811
			return;
812
		}
813
814
		$this->scandir( $path );
815
		foreach ( array_reverse( $this->matched_dirs ) as $dir ) {
816
			rmdir( $dir );
817
		}
818
		rmdir( $path );
819
	}
820
821
	function scandir( $dir ) {
822
		foreach ( scandir( $dir ) as $path ) {
823
			if ( 0 !== strpos( $path, '.' ) && is_dir( $dir . '/' . $path ) ) {
824
				$this->matched_dirs[] = $dir . '/' . $path;
825
				$this->scandir( $dir . '/' . $path );
826
			}
827
		}
828
	}
829
830
	/**
831
	 * Helper to Convert a microtime string into a float
832
	 */
833
	protected function _microtime_to_float($microtime ){
834
		$time_array = explode( ' ', $microtime );
835
		return array_sum( $time_array );
836
	}
837
838
	/**
839
	 * Multisite-agnostic way to delete a user from the database.
840
	 *
841
	 * @since 4.3.0
842
	 */
843
	public static function delete_user( $user_id ) {
844
		if ( is_multisite() ) {
845
			return wpmu_delete_user( $user_id );
846
		} else {
847
			return wp_delete_user( $user_id );
848
		}
849
	}
850
851
	/**
852
	 * Utility method that resets permalinks and flushes rewrites.
853
	 *
854
	 * @since 4.4.0
855
	 *
856
	 * @global WP_Rewrite $wp_rewrite
857
	 *
858
	 * @param string $structure Optional. Permalink structure to set. Default empty.
859
	 */
860
	public function set_permalink_structure( $structure = '' ) {
861
		global $wp_rewrite;
862
863
		$wp_rewrite->init();
864
		$wp_rewrite->set_permalink_structure( $structure );
865
		$wp_rewrite->flush_rules();
866
	}
867
868
	function _make_attachment($upload, $parent_post_id = 0) {
869
		$type = '';
870
		if ( !empty($upload['type']) ) {
871
			$type = $upload['type'];
872
		} else {
873
			$mime = wp_check_filetype( $upload['file'] );
874
			if ($mime)
0 ignored issues
show
Bug Best Practice introduced by
The expression $mime of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
875
				$type = $mime['type'];
876
		}
877
878
		$attachment = array(
879
			'post_title' => basename( $upload['file'] ),
880
			'post_content' => '',
881
			'post_type' => 'attachment',
882
			'post_parent' => $parent_post_id,
883
			'post_mime_type' => $type,
884
			'guid' => $upload[ 'url' ],
885
		);
886
887
		// Save the data
888
		$id = wp_insert_attachment( $attachment, $upload[ 'file' ], $parent_post_id );
889
		wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
890
		return $id;
891
	}
892
893
	/**
894
	 * There's no way to change post_modified through WP functions.
895
	 */
896
	protected function update_post_modified( $post_id, $date ) {
897
		global $wpdb;
898
		return $wpdb->update(
899
			$wpdb->posts,
900
			array(
901
				'post_modified' => $date,
902
				'post_modified_gmt' => $date,
903
			),
904
			array(
905
				'ID' => $post_id,
906
			),
907
			array(
908
				'%s',
909
				'%s',
910
			),
911
			array(
912
				'%d',
913
			)
914
		);
915
	}
916
}
917