diff --git a/core/lib/Drupal/Component/Utility/Random.php b/core/lib/Drupal/Component/Utility/Random.php index df989c0..8bff977 100644 --- a/core/lib/Drupal/Component/Utility/Random.php +++ b/core/lib/Drupal/Component/Utility/Random.php @@ -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 static 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,7 +72,17 @@ 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(static::$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; diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 3eb5c84..d0d7c65 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -1169,7 +1169,38 @@ protected function settingsSet($name, $value) { * @see \Drupal\Component\Utility\Random::string() */ public function randomString($length = 8) { - return Random::string($length, TRUE); + return Random::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; } /** 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..5b4053e 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; + } }