Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utility\FilteredIteratorTest: split up test class + minor improvements to FilteredIterator #729

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 8 additions & 15 deletions src/Utility/FilteredIterator.php
Expand Up @@ -48,6 +48,8 @@ public function __construct($data, $callback) {
/**
* Prevent unserialization of the object for security reasons.
*
* This method is used on PHP 7.4+.
*
* @phpcs:disable PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__unserializeFound
*
* @param array $data Restored array of data originally serialized.
Expand All @@ -59,15 +61,16 @@ public function __unserialize($data) {}
// phpcs:enable

/**
* Perform reinitialization tasks.
* Prevent creating a PHP value from a stored representation of the object for security reasons.
*
* Prevents a callback from being injected during unserialization of an object.
* This method is used on PHP < 7.4.
*
* @param string $data The serialized string.
*
* @return void
*/
public function __wakeup() {
unset($this->callback);
}
#[ReturnTypeWillChange]
public function unserialize($data) {}

/**
* Get the current item's value after filtering
Expand All @@ -84,14 +87,4 @@ public function current() {

return $value;
}

/**
* Prevent creating a PHP value from a stored representation of the object for security reasons.
*
* @param string $data The serialized string.
*
* @return void
*/
#[ReturnTypeWillChange]
public function unserialize($data) {}
}
137 changes: 137 additions & 0 deletions tests/Utility/FilteredIterator/ConstructorTest.php
@@ -0,0 +1,137 @@
<?php

namespace WpOrg\Requests\Tests\Utility\FilteredIterator;

use ReflectionObject;
use stdClass;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Tests\TestCase;
use WpOrg\Requests\Tests\TypeProviderHelper;
use WpOrg\Requests\Utility\FilteredIterator;

/**
* @covers \WpOrg\Requests\Utility\FilteredIterator::__construct
*/
final class ConstructorTest extends TestCase {

/**
* Tests that valid $data is accepted by the constructor.
*
* @dataProvider dataValidData
*
* @param mixed $input Valid input.
*
* @return void
*/
public function testValidData($input) {
$this->assertInstanceOf(FilteredIterator::class, new FilteredIterator($input, 'ltrim'));
}

/**
* Data Provider.
*
* @return array
*/
public function dataValidData() {
return TypeProviderHelper::getSelection(TypeProviderHelper::GROUP_ITERABLE);
}

/**
* Tests receiving an exception when an invalid input type is passed as `$data` to the constructor.
*
* @dataProvider dataInvalidData
*
* @param mixed $input Invalid input.
*
* @return void
*/
public function testInvalidData($input) {
$this->expectException(InvalidArgument::class);
$this->expectExceptionMessage('Argument #1 ($data) must be of type iterable');

new FilteredIterator($input, 'ltrim');
}

/**
* Data Provider.
*
* @return array
*/
public function dataInvalidData() {
return TypeProviderHelper::getAllExcept(TypeProviderHelper::GROUP_ITERABLE);
}

/**
* Tests that valid $callback is accepted by the constructor.
*
* @dataProvider dataValidCallback
*
* @param mixed $input Valid input.
*
* @return void
*/
public function testValidCallback($input) {
$obj = new FilteredIterator([], $input);

$reflection = new ReflectionObject($obj);
$property = $reflection->getProperty('callback');
$property->setAccessible(true);
$callback_value = $property->getValue($obj);
$property->setAccessible(false);

$this->assertSame($input, $callback_value, 'Callback property has not been set');
}

/**
* Data Provider.
*
* @return array
*/
public function dataValidCallback() {
return [
'existing PHP native function' => ['strtolower'],
'dummy callback method' => [[$this, 'dummyCallback']],
];
}

/**
* Verify that invalid callbacks are not accepted by the constructor.
*
* @dataProvider dataInvalidCallback
*
* @param mixed $input Invalid callback.
*
* @return void
*/
public function testInvalidCallback($input) {
$obj = new FilteredIterator([], $input);

$reflection = new ReflectionObject($obj);
$property = $reflection->getProperty('callback');
$property->setAccessible(true);
$callback_value = $property->getValue($obj);
$property->setAccessible(false);

$this->assertNull($callback_value, 'Callback property has been set to invalid callback');
}

/**
* Data Provider.
*
* @return array
*/
public function dataInvalidCallback() {
return [
'null' => [null],
'non-existent function' => ['functionname'],
'plain object' => [new stdClass(), 'method'],
];
}

/**
* Dummy callback method.
*
* @return void
*/
public function dummyCallback() {}
}
71 changes: 71 additions & 0 deletions tests/Utility/FilteredIterator/CurrentTest.php
@@ -0,0 +1,71 @@
<?php

namespace WpOrg\Requests\Tests\Utility\FilteredIterator;

use WpOrg\Requests\Tests\TestCase;
use WpOrg\Requests\Utility\FilteredIterator;

/**
* @covers \WpOrg\Requests\Utility\FilteredIterator::current
*/
final class CurrentTest extends TestCase {

/**
* Tests that when there is a valid callback, the current value is always filtered.
*
* @dataProvider dataCallbackIsAppliedIfValid
*
* @param iterable $data The array or object to be iterated on.
* @param callable $callback Callback to be called on each value.
* @param array $expected The values expected to be seen during iteration.
*
* @return void
*/
public function testCallbackIsAppliedIfValid($data, $callback, $expected) {
$iterator = new FilteredIterator($data, $callback);

$actual = [];
foreach ($iterator as $key => $value) {
$actual[$key] = $value;
}

$this->assertSame($expected, $actual);
}

/**
* Data Provider.
*
* @return array
*/
public function dataCallbackIsAppliedIfValid() {
$original = [
'key1' => 'lowercase',
'key2' => 'UPPER CASE',
'key3' => 'Sentence case',
'key4' => 'Title Case',
];

return [
'invalid callback: null' => [
'data' => $original,
'callback' => null,
'expected' => $original,
],
'invalid callback: function does not exist' => [
'data' => $original,
'callback' => 'i_am_not_callable',
'expected' => $original,
],
'valid callback' => [
'data' => $original,
'callback' => 'strtolower',
'expected' => [
'key1' => 'lowercase',
'key2' => 'upper case',
'key3' => 'sentence case',
'key4' => 'title case',
],
],
];
}
}