diff --git a/core/lib/Drupal/Component/Utility/Random.php b/core/lib/Drupal/Component/Utility/Random.php index df989c0..1533e65 100644 --- a/core/lib/Drupal/Component/Utility/Random.php +++ b/core/lib/Drupal/Component/Utility/Random.php @@ -27,14 +27,14 @@ class Random { * * @var array */ - static protected $strings = array(); + protected $strings = array(); /** * A list of unique names generated by name(). * * @var array */ - static protected $names = array(); + protected $names = array(); /** * Generates a random string of ASCII characters of codes 32 to 126. @@ -48,15 +48,21 @@ class Random { * @param bool $unique * (optional) If TRUE ensures that the random string returned is unique. * Defaults to FALSE. + * @param callable $validator + * (optional) A callable to validate the the string. Defaults to NULL. * * @return string * Randomly generated string. * * @see \Drupal\Component\Utility\Random::name() */ - public static function string($length = 8, $unique = FALSE) { + public function string($length = 8, $unique = FALSE, $validator = NULL) { $counter = 0; + // Continue to loop if $unique is TRUE and the string has been generated + // already or if $callable is not null and the validate method returns + // FALSE. To generate a random string this loop must be carried out at least + // once. do { if ($counter == static::MAXIMUM_TRIES) { throw new \RuntimeException('Unable to generate a unique random name'); @@ -66,10 +72,20 @@ public static function string($length = 8, $unique = FALSE) { $str .= chr(mt_rand(32, 126)); } $counter++; - } while ($unique && isset(static::$strings[$str])); + + $continue = FALSE; + if ($unique) { + $continue = isset($this->strings[$str]); + } + if (!$continue && is_callable($validator)) { + // If the validator callback returns FALSE generate another random + // string. + $continue = !call_user_func($validator, $str); + } + } while ($continue); if ($unique) { - static::$strings[$str] = TRUE; + $this->strings[$str] = TRUE; } return $str; @@ -95,7 +111,7 @@ public static function string($length = 8, $unique = FALSE) { * * @see \Drupal\Component\Utility\Random::string() */ - public static function name($length = 8, $unique = FALSE) { + public function name($length = 8, $unique = FALSE) { $values = array_merge(range(65, 90), range(97, 122), range(48, 57)); $max = count($values) - 1; $counter = 0; @@ -109,10 +125,10 @@ public static function name($length = 8, $unique = FALSE) { $str .= chr($values[mt_rand(0, $max)]); } $counter++; - } while ($unique && isset(static::$names[$str])); + } while ($unique && isset($this->names[$str])); if ($unique) { - static::$names[$str] = TRUE; + $this->names[$str] = TRUE; } return $str; @@ -128,11 +144,11 @@ public static function name($length = 8, $unique = FALSE) { * The generated object, with the specified number of random keys. Each key * has a random string value. */ - public static function object($size = 4) { + public function object($size = 4) { $object = new \stdClass(); for ($i = 0; $i < $size; $i++) { - $random_key = static::name(); - $random_value = static::string(); + $random_key = $this->name(); + $random_value = $this->string(); $object->{$random_key} = $random_value; } return $object; diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 3eb5c84..4baa3fe 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -180,6 +180,13 @@ protected $configImporter; /** + * The random generator. + * + * @var \Drupal\Component\Utility\Random + */ + protected $randomGenerator; + + /** * Constructor for Test. * * @param $test_id @@ -1169,7 +1176,38 @@ protected function settingsSet($name, $value) { * @see \Drupal\Component\Utility\Random::string() */ public function randomString($length = 8) { - return Random::string($length, TRUE); + return $this->getRandomGenerator()->string($length, TRUE, array($this, 'randomStringValidate')); + } + + /** + * Callback for random string validation. + * + * @see \Drupal\Component\Utility\Random::name() + * + * @param string $str + * The random string to validate. + * + * @return bool + * TRUE if the random string is valid, FALSE if not. + */ + public function randomStringValidate($str) { + // Consecutive spaces causes issues for + // Drupal\simpletest\WebTestBase::assertLink(). + if (preg_match('/\s{2,}/', $str)) { + return FALSE; + } + + // Starting with a space means that length might not be what is expected. + if (preg_match('/^\s/', $str)) { + return FALSE; + } + + // Ending with a space means that length might not be what is expected. + if (preg_match('/\s$/', $str)) { + return FALSE; + } + + return TRUE; } /** @@ -1187,7 +1225,7 @@ public function randomString($length = 8) { * @see \Drupal\Component\Utility\Random::name() */ public function randomName($length = 8) { - return Random::name($length, TRUE); + return $this->getRandomGenerator()->name($length, TRUE); } /** @@ -1203,7 +1241,20 @@ public function randomName($length = 8) { * @see \Drupal\Component\Utility\Random::object() */ public function randomObject($size = 4) { - return Random::object($size); + return $this->getRandomGenerator()->object($size); + } + + /** + * Gets the random generator for the utility methods. + * + * @return \Drupal\Component\Utility\Random + * The random generator + */ + protected function getRandomGenerator() { + if (!is_object($this->randomGenerator)) { + $this->randomGenerator = new Random(); + } + return $this->randomGenerator; } /** diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/WebTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/WebTestBaseTest.php new file mode 100644 index 0000000..2bed56a --- /dev/null +++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/WebTestBaseTest.php @@ -0,0 +1,51 @@ + 'WebTestBase helper functions test', + 'description' => 'Test helper functions provided by the WebTestBase class.', + 'group' => 'Simpletest', + ); + } + + /** + * Tests the behaviour of Drupal\simpletest\WebTestBase::assertLink(). + */ + public function testAsertLink() { + $link_texts = array( + 'curry paste', + 'thai green curry paste', + 'Number\\9', + 'Number\9', + 'Number9', + 'Number9\\', + '$7@|{v$S', + ); + + // Set up some links to test. + $this->content = ''; + foreach ($link_texts as $link) { + $this->content .= '' . $link .''; + } + + foreach ($link_texts as $link) { + $result = $this->assertLink($link); + $this->assertTrue($result, 'The assertLink method found link.'); + } + } + +} diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 8e295b3..07b8622 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -1877,7 +1877,7 @@ protected function buildXPathQuery($xpath, array $args = array()) { // Return the string. $value = count($parts) > 1 ? 'concat(' . implode(', \'"\', ', $parts) . ')' : $parts[0]; } - $xpath = preg_replace('/' . preg_quote($placeholder) . '\b/', $value, $xpath); + $xpath = str_replace($placeholder, $value, $xpath); } return $xpath; } diff --git a/core/modules/simpletest/tests/Drupal/simpletest/Tests/TestBaseTest.php b/core/modules/simpletest/tests/Drupal/simpletest/Tests/TestBaseTest.php new file mode 100644 index 0000000..27e3e7b --- /dev/null +++ b/core/modules/simpletest/tests/Drupal/simpletest/Tests/TestBaseTest.php @@ -0,0 +1,71 @@ + 'TestBase helper functions test', + 'description' => 'Test helper functions provided by the TestBase abstract class.', + 'group' => 'Simpletest', + + ); + } + + protected function setUp() { + $this->stub = $this->getMockForAbstractClass('Drupal\simpletest\TestBase'); + } + + /** + * Provides data for the random string validation test. + * + * @return array + * An array of values passed to the test method. + */ + public function randomStringValidateProvider () { + return array( + array(' curry paste', FALSE), + array('curry paste ', FALSE), + array('curry paste', FALSE), + array('curry paste', FALSE), + array('curry paste', TRUE), + array('thai green curry paste', TRUE), + ); + } + + /** + * Tests the random strings validation rules. + * + * @param string $str + * The string to validate. + * @param bool $expected + * The expected result of the validation. + * + * @see \Drupal\simpletest\TestBase::randomStringValidate(). + * + * @dataProvider randomStringValidateProvider + */ + public function testRandomStringValidate($str, $expected) { + $actual = $this->stub->randomStringValidate($str); + $this->assertEquals($expected, $actual); + } + +} diff --git a/core/tests/Drupal/Tests/Component/Utility/RandomTest.php b/core/tests/Drupal/Tests/Component/Utility/RandomTest.php index 894ecc2..dfff5bc 100644 --- a/core/tests/Drupal/Tests/Component/Utility/RandomTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/RandomTest.php @@ -18,6 +18,15 @@ */ class RandomTest extends UnitTestCase { + /** + * The first random string passed to the test callback. + * + * @see \Drupal\Tests\Component\Utility\RandomTest::_RandomStringValidate + * + * @var string + */ + protected $firstStringGenerated = ''; + public static function getInfo() { return array( 'name' => 'Random data generation tests', @@ -27,28 +36,30 @@ public static function getInfo() { } /** - * Tests unique random name generation. + * Tests unique random string generation. * - * @see \Drupal\Component\Utility\Random::name() + * @see \Drupal\Component\Utility\Random::string() */ public function testRandomStringUniqueness() { $strings = array(); + $random = new Random(); for ($i = 0; $i <= 50; $i++) { - $str = Random::string(1, TRUE); + $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. + * Tests unique random name generation. * - * @see \Drupal\Component\Utility\Random::string() + * @see \Drupal\Component\Utility\Random::name() */ public function testRandomNamesUniqueness() { $names = array(); + $random = new Random(); for ($i = 0; $i <= 10; $i++) { - $str = Random::name(1, TRUE); + $str = $random->name(1, TRUE); $this->assertFalse(isset($names[$str]), String::format('Generated duplicate random name !name', array('!name' => $str))); $names[$str] = TRUE; } @@ -64,8 +75,9 @@ public function testRandomNamesUniqueness() { public function testRandomNameException() { // There are fewer than 100 possibilities so an exception should occur to // prevent infinite loops. + $random = new Random(); for ($i = 0; $i <= 100; $i++) { - $str = Random::name(1, TRUE); + $str = $random->name(1, TRUE); $names[$str] = TRUE; } } @@ -80,8 +92,9 @@ public function testRandomNameException() { public function testRandomStringException() { // There are fewer than 100 possibilities so an exception should occur to // prevent infinite loops. + $random = new Random(); for ($i = 0; $i <= 100; $i++) { - $str = Random::string(1, TRUE); + $str = $random->string(1, TRUE); $names[$str] = TRUE; } } @@ -94,8 +107,9 @@ public function testRandomStringException() { public function testRandomNameNonUnique() { // There are fewer than 100 possibilities if we were forcing uniqueness so // exception would occur. + $random = new Random(); for ($i = 0; $i <= 100; $i++) { - Random::name(1); + $random->name(1); } $this->assertTrue(TRUE, 'No exception thrown when uniqueness is not enforced.'); } @@ -108,8 +122,9 @@ public function testRandomNameNonUnique() { public function testRandomStringNonUnique() { // There are fewer than 100 possibilities if we were forcing uniqueness so // exception would occur. + $random = new Random(); for ($i = 0; $i <= 100; $i++) { - Random::string(1); + $random->string(1); } $this->assertTrue(TRUE, 'No exception thrown when uniqueness is not enforced.'); } @@ -122,10 +137,42 @@ public function testRandomStringNonUnique() { public function testRandomObject() { // For values of 0 and 1 \Drupal\Component\Utility\Random::object() will // have different execution paths. + $random = new Random(); for ($i = 0; $i <= 1; $i++) { - $obj = Random::object($i); + $obj = $random->object($i); $this->assertEquals($i, count(get_object_vars($obj)), 'Generated random object has expected number of properties'); } } + /** + * Tests random string validation callbacks. + * + * @see \Drupal\Component\Utility\Random::name() + */ + public function testRandomStringValidator() { + $random = new Random(); + $this->firstStringGenerated = ''; + $str = $random->string(1, TRUE, array($this, '_RandomStringValidate')); + $this->assertNotEquals($this->firstStringGenerated, $str); + } + + /** + * Callback for random string validation. + * + * @see \Drupal\Component\Utility\Random::name() + * @see \Drupal\Tests\Component\Utility\RandomTest::testRandomStringValidator() + * + * @param string $str + * The random string to validate. + * + * @return bool + * TRUE if the random string is valid, FALSE if not. + */ + public function _RandomStringValidate($str) { + if (empty($this->firstStringGenerated)) { + $this->firstStringGenerated = $str; + return FALSE; + } + return TRUE; + } } diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php index 727976f..a0d5d81 100644 --- a/core/tests/Drupal/Tests/UnitTestCase.php +++ b/core/tests/Drupal/Tests/UnitTestCase.php @@ -15,6 +15,13 @@ abstract class UnitTestCase extends \PHPUnit_Framework_TestCase { /** + * The random generator. + * + * @var \Drupal\Component\Utility\Random + */ + protected $randomGenerator; + + /** * This method exists to support the simpletest UI runner. * * It should eventually be replaced with something native to phpunit. @@ -43,13 +50,27 @@ public static function getInfo() { * @return string * Randomly generated unique string. * - * @see \Drupal\Component\Utility::string() + * @see \Drupal\Component\Utility\Random::name() */ public function randomName($length = 8) { - return Random::name($length, TRUE); + return $this->getRandomGenerator()->name($length, TRUE); } /** + * Gets the random generator for the utility methods. + * + * @return \Drupal\Component\Utility\Random + * The random generator + */ + protected function getRandomGenerator() { + if (!is_object($this->randomGenerator)) { + $this->randomGenerator = new Random(); + } + return $this->randomGenerator; + } + + + /** * Returns a stub config factory that behaves according to the passed in array. * * Use this to generate a config factory that will return the desired values