diff --git a/tests/phpunit/tests/functions/wpListFilter.php b/tests/phpunit/tests/functions/wpListFilter.php index 1039b70f3ec20..2c9a8a7d6b134 100644 --- a/tests/phpunit/tests/functions/wpListFilter.php +++ b/tests/phpunit/tests/functions/wpListFilter.php @@ -10,6 +10,8 @@ class Tests_Functions_wpListFilter extends WP_UnitTestCase { /** + * @ticket 53987 + * * @dataProvider data_wp_list_filter * * @param array $input_list An array of objects to filter. @@ -22,6 +24,32 @@ public function test_wp_list_filter( $input_list, $args, $operator, $expected ) $this->assertEqualSetsWithIndex( $expected, wp_list_filter( $input_list, $args, $operator ) ); } + /** + * Tests that the 'AND' operator is used when no operator is passed. + * + * @ticket 53987 + */ + public function test_wp_list_filter_should_default_to_and_operator() { + $input_list = array( + (object) array( + 'foo' => 'bar', + 'bar' => 'baz', + ), + (object) array( 'foo' => 'bar' ), + ); + + $this->assertEqualSetsWithIndex( + array( 0 => $input_list[0] ), + wp_list_filter( + $input_list, + array( + 'foo' => 'bar', + 'bar' => 'baz', + ) + ) + ); + } + /** * Data provider. * @@ -29,25 +57,25 @@ public function test_wp_list_filter( $input_list, $args, $operator, $expected ) */ public static function data_wp_list_filter() { return array( - 'string instead of array' => array( + 'string instead of array' => array( 'foo', array(), 'AND', array(), ), - 'object instead of array' => array( + 'object instead of array' => array( (object) array( 'foo' ), array(), 'AND', array(), ), - 'empty args' => array( + 'empty args' => array( array( 'foo', 'bar' ), array(), 'AND', array( 'foo', 'bar' ), ), - 'invalid operator' => array( + 'invalid operator' => array( array( (object) array( 'foo' => 'bar' ), (object) array( 'foo' => 'baz' ), @@ -56,7 +84,7 @@ public static function data_wp_list_filter() { 'XOR', array(), ), - 'single argument to match' => array( + 'single argument to match' => array( array( (object) array( 'foo' => 'bar', @@ -94,7 +122,7 @@ public static function data_wp_list_filter() { ), ), ), - 'all must match' => array( + 'all must match' => array( array( (object) array( 'foo' => 'bar', @@ -132,7 +160,7 @@ public static function data_wp_list_filter() { ), ), ), - 'any must match' => array( + 'any must match' => array( array( (object) array( 'foo' => 'bar', @@ -179,7 +207,7 @@ public static function data_wp_list_filter() { ), ), ), - 'none must match' => array( + 'none must match' => array( array( (object) array( 'foo' => 'bar', @@ -216,7 +244,7 @@ public static function data_wp_list_filter() { ), ), ), - 'string to int comparison' => array( + 'string to int comparison' => array( array( (object) array( 'foo' => '1', @@ -230,6 +258,248 @@ public static function data_wp_list_filter() { ), ), ), + 'bool to string comparison' => array( + array( + (object) array( + 'foo' => true, + ), + ), + array( 'foo' => 'yes' ), + 'AND', + array( + 0 => (object) array( + 'foo' => true, + ), + ), + ), + 'null instead of array' => array( + null, + array( 'foo' => 'bar' ), + 'AND', + array(), + ), + 'empty list' => array( + array(), + array( 'foo' => 'bar' ), + 'AND', + array(), + ), + + /* + * Empty arguments are checked before the operator is validated, + * so the original list is returned even for an invalid operator. + */ + 'empty args, invalid operator' => array( + array( 'foo', 'bar' ), + array(), + 'XOR', + array( 'foo', 'bar' ), + ), + + // The operator is uppercased before it is validated. + 'lowercase AND operator' => array( + array( + (object) array( 'foo' => 'bar' ), + (object) array( 'foo' => 'baz' ), + ), + array( 'foo' => 'bar' ), + 'and', + array( + 0 => (object) array( 'foo' => 'bar' ), + ), + ), + 'mixed case OR operator' => array( + array( + (object) array( 'foo' => 'bar' ), + (object) array( 'foo' => 'baz' ), + ), + array( 'foo' => 'bar' ), + 'oR', + array( + 0 => (object) array( 'foo' => 'bar' ), + ), + ), + 'lowercase NOT operator' => array( + array( + (object) array( 'foo' => 'bar' ), + (object) array( 'foo' => 'baz' ), + ), + array( 'foo' => 'bar' ), + 'not', + array( + 1 => (object) array( 'foo' => 'baz' ), + ), + ), + + // Lists of arrays are supported alongside lists of objects. + 'arrays: single argument to match' => array( + array( + array( + 'foo' => 'bar', + 'key' => 'foo', + ), + array( + 'foo' => 'baz', + 'key' => 'value', + ), + array( + 'foo' => 'bar', + 'key' => 'value', + ), + ), + array( 'foo' => 'bar' ), + 'AND', + array( + 0 => array( + 'foo' => 'bar', + 'key' => 'foo', + ), + 2 => array( + 'foo' => 'bar', + 'key' => 'value', + ), + ), + ), + 'arrays: all must match' => array( + array( + array( + 'foo' => 'bar', + 'bar' => 'baz', + ), + array( 'foo' => 'bar' ), + ), + array( + 'foo' => 'bar', + 'bar' => 'baz', + ), + 'AND', + array( + 0 => array( + 'foo' => 'bar', + 'bar' => 'baz', + ), + ), + ), + 'arrays: any must match' => array( + array( + array( 'foo' => 'bar' ), + array( 'bar' => 'baz' ), + array( 'foo' => 'baz' ), + ), + array( + 'foo' => 'bar', + 'bar' => 'baz', + ), + 'OR', + array( + 0 => array( 'foo' => 'bar' ), + 1 => array( 'bar' => 'baz' ), + ), + ), + 'arrays: none must match' => array( + array( + array( 'foo' => 'bar' ), + array( 'bar' => 'baz' ), + array( 'foo' => 'baz' ), + ), + array( + 'foo' => 'bar', + 'bar' => 'baz', + ), + 'NOT', + array( + 2 => array( 'foo' => 'baz' ), + ), + ), + 'mixed arrays and objects' => array( + array( + array( 'foo' => 'bar' ), + (object) array( 'foo' => 'bar' ), + array( 'foo' => 'baz' ), + (object) array( 'foo' => 'baz' ), + ), + array( 'foo' => 'bar' ), + 'AND', + array( + 0 => array( 'foo' => 'bar' ), + 1 => (object) array( 'foo' => 'bar' ), + ), + ), + + /* + * Values that are neither arrays nor objects can never match, + * so they are only returned by the 'NOT' operator. + */ + 'scalar values with AND' => array( + array( 'a string', 42, null, array( 'foo' => 'bar' ) ), + array( 'foo' => 'bar' ), + 'AND', + array( + 3 => array( 'foo' => 'bar' ), + ), + ), + 'scalar values with NOT' => array( + array( 'a string', 42, null, array( 'foo' => 'bar' ) ), + array( 'foo' => 'bar' ), + 'NOT', + array( + 0 => 'a string', + 1 => 42, + 2 => null, + ), + ), + + /* + * Array values are checked with array_key_exists(), so a null value + * matches, while object properties are checked with isset(), + * so a null property does not. + */ + 'array with a null value matching null' => array( + array( array( 'foo' => null ) ), + array( 'foo' => null ), + 'AND', + array( + 0 => array( 'foo' => null ), + ), + ), + 'object with a null property matching null' => array( + array( (object) array( 'foo' => null ) ), + array( 'foo' => null ), + 'AND', + array(), + ), + + 'string keys are preserved' => array( + array( + 'first' => (object) array( 'foo' => 'bar' ), + 'second' => (object) array( 'foo' => 'baz' ), + 'third' => (object) array( 'foo' => 'bar' ), + ), + array( 'foo' => 'bar' ), + 'AND', + array( + 'first' => (object) array( 'foo' => 'bar' ), + 'third' => (object) array( 'foo' => 'bar' ), + ), + ), + 'no matches with OR' => array( + array( + (object) array( 'foo' => 'baz' ), + (object) array( 'foo' => 'qux' ), + ), + array( 'foo' => 'bar' ), + 'OR', + array(), + ), + 'all matches with NOT' => array( + array( + (object) array( 'foo' => 'bar' ), + (object) array( 'foo' => 'bar' ), + ), + array( 'foo' => 'bar' ), + 'NOT', + array(), + ), ); } }