Commit f1e9349a authored by catch's avatar catch

Issue #2020209 by alexpott, YesCT, Gábor Hojtsy: Fixed Random failures due to...

Issue #2020209 by alexpott, YesCT, Gábor Hojtsy: Fixed Random failures due to non uniqueness of randomString() and randomName().
parent aa9c2ccc
<?php
/**
* @file
* Contains \Drupal\Component\Utility\Random.
*/
namespace Drupal\Component\Utility;
/**
* Defines a utility class for creating random data.
*/
class Random {
/**
* The maximum number of times name() and string() can loop.
*
* This prevents infinite loops if the length of the random value is very
* small.
*
* @see \Drupal\Tests\Component\Utility\RandomTest
*/
const MAXIMUM_TRIES = 100;
/**
* A list of unique strings generated by string().
*
* @var array
*/
static protected $strings = array();
/**
* A list of unique names generated by name().
*
* @var array
*/
static protected $names = array();
/**
* Generates a random string of ASCII characters of codes 32 to 126.
*
* The generated string includes alpha-numeric characters and common
* miscellaneous characters. Use this method when testing general input
* where the content is not restricted.
*
* @param int $length
* Length of random string to generate.
* @param bool $unique
* (optional) If TRUE ensures that the random string returned is unique.
* Defaults to FALSE.
*
* @return string
* Randomly generated string.
*
* @see \Drupal\Component\Utility\Random::name()
*/
public static function string($length = 8, $unique = FALSE) {
$counter = 0;
do {
if ($counter == static::MAXIMUM_TRIES) {
throw new \RuntimeException('Unable to generate a unique random name');
}
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= chr(mt_rand(32, 126));
}
$counter++;
} while ($unique && isset(static::$strings[$str]));
if ($unique) {
static::$strings[$str] = TRUE;
}
return $str;
}
/**
* Generates a random string containing letters and numbers.
*
* The string will always start with a letter. The letters may be upper or
* lower case. This method is better for restricted inputs that do not
* accept certain characters. For example, when testing input fields that
* require machine readable values (i.e. without spaces and non-standard
* characters) this method is best.
*
* @param int $length
* Length of random string to generate.
* @param bool $unique
* (optional) If TRUE ensures that the random string returned is unique.
* Defaults to FALSE.
*
* @return string
* Randomly generated string.
*
* @see \Drupal\Component\Utility\Random::string()
*/
public static function name($length = 8, $unique = FALSE) {
$values = array_merge(range(65, 90), range(97, 122), range(48, 57));
$max = count($values) - 1;
$counter = 0;
do {
if ($counter == static::MAXIMUM_TRIES) {
throw new \RuntimeException('Unable to generate a unique random name');
}
$str = chr(mt_rand(97, 122));
for ($i = 1; $i < $length; $i++) {
$str .= chr($values[mt_rand(0, $max)]);
}
$counter++;
} while ($unique && isset(static::$names[$str]));
if ($unique) {
static::$names[$str] = TRUE;
}
return $str;
}
/**
* Generates a random PHP object.
*
* @param int $size
* The number of random keys to add to the object.
*
* @return \stdClass
* The generated object, with the specified number of random keys. Each key
* has a random string value.
*/
public static function object($size = 4) {
$object = new \stdClass();
for ($i = 0; $i < $size; $i++) {
$random_key = static::name();
$random_value = static::string();
$object->{$random_key} = $random_value;
}
return $object;
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\simpletest;
use Drupal\Component\Utility\Random;
use Drupal\Core\Database\Database;
use Drupal\Component\Utility\Settings;
use Drupal\Core\Config\ConfigImporter;
......@@ -178,20 +179,6 @@ abstract class TestBase {
*/
protected $configImporter;
/**
* A list of unique strings generated by randomString().
*
* @var array
*/
protected $randomStrings = array();
/**
* A list of unique names generated by randomName().
*
* @var array
*/
protected $randomNames = array();
/**
* Constructor for Test.
*
......@@ -1169,62 +1156,38 @@ protected function settingsSet($name, $value) {
/**
* Generates a unique random string of ASCII characters of codes 32 to 126.
*
* The generated string includes alpha-numeric characters and common
* miscellaneous characters. Use this method when testing general input
* where the content is not restricted.
*
* Do not use this method when special characters are not possible (e.g., in
* machine or file names that have already been validated); instead, use
* Drupal\simpletest\TestBase::randomName().
* \Drupal\simpletest\TestBase::randomName().
*
* @param $length
* @param int $length
* Length of random string to generate.
*
* @return
* Randomly generated string.
* @return string
* Randomly generated unique string.
*
* @see Drupal\simpletest\TestBase::randomName()
* @see \Drupal\Component\Utility\Random::string()
*/
public function randomString($length = 8) {
do {
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= chr(mt_rand(32, 126));
}
} while (isset($this->randomStrings[$str]));
return $str;
return Random::string($length, TRUE);
}
/**
* Generates a unique random string containing letters and numbers.
*
* The string will always start with a letter. The letters may be upper or
* lower case. This method is better for restricted inputs that do not
* accept certain characters. For example, when testing input fields that
* require machine readable values (i.e. without spaces and non-standard
* characters) this method is best.
*
* Do not use this method when testing unvalidated user input. Instead, use
* Drupal\simpletest\TestBase::randomString().
* \Drupal\simpletest\TestBase::randomString().
*
* @param $length
* @param int $length
* Length of random string to generate.
*
* @return
* Randomly generated string.
* @return string
* Randomly generated unique string.
*
* @see Drupal\simpletest\TestBase::randomString()
* @see \Drupal\Component\Utility\Random::name()
*/
public function randomName($length = 8) {
do {
$values = array_merge(range(65, 90), range(97, 122), range(48, 57));
$max = count($values) - 1;
$str = chr(mt_rand(97, 122));
for ($i = 1; $i < $length; $i++) {
$str .= chr($values[mt_rand(0, $max)]);
}
} while (isset($this->randomNames[$str]));
return $str;
return Random::name($length, TRUE);
}
/**
......@@ -1236,15 +1199,11 @@ public function randomName($length = 8) {
* @return \stdClass
* The generated object, with the specified number of random keys. Each key
* has a random string value.
*
* @see \Drupal\Component\Utility\Random::object()
*/
public function randomObject($size = 4) {
$object = new \stdClass();
for ($i = 0; $i < $size; $i++) {
$random_key = $this->randomName();
$random_value = $this->randomString();
$object->{$random_key} = $random_value;
}
return $object;
return Random::object($size);
}
/**
......
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\RandomTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Random;
use Drupal\Component\Utility\String;
use Drupal\Tests\UnitTestCase;
/**
* Tests random data generation.
*
* @see \Drupal\Component\Utility\Random
*/
class RandomTest extends UnitTestCase {
public static function getInfo() {
return array(
'name' => 'Random data generation tests',
'description' => 'Confirm that Random::name() and Random::string() work correctly.',
'group' => 'Common',
);
}
/**
* Tests unique random name generation.
*
* @see \Drupal\Component\Utility\Random::name()
*/
public function testRandomStringUniqueness() {
$strings = array();
for ($i = 0; $i <= 50; $i++) {
$str = Random::string(1, TRUE);
$this->assertFalse(isset($strings[$str]), String::format('Generated duplicate random string !string', array('!string' => $str)));
$strings[$str] = TRUE;
}
}
/**
* Tests unique random string generation.
*
* @see \Drupal\Component\Utility\Random::string()
*/
public function testRandomNamesUniqueness() {
$names = array();
for ($i = 0; $i <= 10; $i++) {
$str = Random::name(1, TRUE);
$this->assertFalse(isset($names[$str]), String::format('Generated duplicate random name !name', array('!name' => $str)));
$names[$str] = TRUE;
}
}
/**
* Tests infinite loop prevention whilst generating random names.
*
* @see \Drupal\Component\Utility\Random::name()
*
* @expectedException RuntimeException
*/
public function testRandomNameException() {
// There are fewer than 100 possibilities so an exception should occur to
// prevent infinite loops.
for ($i = 0; $i <= 100; $i++) {
$str = Random::name(1, TRUE);
$names[$str] = TRUE;
}
}
/**
* Tests infinite loop prevention whilst generating random strings.
*
* @see \Drupal\Component\Utility\Random::string()
*
* @expectedException RuntimeException
*/
public function testRandomStringException() {
// There are fewer than 100 possibilities so an exception should occur to
// prevent infinite loops.
for ($i = 0; $i <= 100; $i++) {
$str = Random::string(1, TRUE);
$names[$str] = TRUE;
}
}
/**
* Tests random name generation if uniqueness is not enforced.
*
* @see \Drupal\Component\Utility\Random::name()
*/
public function testRandomNameNonUnique() {
// There are fewer than 100 possibilities if we were forcing uniqueness so
// exception would occur.
for ($i = 0; $i <= 100; $i++) {
Random::name(1);
}
$this->assertTrue(TRUE, 'No exception thrown when uniqueness is not enforced.');
}
/**
* Tests random string if uniqueness is not enforced.
*
* @see \Drupal\Component\Utility\Random::string()
*/
public function testRandomStringNonUnique() {
// There are fewer than 100 possibilities if we were forcing uniqueness so
// exception would occur.
for ($i = 0; $i <= 100; $i++) {
Random::string(1);
}
$this->assertTrue(TRUE, 'No exception thrown when uniqueness is not enforced.');
}
/**
* Tests random object generation to ensure the expected number of properties.
*
* @see \Drupal\Component\Utility\Random::object()
*/
public function testRandomObject() {
// For values of 0 and 1 \Drupal\Component\Utility\Random::object() will
// have different execution paths.
for ($i = 0; $i <= 1; $i++) {
$obj = Random::object($i);
$this->assertEquals($i, count(get_object_vars($obj)), 'Generated random object has expected number of properties');
}
}
}
......@@ -7,6 +7,8 @@
namespace Drupal\Tests;
use Drupal\Component\Utility\Random;
/**
* Provides a base class and helpers for Drupal unit tests.
*/
......@@ -33,33 +35,18 @@ public static function getInfo() {
}
/**
* Generates a random string containing letters and numbers.
*
* The string will always start with a letter. The letters may be upper or
* lower case. This method is better for restricted inputs that do not accept
* certain characters. For example, when testing input fields that require
* machine readable values (i.e. without spaces and non-standard characters)
* this method is best.
*
* Do not use this method when testing unvalidated user input. Instead, use
* Drupal\simpletest\TestBase::randomString().
* Generates a unique random string containing letters and numbers.
*
* @param int $length
* Length of random string to generate.
*
* @return string
* Randomly generated string.
* Randomly generated unique string.
*
* @see Drupal\simpletest\TestBase::randomString()
* @see \Drupal\Component\Utility::string()
*/
public static function randomName($length = 8) {
$values = array_merge(range(65, 90), range(97, 122), range(48, 57));
$max = count($values) - 1;
$str = chr(mt_rand(97, 122));
for ($i = 1; $i < $length; $i++) {
$str .= chr($values[mt_rand(0, $max)]);
}
return $str;
public function randomName($length = 8) {
return Random::name($length, TRUE);
}
/**
......
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