Commit 7e532bf2 authored by catch's avatar catch

Issue #2030173 by jhedstrom: Convert system module's ValidNumberStepUnitTest to phpunit.

parent 76ed8761
......@@ -2,6 +2,7 @@
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Json;
use Drupal\Component\Utility\Number;
use Drupal\Component\Utility\Settings;
use Drupal\Component\Utility\SortArray;
use Drupal\Component\Utility\String;
......@@ -634,47 +635,24 @@ function valid_url($url, $absolute = FALSE) {
/**
* Verifies that a number is a multiple of a given step.
*
* The implementation assumes it is dealing with IEEE 754 double precision
* floating point numbers that are used by PHP on most systems.
* @see \Drupal\Component\Utility\Number::validStep()
*
* This is based on the number/range verification methods of webkit.
*
* @param $value
* @param numeric $value
* The value that needs to be checked.
* @param $step
* @param numeric $step
* The step scale factor. Must be positive.
* @param $offset
* @param numeric $offset
* (optional) An offset, to which the difference must be a multiple of the
* given step.
*
* @return bool
* TRUE if no step mismatch has occured, or FALSE otherwise.
*
* @see http://opensource.apple.com/source/WebCore/WebCore-1298/html/NumberInputType.cpp
* @deprecated as of Drupal 8.0. Use
* \Drupal\Component\Utility\Number::validStep() directly instead
*/
function valid_number_step($value, $step, $offset = 0.0) {
$double_value = (double) abs($value - $offset);
// The fractional part of a double has 53 bits. The greatest number that could
// be represented with that is 2^53. If the given value is even bigger than
// $step * 2^53, then dividing by $step will result in a very small remainder.
// Since that remainder can't even be represented with a single precision
// float the following computation of the remainder makes no sense and we can
// safely ignore it instead.
if ($double_value / pow(2.0, 53) > $step) {
return TRUE;
}
// Now compute that remainder of a division by $step.
$remainder = (double) abs($double_value - $step * round($double_value / $step));
// $remainder is a double precision floating point number. Remainders that
// can't be represented with single precision floats are acceptable. The
// fractional part of a float has 24 bits. That means remainders smaller than
// $step * 2^-24 are acceptable.
$computed_acceptable_error = (double)($step / pow(2.0, 24));
return $computed_acceptable_error >= $remainder || $remainder >= ($step - $computed_acceptable_error);
return Number::validStep($value, $step, $offset);
}
/**
......
......@@ -7,6 +7,7 @@
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Number;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Url;
use Drupal\Core\Form\FormInterface;
......@@ -4385,7 +4386,7 @@ function form_validate_number(&$element, &$form_state) {
// #min is set).
$offset = isset($element['#min']) ? $element['#min'] : 0.0;
if (!valid_number_step($value, $element['#step'], $offset)) {
if (!Number::validStep($value, $element['#step'], $offset)) {
form_error($element, t('%name is not a valid number.', array('%name' => $name)));
}
}
......
<?php
/**
* @file
* Contains \Drupal\Component\Utility\Number.
*/
namespace Drupal\Component\Utility;
/**
* Provides helper methods for manipulating numbers.
*/
class Number {
/**
* Verifies that a number is a multiple of a given step.
*
* The implementation assumes it is dealing with IEEE 754 double precision
* floating point numbers that are used by PHP on most systems.
*
* This is based on the number/range verification methods of webkit.
*
* @param numeric $value
* The value that needs to be checked.
* @param numeric $step
* The step scale factor. Must be positive.
* @param numeric $offset
* (optional) An offset, to which the difference must be a multiple of the
* given step.
*
* @return bool
* TRUE if no step mismatch has occured, or FALSE otherwise.
*
* @see http://opensource.apple.com/source/WebCore/WebCore-1298/html/NumberInputType.cpp
*/
public static function validStep($value, $step, $offset = 0.0) {
$double_value = (double) abs($value - $offset);
// The fractional part of a double has 53 bits. The greatest number that
// could be represented with that is 2^53. If the given value is even bigger
// than $step * 2^53, then dividing by $step will result in a very small
// remainder. Since that remainder can't even be represented with a single
// precision float the following computation of the remainder makes no sense
// and we can safely ignore it instead.
if ($double_value / pow(2.0, 53) > $step) {
return TRUE;
}
// Now compute that remainder of a division by $step.
$remainder = (double) abs($double_value - $step * round($double_value / $step));
// $remainder is a double precision floating point number. Remainders that
// can't be represented with single precision floats are acceptable. The
// fractional part of a float has 24 bits. That means remainders smaller than
// $step * 2^-24 are acceptable.
$computed_acceptable_error = (double)($step / pow(2.0, 24));
return $computed_acceptable_error >= $remainder || $remainder >= ($step - $computed_acceptable_error);
}
}
<?php
/**
* @file
* Definition of Drupal\system\Tests\Common\ValidNumberStepUnitTest.
*/
namespace Drupal\system\Tests\Common;
use Drupal\simpletest\UnitTestBase;
/**
* Tests number step validation by valid_number_step().
*/
class ValidNumberStepUnitTest extends UnitTestBase {
public static function getInfo() {
return array(
'name' => 'Number step validation',
'description' => 'Tests number step validation by valid_number_step()',
'group' => 'Common',
);
}
/**
* Tests valid_number_step() without offset.
*/
function testNumberStep() {
// Value and step equal.
$this->assertTrue(valid_number_step(10.3, 10.3));
// Valid integer steps.
$this->assertTrue(valid_number_step(42, 21));
$this->assertTrue(valid_number_step(42, 3));
// Valid float steps.
$this->assertTrue(valid_number_step(42, 10.5));
$this->assertTrue(valid_number_step(1, 1/3));
$this->assertTrue(valid_number_step(-100, 100/7));
$this->assertTrue(valid_number_step(1000, -10));
// Valid and very small float steps.
$this->assertTrue(valid_number_step(1000.12345, 1e-10));
$this->assertTrue(valid_number_step(3.9999999999999, 1e-13));
// Invalid integer steps.
$this->assertFalse(valid_number_step(100, 30));
$this->assertFalse(valid_number_step(-10, 4));
// Invalid float steps.
$this->assertFalse(valid_number_step(6, 5/7));
$this->assertFalse(valid_number_step(10.3, 10.25));
// Step mismatches very close to beeing valid.
$this->assertFalse(valid_number_step(70 + 9e-7, 10 + 9e-7));
$this->assertFalse(valid_number_step(1936.5, 3e-8));
}
/**
* Tests valid_number_step() with offset.
*/
function testNumberStepOffset() {
// Try obvious fits.
$this->assertTrue(valid_number_step(11.3, 10.3, 1));
$this->assertTrue(valid_number_step(100, 10, 50));
$this->assertTrue(valid_number_step(-100, 90/7, -10));
$this->assertTrue(valid_number_step(2/7 + 5/9, 1/7, 5/9));
// Ensure a small offset is still invalid.
$this->assertFalse(valid_number_step(10.3, 10.3, 0.0001));
$this->assertFalse(valid_number_step(1/5, 1/7, 1/11));
// Try negative values and offsets.
$this->assertFalse(valid_number_step(1000, 10, -5));
$this->assertFalse(valid_number_step(-10, 4, 0));
$this->assertFalse(valid_number_step(-10, 4, -4));
}
}
<?php
/**
* @file
* Contains \Drupal\Tests\Common\Utility\NumberTest.
*
* @see \Drupal\Component\Utility\Number
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Number;
use Drupal\Tests\UnitTestCase;
/**
* Tests number step validation by valid_number_step().
*/
class NumberTest extends UnitTestCase {
public static function getInfo() {
return array(
'name' => 'Number step validation',
'description' => 'Tests number step validation by valid_number_step()',
'group' => 'Common',
);
}
/**
* Tests Number::validStep() without offset.
*
* @param numeric $value
* The value argument for Number::validStep().
* @param numeric $step
* The step argument for Number::validStep().
* @param boolean $expected
* Expected return value from Number::validStep().
*
* @dataProvider providerTestValidStep
*/
public function testValidStep($value, $step, $expected) {
$return = Number::validStep($value, $step);
$this->assertEquals($expected, $return);
}
/**
* Tests valid_number_step() with offset.
*
* @param numeric $value
* The value argument for Number::validStep().
* @param numeric $step
* The step argument for Number::validStep().
* @param numeric $offset
* The offset argument for Number::validStep().
* @param boolean $expected
* Expected return value from Number::validStep().
*
* @dataProvider providerTestValidStepOffset
*/
public function testValidStepOffset($value, $step, $offset, $expected) {
$return = Number::validStep($value, $step, $offset);
$this->assertEquals($expected, $return);
}
/**
* Provides data for self::testNumberStep().
*
* @see \Drupal\Tests\Component\Utility\Number::testValidStep
*/
public static function providerTestValidStep() {
return array(
// Value and step equal.
array(10.3, 10.3, TRUE),
// Valid integer steps.
array(42, 21, TRUE),
array(42, 3, TRUE),
// Valid float steps.
array(42, 10.5, TRUE),
array(1, 1/3, TRUE),
array(-100, 100/7, TRUE),
array(1000, -10, TRUE),
// Valid and very small float steps.
array(1000.12345, 1e-10, TRUE),
array(3.9999999999999, 1e-13, TRUE),
// Invalid integer steps.
array(100, 30, FALSE),
array(-10, 4, FALSE),
// Invalid float steps.
array(6, 5/7, FALSE),
array(10.3, 10.25, FALSE),
// Step mismatches very close to beeing valid.
array(70 + 9e-7, 10 + 9e-7, FALSE),
array(1936.5, 3e-8, FALSE),
);
}
/**
* Data provider for \Drupal\Test\Component\Utility\NumberTest::testValidStepOffset().
*
* @see \Drupal\Test\Component\Utility\NumberTest::testValidStepOffset()
*/
public static function providerTestValidStepOffset() {
return array(
// Try obvious fits.
array(11.3, 10.3, 1, TRUE),
array(100, 10, 50, TRUE),
array(-100, 90/7, -10, TRUE),
array(2/7 + 5/9, 1/7, 5/9, TRUE),
// Ensure a small offset is still invalid.
array(10.3, 10.3, 0.0001, FALSE),
array(1/5, 1/7, 1/11, FALSE),
// Try negative values and offsets.
array(1000, 10, -5, FALSE),
array(-10, 4, 0, FALSE),
array(-10, 4, -4, FALSE),
);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment