Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023

This commit is contained in:
Pantheon Automation 2015-09-04 13:20:09 -07:00 committed by Greg Anderson
parent 2720a9ec4b
commit f3791f1da3
1898 changed files with 54300 additions and 11481 deletions

View file

@ -0,0 +1,21 @@
id: d7_simpletest_settings
label: Drupal 7 SimpleTest configuration
migration_tags:
- Drupal 7
source:
plugin: variable
variables:
- simpletest_clear_results
- simpletest_httpauth_method
- simpletest_httpauth_password
- simpletest_httpauth_username
- simpletest_verbose
process:
clear_results: simpletest_clear_results
'httpauth/method': simpletest_httpauth_method
'httpauth/password': simpletest_httpauth_password
'httpauth/username': simpletest_httpauth_username
verbose: simpletest_verbose
destination:
plugin: config
config_name: simpletest.settings

View file

@ -11,6 +11,9 @@
* Collapses table rows followed by group rows on the test listing page.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attach collapse behavior on the test listing page.
*/
Drupal.behaviors.simpleTestGroupCollapse = {
attach: function (context) {
@ -34,6 +37,9 @@
* Toggles test checkboxes to match the group checkbox.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches behavior for selecting all tests in a group.
*/
Drupal.behaviors.simpleTestSelectAll = {
attach: function (context) {
@ -75,6 +81,9 @@
* Source text: .table-filter-text-source
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the filter behavior the the text input element.
*/
Drupal.behaviors.simpletestTableFilterByText = {
attach: function (context) {
@ -95,8 +104,8 @@
// Filter if the length of the query is at least 3 characters.
if (query.length >= 3) {
// Indicate that a search has been performed, and hide the "select all"
// checkbox.
// Indicate that a search has been performed, and hide the
// "select all" checkbox.
searched = true;
$('#simpletest-form-table thead th.select-all input').hide();

View file

@ -287,15 +287,21 @@ function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpun
* The command that can be run through exec().
*/
function simpletest_phpunit_command() {
// Load the actual autoloader being used and determine its filename using
// reflection. We can determine the vendor directory based on that filename.
$autoloader = require \Drupal::root() . '/autoload.php';
$reflector = new ReflectionClass($autoloader);
$vendor_dir = dirname(dirname($reflector->getFileName()));
// Don't use the committed version in composer's bin dir if running on
// windows.
if (substr(PHP_OS, 0, 3) == 'WIN') {
$php_executable_finder = new PhpExecutableFinder();
$php = $php_executable_finder->find();
$phpunit_bin = escapeshellarg($php) . " -f " . escapeshellarg(\Drupal::root() . "/core/vendor/phpunit/phpunit/composer/bin/phpunit") . " --";
$phpunit_bin = escapeshellarg($php) . ' -f ' . escapeshellarg($vendor_dir . '/phpunit/phpunit/composer/bin/phpunit') . ' --';
}
else {
$phpunit_bin = \Drupal::root() . "/core/vendor/bin/phpunit";
$phpunit_bin = $vendor_dir . '/phpunit/phpunit/phpunit';
}
return $phpunit_bin;
}

View file

@ -8,6 +8,7 @@
namespace Drupal\simpletest;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Render\RenderContext;
@ -66,7 +67,7 @@ trait AssertContentTrait {
$this->plainTextContent = NULL;
$this->elements = NULL;
$this->drupalSettings = array();
if (preg_match('/var drupalSettings = (.*?);$/m', $content, $matches)) {
if (preg_match('@<script type="application/json" data-drupal-selector="drupal-settings-json">([^<]*)</script>@', $content, $matches)) {
$this->drupalSettings = Json::decode($matches[1]);
}
}
@ -76,7 +77,10 @@ trait AssertContentTrait {
*/
protected function getTextContent() {
if (!isset($this->plainTextContent)) {
$this->plainTextContent = Xss::filter($this->getRawContent(), array());
$raw_content = $this->getRawContent();
// Strip everything between the HEAD tags.
$raw_content = preg_replace('@<head>(.+?)</head>@si', '', $raw_content);
$this->plainTextContent = Xss::filter($raw_content, array());
}
return $this->plainTextContent;
}
@ -283,7 +287,7 @@ trait AssertContentTrait {
* Link position counting from zero.
* @param string $message
* (optional) A message to display with the assertion. Do not translate
* messages: use format_string() to embed variables in the message text, not
* messages: use strtr() to embed variables in the message text, not
* t(). If left blank, a default message will be displayed.
* @param string $group
* (optional) The group this message is in, which is displayed in a column
@ -296,7 +300,7 @@ trait AssertContentTrait {
*/
protected function assertLink($label, $index = 0, $message = '', $group = 'Other') {
$links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
$message = ($message ? $message : SafeMarkup::format('Link with label %label found.', array('%label' => $label)));
$message = ($message ? $message : strtr('Link with label %label found.', array('%label' => $label)));
return $this->assert(isset($links[$index]), $message, $group);
}
@ -374,6 +378,30 @@ trait AssertContentTrait {
return $this->assert(empty($links), $message, $group);
}
/**
* Passes if a link containing a given href is not found in the main region.
*
* @param string $href
* The full or partial value of the 'href' attribute of the anchor tag.
* @param string $message
* (optional) A message to display with the assertion. Do not translate
* messages: use format_string() to embed variables in the message text, not
* t(). If left blank, a default message will be displayed.
* @param string $group
* (optional) The group this message is in, which is displayed in a column
* in test output. Use 'Debug' to indicate this is debugging output. Do not
* translate this string. Defaults to 'Other'; most tests do not override
* this default.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertNoLinkByHrefInMainRegion($href, $message = '', $group = 'Other') {
$links = $this->xpath('//main//a[contains(@href, :href)]', array(':href' => $href));
$message = ($message ? $message : SafeMarkup::format('No link containing href %href found.', array('%href' => $href)));
return $this->assert(empty($links), $message, $group);
}
/**
* Passes if the raw text IS found on the loaded page, fail otherwise.
*
@ -452,7 +480,7 @@ trait AssertContentTrait {
if (!$message) {
$message = SafeMarkup::format('Escaped "@raw" found', array('@raw' => $raw));
}
return $this->assert(strpos($this->getRawContent(), SafeMarkup::checkPlain($raw)) !== FALSE, $message, $group);
return $this->assert(strpos($this->getRawContent(), Html::escape($raw)) !== FALSE, $message, $group);
}
/**
@ -480,7 +508,7 @@ trait AssertContentTrait {
if (!$message) {
$message = SafeMarkup::format('Escaped "@raw" not found', array('@raw' => $raw));
}
return $this->assert(strpos($this->getRawContent(), SafeMarkup::checkPlain($raw)) === FALSE, $message, $group);
return $this->assert(strpos($this->getRawContent(), Html::escape($raw)) === FALSE, $message, $group);
}
/**
@ -747,14 +775,19 @@ trait AssertContentTrait {
* TRUE on pass, FALSE on fail.
*/
protected function assertTitle($title, $message = '', $group = 'Other') {
$actual = (string) current($this->xpath('//title'));
if (!$message) {
$message = SafeMarkup::format('Page title @actual is equal to @expected.', array(
'@actual' => var_export($actual, TRUE),
'@expected' => var_export($title, TRUE),
));
// Don't use xpath as it messes with HTML escaping.
preg_match('@<title>(.*)</title>@', $this->getRawContent(), $matches);
if (isset($matches[1])) {
$actual = $matches[1];
if (!$message) {
$message = SafeMarkup::format('Page title @actual is equal to @expected.', array(
'@actual' => var_export($actual, TRUE),
'@expected' => var_export($title, TRUE),
));
}
return $this->assertEqual($actual, $title, $message, $group);
}
return $this->assertEqual($actual, $title, $message, $group);
return $this->fail('No title element found on the page.');
}
/**
@ -812,12 +845,15 @@ trait AssertContentTrait {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$output = $renderer->executeInRenderContext(new RenderContext(), function() use ($callback, $variables) {
// The string cast is necessary because theme functions return
// SafeStringInterface objects. This means we can assert that $expected
// matches the theme output without having to worry about 0 == ''.
$output = (string) $renderer->executeInRenderContext(new RenderContext(), function() use ($callback, $variables) {
return \Drupal::theme()->render($callback, $variables);
});
$this->verbose(
'<hr />' . 'Result:' . '<pre>' . SafeMarkup::checkPlain(var_export($output, TRUE)) . '</pre>'
. '<hr />' . 'Expected:' . '<pre>' . SafeMarkup::checkPlain(var_export($expected, TRUE)) . '</pre>'
'<hr />' . 'Result:' . '<pre>' . Html::escape(var_export($output, TRUE)) . '</pre>'
. '<hr />' . 'Expected:' . '<pre>' . Html::escape(var_export($expected, TRUE)) . '</pre>'
. '<hr />' . $output
);
if (!$message) {

View file

@ -12,8 +12,6 @@ use Behat\Mink\Element\Element;
use Behat\Mink\Exception\Exception;
use Behat\Mink\Mink;
use Behat\Mink\Session;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Random;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\ConnectionNotDefinedException;
@ -52,6 +50,7 @@ use Symfony\Component\HttpFoundation\Request;
*/
abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
use RandomGeneratorTrait;
use SessionTestTrait;
/**
@ -146,13 +145,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
*/
protected $configImporter;
/**
* The random data generator.
*
* @var \Drupal\Component\Utility\Random
*/
protected $randomGenerator;
/**
* The profile to install as a basis for testing.
*
@ -324,10 +316,9 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
$test_connection_info = Database::getConnectionInfo('default');
$test_prefix = $test_connection_info['default']['prefix']['default'];
if ($original_prefix != $test_prefix) {
$tables = Database::getConnection()->schema()->findTables($test_prefix . '%');
$prefix_length = strlen($test_prefix);
$tables = Database::getConnection()->schema()->findTables('%');
foreach ($tables as $table) {
if (Database::getConnection()->schema()->dropTable(substr($table, $prefix_length))) {
if (Database::getConnection()->schema()->dropTable($table)) {
unset($tables[$table]);
}
}
@ -452,7 +443,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
$path = substr($path, $length);
}
// Ensure that we have an absolute path.
if ($path[0] !== '/') {
if (empty($path) || $path[0] !== '/') {
$path = '/' . $path;
}
// Finally, prepend the $base_url.
@ -573,69 +564,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
return FALSE;
}
/**
* 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;
}
/**
* Generates a unique random string containing letters and numbers.
*
* Do not use this method when testing unvalidated user input. Instead, use
* \Drupal\simpletest\BrowserTestBase::randomString().
*
* @param int $length
* (optional) Length of random string to generate.
*
* @return string
* Randomly generated unique string.
*
* @see \Drupal\Component\Utility\Random::name()
*/
public function randomMachineName($length = 8) {
return $this->getRandomGenerator()->name($length, TRUE);
}
/**
* Generates a pseudo-random string of ASCII characters of codes 32 to 126.
*
* 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::randomMachineName(). If $length is greater
* than 2 the random string will include at least one ampersand ('&')
* character to ensure coverage for special characters and avoid the
* introduction of random test failures.
*
* @param int $length
* (optional) Length of random string to generate.
*
* @return string
* Pseudo-randomly generated unique string including special characters.
*
* @see \Drupal\Component\Utility\Random::string()
*/
public function randomString($length = 8) {
if ($length < 3) {
return $this->getRandomGenerator()->string($length, TRUE, array($this, 'randomStringValidate'));
}
// To prevent the introduction of random test failures, ensure that the
// returned string contains a character that needs to be escaped in HTML by
// injecting an ampersand into it.
$replacement_pos = floor($length / 2);
// Remove 1 from the length to account for the ampersand character.
$string = $this->getRandomGenerator()->string($length - 1, TRUE, array($this, 'randomStringValidate'));
return substr_replace($string, '&', $replacement_pos, 0);
}
/**
* Checks whether a given list of permission names is valid.
*
@ -843,7 +771,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
// Not using File API; a potential error must trigger a PHP warning.
$directory = DRUPAL_ROOT . '/' . $this->siteDirectory;
copy(DRUPAL_ROOT . '/sites/default/default.settings.php', $directory . '/settings.php');
copy(DRUPAL_ROOT . '/sites/default/default.services.yml', $directory . '/services.yml');
// All file system paths are created by System module during installation.
// @see system_requirements()

View file

@ -74,7 +74,7 @@ class SimpletestResultsForm extends FormBase {
);
$image_fail = array(
'#theme' => 'image',
'#uri' => 'core/misc/icons/ea2800/error.svg',
'#uri' => 'core/misc/icons/e32700/error.svg',
'#width' => 18,
'#height' => 18,
'#alt' => 'Fail',

View file

@ -8,7 +8,6 @@
namespace Drupal\simpletest\Form;
use Drupal\Component\Utility\SortArray;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
@ -179,7 +178,7 @@ class SimpletestTestForm extends FormBase {
);
$form['tests'][$class]['description'] = array(
'#prefix' => '<div class="description">',
'#markup' => SafeMarkup::checkPlain($info['description']),
'#plain_text' => $info['description'],
'#suffix' => '</div>',
'#wrapper_attributes' => array(
'class' => array('simpletest-test-description', 'table-filter-text-source'),

View file

@ -138,33 +138,33 @@ abstract class InstallerTestBase extends WebTestBase {
// Configure site.
$this->setUpSite();
// Import new settings.php written by the installer.
$request = Request::createFromGlobals();
$class_loader = require $this->container->get('app.root') . '/autoload.php';
Settings::initialize($this->container->get('app.root'), DrupalKernel::findSitePath($request), $class_loader);
foreach ($GLOBALS['config_directories'] as $type => $path) {
$this->configDirectories[$type] = $path;
if ($this->isInstalled) {
// Import new settings.php written by the installer.
$request = Request::createFromGlobals();
$class_loader = require $this->container->get('app.root') . '/autoload.php';
Settings::initialize($this->container->get('app.root'), DrupalKernel::findSitePath($request), $class_loader);
foreach ($GLOBALS['config_directories'] as $type => $path) {
$this->configDirectories[$type] = $path;
}
// After writing settings.php, the installer removes write permissions
// from the site directory. To allow drupal_generate_test_ua() to write
// a file containing the private key for drupal_valid_test_ua(), the site
// directory has to be writable.
// WebTestBase::tearDown() will delete the entire test site directory.
// Not using File API; a potential error must trigger a PHP warning.
chmod($this->container->get('app.root') . '/' . $this->siteDirectory, 0777);
$this->kernel = DrupalKernel::createFromRequest($request, $class_loader, 'prod', FALSE);
$this->kernel->prepareLegacyRequest($request);
$this->container = $this->kernel->getContainer();
// Manually configure the test mail collector implementation to prevent
// tests from sending out e-mails and collect them in state instead.
$this->container->get('config.factory')
->getEditable('system.mail')
->set('interface.default', 'test_mail_collector')
->save();
}
// After writing settings.php, the installer removes write permissions
// from the site directory. To allow drupal_generate_test_ua() to write
// a file containing the private key for drupal_valid_test_ua(), the site
// directory has to be writable.
// WebTestBase::tearDown() will delete the entire test site directory.
// Not using File API; a potential error must trigger a PHP warning.
chmod($this->container->get('app.root') . '/' . $this->siteDirectory, 0777);
$this->kernel = DrupalKernel::createFromRequest($request, $class_loader, 'prod', FALSE);
$this->kernel->prepareLegacyRequest($request);
$this->container = $this->kernel->getContainer();
$config = $this->container->get('config.factory');
// Manually configure the test mail collector implementation to prevent
// tests from sending out e-mails and collect them in state instead.
$config->getEditable('system.mail')
->set('interface.default', 'test_mail_collector')
->save();
$this->isInstalled = TRUE;
}
/**
@ -196,11 +196,14 @@ abstract class InstallerTestBase extends WebTestBase {
}
/**
* Installer step: Configure site.
* Final installer step: Configure site.
*/
protected function setUpSite() {
$edit = $this->translatePostValues($this->parameters['forms']['install_configure_form']);
$this->drupalPostForm(NULL, $edit, $this->translations['Save and continue']);
// If we've got to this point the site is installed using the regular
// installation workflow.
$this->isInstalled = TRUE;
}
/**

View file

@ -7,6 +7,7 @@
namespace Drupal\simpletest;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Variable;
use Drupal\Core\Database\Database;
@ -33,6 +34,9 @@ use Symfony\Component\HttpFoundation\Request;
* Additional modules needed in a test may be loaded and added to the fixed
* module list.
*
* @deprecated in Drupal 8.0.x, will be removed before Drupal 8.2.x. Use
* \Drupal\KernelTests\KernelTestBase instead.
*
* @see \Drupal\simpletest\KernelTestBase::$modules
* @see \Drupal\simpletest\KernelTestBase::enableModules()
*
@ -571,7 +575,7 @@ EOD;
$content = $this->container->get('renderer')->renderRoot($elements);
drupal_process_attached($elements);
$this->setRawContent($content);
$this->verbose('<pre style="white-space: pre-wrap">' . SafeMarkup::checkPlain($content));
$this->verbose('<pre style="white-space: pre-wrap">' . Html::escape($content));
return $content;
}

View file

@ -0,0 +1,131 @@
<?php
/**
* @file
* Contains \Drupal\simpletest\RandomGeneratorTrait.
*/
namespace Drupal\simpletest;
use Drupal\Component\Utility\Random;
/**
* Provides random generator utility methods.
*/
trait RandomGeneratorTrait {
/**
* The random generator.
*
* @var \Drupal\Component\Utility\Random
*/
protected $randomGenerator;
/**
* Generates a pseudo-random string of ASCII characters of codes 32 to 126.
*
* 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::randomMachineName(). If $length is greater
* than 3 the random string will include at least one ampersand ('&') and
* at least one greater than ('>') character to ensure coverage for special
* characters and avoid the introduction of random test failures.
*
* @param int $length
* Length of random string to generate.
*
* @return string
* Pseudo-randomly generated unique string including special characters.
*
* @see \Drupal\Component\Utility\Random::string()
*/
public function randomString($length = 8) {
if ($length < 4) {
return $this->getRandomGenerator()->string($length, TRUE, array($this, 'randomStringValidate'));
}
// To prevent the introduction of random test failures, ensure that the
// returned string contains a character that needs to be escaped in HTML by
// injecting an ampersand into it.
$replacement_pos = floor($length / 2);
// Remove 2 from the length to account for the ampersand and greater than
// characters.
$string = $this->getRandomGenerator()->string($length - 2, TRUE, array($this, 'randomStringValidate'));
return substr_replace($string, '>&', $replacement_pos, 0);
}
/**
* Callback for random string validation.
*
* @see \Drupal\Component\Utility\Random::string()
*
* @param string $string
* The random string to validate.
*
* @return bool
* TRUE if the random string is valid, FALSE if not.
*/
public function randomStringValidate($string) {
// Consecutive spaces causes issues for
// \Drupal\simpletest\WebTestBase::assertLink().
if (preg_match('/\s{2,}/', $string)) {
return FALSE;
}
// Starting or ending with a space means that length might not be what is
// expected.
if (preg_match('/^\s|\s$/', $string)) {
return FALSE;
}
return TRUE;
}
/**
* Generates a unique random string containing letters and numbers.
*
* Do not use this method when testing unvalidated user input. Instead, use
* \Drupal\simpletest\TestBase::randomString().
*
* @param int $length
* Length of random string to generate.
*
* @return string
* Randomly generated unique string.
*
* @see \Drupal\Component\Utility\Random::name()
*/
protected function randomMachineName($length = 8) {
return $this->getRandomGenerator()->name($length, TRUE);
}
/**
* 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.
*
* @see \Drupal\Component\Utility\Random::object()
*/
public function randomObject($size = 4) {
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;
}
}

View file

@ -28,6 +28,7 @@ use Drupal\Core\Utility\Error;
abstract class TestBase {
use SessionTestTrait;
use RandomGeneratorTrait;
/**
* The test run ID.
@ -283,13 +284,6 @@ abstract class TestBase {
*/
protected $configImporter;
/**
* The random generator.
*
* @var \Drupal\Component\Utility\Random
*/
protected $randomGenerator;
/**
* Set to TRUE to strict check all configuration saved.
*
@ -1286,10 +1280,9 @@ abstract class TestBase {
$test_connection_info = Database::getConnectionInfo('default');
$test_prefix = $test_connection_info['default']['prefix']['default'];
if ($original_prefix != $test_prefix) {
$tables = Database::getConnection()->schema()->findTables($test_prefix . '%');
$prefix_length = strlen($test_prefix);
$tables = Database::getConnection()->schema()->findTables('%');
foreach ($tables as $table) {
if (Database::getConnection()->schema()->dropTable(substr($table, $prefix_length))) {
if (Database::getConnection()->schema()->dropTable($table)) {
unset($tables[$table]);
}
}
@ -1391,12 +1384,10 @@ abstract class TestBase {
'line' => $exception->getLine(),
'file' => $exception->getFile(),
));
// \Drupal\Core\Utility\Error::decodeException() runs the exception
// message through \Drupal\Component\Utility\SafeMarkup::checkPlain().
$decoded_exception = Error::decodeException($exception);
unset($decoded_exception['backtrace']);
$message = SafeMarkup::format('%type: !message in %function (line %line of %file). <pre class="backtrace">!backtrace</pre>', $decoded_exception + array(
'!backtrace' => Error::formatBacktrace($verbose_backtrace),
$message = SafeMarkup::format('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $decoded_exception + array(
'@backtrace' => Error::formatBacktrace($verbose_backtrace),
));
$this->error($message, 'Uncaught exception', Error::getLastCaller($backtrace));
}
@ -1417,119 +1408,6 @@ abstract class TestBase {
new Settings($settings);
}
/**
* Generates a pseudo-random string of ASCII characters of codes 32 to 126.
*
* 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::randomMachineName(). If $length is greater
* than 3 the random string will include at least one ampersand ('&') and
* at least one greater than ('>') character to ensure coverage for special
* characters and avoid the introduction of random test failures.
*
* @param int $length
* Length of random string to generate.
*
* @return string
* Pseudo-randomly generated unique string including special characters.
*
* @see \Drupal\Component\Utility\Random::string()
*/
public function randomString($length = 8) {
if ($length < 4) {
return $this->getRandomGenerator()->string($length, TRUE, array($this, 'randomStringValidate'));
}
// To prevent the introduction of random test failures, ensure that the
// returned string contains a character that needs to be escaped in HTML by
// injecting an ampersand into it.
$replacement_pos = floor($length / 2);
// Remove 2 from the length to account for the ampersand and greater than
// characters.
$string = $this->getRandomGenerator()->string($length - 2, TRUE, array($this, 'randomStringValidate'));
return substr_replace($string, '>&', $replacement_pos, 0);
}
/**
* Callback for random string validation.
*
* @see \Drupal\Component\Utility\Random::string()
*
* @param string $string
* The random string to validate.
*
* @return bool
* TRUE if the random string is valid, FALSE if not.
*/
public function randomStringValidate($string) {
// Consecutive spaces causes issues for
// Drupal\simpletest\WebTestBase::assertLink().
if (preg_match('/\s{2,}/', $string)) {
return FALSE;
}
// Starting with a space means that length might not be what is expected.
// Starting with an @ sign causes CURL to fail if used in conjunction with a
// file upload. See https://www.drupal.org/node/2174997.
if (preg_match('/^(\s|@)/', $string)) {
return FALSE;
}
// Ending with a space means that length might not be what is expected.
if (preg_match('/\s$/', $string)) {
return FALSE;
}
return TRUE;
}
/**
* Generates a unique random string containing letters and numbers.
*
* Do not use this method when testing unvalidated user input. Instead, use
* \Drupal\simpletest\TestBase::randomString().
*
* @param int $length
* Length of random string to generate.
*
* @return string
* Randomly generated unique string.
*
* @see \Drupal\Component\Utility\Random::name()
*/
public function randomMachineName($length = 8) {
return $this->getRandomGenerator()->name($length, TRUE);
}
/**
* 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.
*
* @see \Drupal\Component\Utility\Random::object()
*/
public function randomObject($size = 4) {
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;
}
/**
* Converts a list of possible parameters into a stack of permutations.
*

View file

@ -81,6 +81,7 @@ class TestDiscovery {
// Add PHPUnit test namespaces of Drupal core.
$this->testNamespaces['Drupal\\Tests\\'] = [DRUPAL_ROOT . '/core/tests/Drupal/Tests'];
$this->testNamespaces['Drupal\\KernelTests\\'] = [DRUPAL_ROOT . '/core/tests/Drupal/KernelTests'];
$this->testNamespaces['Drupal\\FunctionalTests\\'] = [DRUPAL_ROOT . '/core/tests/Drupal/FunctionalTests'];
$this->availableExtensions = array();
@ -98,6 +99,7 @@ class TestDiscovery {
// Add PHPUnit test namespaces.
$this->testNamespaces["Drupal\\Tests\\$name\\Unit\\"][] = "$base_path/tests/src/Unit";
$this->testNamespaces["Drupal\\Tests\\$name\\Kernel\\"][] = "$base_path/tests/src/Kernel";
$this->testNamespaces["Drupal\\Tests\\$name\\Functional\\"][] = "$base_path/tests/src/Functional";
}

View file

@ -23,6 +23,22 @@ class BrowserTest extends WebTestBase {
*/
protected static $cookieSet = FALSE;
/**
* Modules to enable.
*
* @var string[]
*/
public static $modules = ['block'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Test \Drupal\simpletest\WebTestBase::getAbsoluteUrl().
*/

View file

@ -7,6 +7,7 @@
namespace Drupal\simpletest\Tests;
use Drupal\Core\Database\Database;
use Drupal\simpletest\KernelTestBase;
/**
@ -324,4 +325,39 @@ EOS;
$this->assertNull(drupal_get_profile());
}
/**
* {@inheritdoc}
*/
public function run(array $methods = array()) {
parent::run($methods);
// Check that all tables of the test instance have been deleted. At this
// point the original database connection is restored so we need to prefix
// the tables.
$connection = Database::getConnection();
if ($connection->databaseType() != 'sqlite') {
$tables = $connection->schema()->findTables($this->databasePrefix . '%');
$this->assertTrue(empty($tables), 'All test tables have been removed.');
}
else {
// We don't have the test instance connection anymore so we have to
// re-attach its database and then use the same query as
// \Drupal\Core\Database\Driver\sqlite\Schema::findTables().
// @see \Drupal\Core\Database\Driver\sqlite\Connection::__construct()
$info = Database::getConnectionInfo();
$connection->query('ATTACH DATABASE :database AS :prefix', [
':database' => $info['default']['database'] . '-' . $this->databasePrefix,
':prefix' => $this->databasePrefix
]);
$result = $connection->query("SELECT name FROM " . $this->databasePrefix . ".sqlite_master WHERE type = :type AND name LIKE :table_name AND name NOT LIKE :pattern", array(
':type' => 'table',
':table_name' => '%',
':pattern' => 'sqlite_%',
))->fetchAllKeyed(0, 0);
$this->assertTrue(empty($result), 'All test tables have been removed.');
}
}
}

View file

@ -13,7 +13,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to simpletest.settings.yml.
*
* @group simpletest
* @group migrate_drupal_6
*/
class MigrateSimpletestConfigsTest extends MigrateDrupal6TestBase {
@ -33,7 +33,6 @@ class MigrateSimpletestConfigsTest extends MigrateDrupal6TestBase {
parent::setUp();
$this->installConfig(['simpletest']);
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_simpletest_settings');
}

View file

@ -0,0 +1,42 @@
<?php
/**
* @file
* Contains \Drupal\simpletest\Tests\Migrate\d7\MigrateSimpletestSettingsTest.
*/
namespace Drupal\simpletest\Tests\Migrate\d7;
use Drupal\migrate_drupal\Tests\d7\MigrateDrupal7TestBase;
/**
* Tests migration of SimpleTest's variables to configuration.
*
* @group simpletest
*/
class MigrateSimpletestSettingsTest extends MigrateDrupal7TestBase {
public static $modules = ['simpletest'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(static::$modules);
$this->executeMigration('d7_simpletest_settings');
}
/**
* Tests migration of SimpleTest settings to configuration.
*/
public function testMigration() {
$config = \Drupal::config('simpletest.settings')->get();
$this->assertTrue($config['clear_results']);
$this->assertIdentical(CURLAUTH_BASIC, $config['httpauth']['method']);
$this->assertIdentical('testbot', $config['httpauth']['username']);
$this->assertIdentical('foobaz', $config['httpauth']['password']);
$this->assertTrue($config['verbose']);
}
}

View file

@ -164,7 +164,16 @@ EOD;
$site_path = $this->container->get('site.path');
file_put_contents($key_file, $private_key);
// This causes the first of the fifteen passes asserted in
// Check to see if runtime assertions are indeed on, if successful this
// will be the first of sixteen passes asserted in confirmStubResults()
try {
assert(FALSE, 'Lorem Ipsum');
$this->fail('Runtime assertions are not working.');
}
catch (\AssertionError $e) {
$this->assertEqual($e->getMessage(), 'Lorem Ipsum', 'Runtime assertions Enabled and running.');
}
// This causes the second of the sixteen passes asserted in
// confirmStubResults().
$this->pass($this->passMessage);
@ -175,7 +184,7 @@ EOD;
// confirmStubResults().
$this->fail($this->failMessage);
// This causes the second to fourth of the fifteen passes asserted in
// This causes the third to fifth of the sixteen passes asserted in
// confirmStubResults().
$user = $this->drupalCreateUser(array($this->validPermission), 'SimpleTestTest');
@ -183,15 +192,15 @@ EOD;
$this->drupalCreateUser(array($this->invalidPermission));
// Test logging in as a user.
// This causes the fifth to ninth of the fifteen passes asserted in
// This causes the sixth to tenth of the sixteen passes asserted in
// confirmStubResults().
$this->drupalLogin($user);
// This causes the tenth of the fifteen passes asserted in
// This causes the eleventh of the sixteen passes asserted in
// confirmStubResults().
$this->pass(t('Test ID is @id.', array('@id' => $this->testId)));
// These cause the eleventh to fourteenth of the fifteen passes asserted in
// These cause the twelfth to fifteenth of the sixteen passes asserted in
// confirmStubResults().
$this->assertTrue(file_exists($site_path . '/settings.testing.php'));
// Check the settings.testing.php file got included.
@ -206,7 +215,7 @@ EOD;
// Generates a warning inside a PHP function.
array_key_exists(NULL, NULL);
// This causes the fifteenth of the fifteen passes asserted in
// This causes the sixteenth of the sixteen passes asserted in
// confirmStubResults().
$this->assertNothing();
@ -250,7 +259,7 @@ EOD;
$this->assertAssertion("Debug: 'Foo'", 'Debug', 'Fail', 'SimpleTestTest.php', 'Drupal\simpletest\Tests\SimpleTestTest->stubTest()');
$this->assertEqual('15 passes, 3 fails, 2 exceptions, 3 debug messages', $this->childTestResults['summary']);
$this->assertEqual('16 passes, 3 fails, 2 exceptions, 3 debug messages', $this->childTestResults['summary']);
$this->testIds[] = $test_id = $this->getTestIdFromResults();
$this->assertTrue($test_id, 'Found test ID in results.');

View file

@ -70,6 +70,13 @@ abstract class WebTestBase extends TestBase {
*/
protected $curlHandle;
/**
* Whether or not to assert the presence of the X-Drupal-Ajax-Token.
*
* @var bool
*/
protected $assertAjaxHeader = TRUE;
/**
* The headers of the page currently loaded in the internal browser.
*
@ -166,6 +173,21 @@ abstract class WebTestBase extends TestBase {
*/
protected $redirectCount;
/**
* The number of meta refresh redirects to follow, or NULL if unlimited.
*
* @var null|int
*/
protected $maximumMetaRefreshCount = NULL;
/**
* The number of meta refresh redirects followed during ::drupalGet().
*
* @var int
*/
protected $metaRefreshCount = 0;
/**
* The kernel used in this test.
*
@ -711,7 +733,6 @@ abstract class WebTestBase extends TestBase {
// Not using File API; a potential error must trigger a PHP warning.
$directory = DRUPAL_ROOT . '/' . $this->siteDirectory;
copy(DRUPAL_ROOT . '/sites/default/default.settings.php', $directory . '/settings.php');
copy(DRUPAL_ROOT . '/sites/default/default.services.yml', $directory . '/services.yml');
// All file system paths are created by System module during installation.
// @see system_requirements()
@ -753,10 +774,12 @@ abstract class WebTestBase extends TestBase {
file_put_contents($directory . '/settings.php', "\n\$test_class = '" . get_class($this) ."';\n" . 'include DRUPAL_ROOT . \'/\' . $site_path . \'/settings.testing.php\';' ."\n", FILE_APPEND);
}
$settings_services_file = DRUPAL_ROOT . '/' . $this->originalSite . '/testing.services.yml';
if (file_exists($settings_services_file)) {
// Copy the testing-specific service overrides in place.
copy($settings_services_file, $directory . '/services.yml');
if (!file_exists($settings_services_file)) {
// Otherwise, use the default services as a starting point for overrides.
$settings_services_file = DRUPAL_ROOT . '/sites/default/default.services.yml';
}
// Copy the testing-specific service overrides in place.
copy($settings_services_file, $directory . '/services.yml');
if ($this->strictConfigSchema) {
// Add a listener to validate configuration schema on save.
$yaml = new \Symfony\Component\Yaml\Yaml();
@ -831,6 +854,15 @@ abstract class WebTestBase extends TestBase {
->set('css.preprocess', FALSE)
->set('js.preprocess', FALSE)
->save();
// Set an explicit time zone to not rely on the system one, which may vary
// from setup to setup. The Australia/Sydney time zone is chosen so all
// tests are run using an edge case scenario (UTC+10 and DST). This choice
// is made to prevent time zone related regressions and reduce the
// fragility of the testing system in general.
$config->getEditable('system.date')
->set('timezone.default', 'Australia/Sydney')
->save();
}
/**
@ -1206,6 +1238,9 @@ abstract class WebTestBase extends TestBase {
}
parent::tearDown();
// Ensure that the maximum meta refresh count is reset.
$this->maximumMetaRefreshCount = NULL;
// Ensure that internal logged in variable and cURL options are reset.
$this->loggedInUser = FALSE;
$this->additionalCurlOptions = array();
@ -1248,6 +1283,8 @@ abstract class WebTestBase extends TestBase {
CURLOPT_SSL_VERIFYHOST => FALSE,
CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'),
CURLOPT_USERAGENT => $this->databasePrefix,
// Disable support for the @ prefix for uploading files.
CURLOPT_SAFE_UPLOAD => TRUE,
);
if (isset($this->httpAuthCredentials)) {
$curl_options[CURLOPT_HTTPAUTH] = $this->httpAuthMethod;
@ -1493,6 +1530,8 @@ abstract class WebTestBase extends TestBase {
// Replace original page output with new output from redirected page(s).
if ($new = $this->checkForMetaRefresh()) {
$out = $new;
// We are finished with all meta refresh redirects, so reset the counter.
$this->metaRefreshCount = 0;
}
if ($path instanceof Url) {
@ -1502,7 +1541,7 @@ abstract class WebTestBase extends TestBase {
$verbose = 'GET request to: ' . $path .
'<hr />Ending URL: ' . $this->getUrl();
if ($this->dumpHeaders) {
$verbose .= '<hr />Headers: <pre>' . SafeMarkup::checkPlain(var_export(array_map('trim', $this->headers), TRUE)) . '</pre>';
$verbose .= '<hr />Headers: <pre>' . Html::escape(var_export(array_map('trim', $this->headers), TRUE)) . '</pre>';
}
$verbose .= '<hr />' . $out;
@ -1609,9 +1648,6 @@ abstract class WebTestBase extends TestBase {
* $edit = array();
* $edit['name[]'] = array('value1', 'value2');
* @endcode
*
* Note that when a form contains file upload fields, other
* fields cannot start with the '@' character.
* @param $submit
* Value of the submit button whose click is to be emulated. For example,
* t('Save'). The processing of the request depends on this value. For
@ -1729,7 +1765,7 @@ abstract class WebTestBase extends TestBase {
$verbose = 'POST request to: ' . $path;
$verbose .= '<hr />Ending URL: ' . $this->getUrl();
if ($this->dumpHeaders) {
$verbose .= '<hr />Headers: <pre>' . SafeMarkup::checkPlain(var_export(array_map('trim', $this->headers), TRUE)) . '</pre>';
$verbose .= '<hr />Headers: <pre>' . Html::escape(var_export(array_map('trim', $this->headers), TRUE)) . '</pre>';
}
$verbose .= '<hr />Fields: ' . highlight_string('<?php ' . var_export($post_array, TRUE), TRUE);
$verbose .= '<hr />' . $out;
@ -1867,6 +1903,9 @@ abstract class WebTestBase extends TestBase {
// Submit the POST request.
$return = Json::decode($this->drupalPostForm(NULL, $edit, array('path' => $ajax_path, 'triggering_element' => $triggering_element), $options, $headers, $form_html_id, $extra_post));
if ($this->assertAjaxHeader) {
$this->assertIdentical($this->drupalGetHeader('X-Drupal-Ajax-Token'), '1', 'Ajax response header found.');
}
// Change the page content by applying the returned commands.
if (!empty($ajax_settings) && !empty($return)) {
@ -2154,12 +2193,13 @@ abstract class WebTestBase extends TestBase {
* Either the new page content or FALSE.
*/
protected function checkForMetaRefresh() {
if (strpos($this->getRawContent(), '<meta ') && $this->parse()) {
if (strpos($this->getRawContent(), '<meta ') && $this->parse() && (!isset($this->maximumMetaRefreshCount) || $this->metaRefreshCount < $this->maximumMetaRefreshCount)) {
$refresh = $this->xpath('//meta[@http-equiv="Refresh"]');
if (!empty($refresh)) {
// Parse the content attribute of the meta tag for the format:
// "[delay]: URL=[page_to_redirect_to]".
if (preg_match('/\d+;\s*URL=(?<url>.*)/i', $refresh[0]['content'], $match)) {
$this->metaRefreshCount++;
return $this->drupalGet($this->getAbsoluteUrl(Html::decodeEntities($match['url'])));
}
}
@ -2191,7 +2231,7 @@ abstract class WebTestBase extends TestBase {
if ($this->dumpHeaders) {
$this->verbose('GET request to: ' . $path .
'<hr />Ending URL: ' . $this->getUrl() .
'<hr />Headers: <pre>' . SafeMarkup::checkPlain(var_export(array_map('trim', $this->headers), TRUE)) . '</pre>');
'<hr />Headers: <pre>' . Html::escape(var_export(array_map('trim', $this->headers), TRUE)) . '</pre>');
}
return $out;
@ -2465,7 +2505,7 @@ abstract class WebTestBase extends TestBase {
$path = substr($path, $length);
}
// Ensure that we have an absolute path.
if ($path[0] !== '/') {
if (empty($path) || $path[0] !== '/') {
$path = '/' . $path;
}
// Finally, prepend the $base_url.

View file

@ -0,0 +1,54 @@
<?php
/**
* @file
* Contains \Drupal\Tests\simpletest\Unit\AssertContentTraitTest.
*/
namespace Drupal\Tests\simpletest\Unit;
use Drupal\simpletest\AssertContentTrait;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\simpletest\AssertContentTrait
* @group simpletest
*/
class AssertContentTraitTest extends UnitTestCase {
/**
* @covers ::getTextContent
*/
public function testGetTextContent() {
$test = new TestClass();
$raw_content = <<<EOT
<Head>
<style>
@import url("foo.css");
</style>
</head>
<body>
bar
</body>
EOT;
$test->_setRawContent($raw_content);
$this->assertNotContains('foo', $test->_getTextContent());
$this->assertNotContains('<body>', $test->_getTextContent());
$this->assertContains('bar', $test->_getTextContent());
}
}
class TestClass {
use AssertContentTrait;
public function _setRawContent($content) {
$this->setRawContent($content);
}
public function _getTextContent() {
return $this->getTextContent();
}
}

View file

@ -73,7 +73,7 @@ class TestBaseTest extends UnitTestCase {
array(FALSE, 'curry paste'),
array(TRUE, 'curry paste'),
array(TRUE, 'thai green curry paste'),
array(FALSE, '@startswithat'),
array(TRUE, '@startswithat'),
array(TRUE, 'contains@at'),
);
}
@ -117,7 +117,7 @@ class TestBaseTest extends UnitTestCase {
$this->assertEquals($length, strlen($string));
// randomString() should always include an ampersand ('&') and a
// greater than ('>') if $length is greater than 3.
if ($length > 3) {
if ($length > 4) {
$this->assertContains('&', $string);
$this->assertContains('>', $string);
}

View file

@ -218,6 +218,7 @@ class WebTestBaseTest extends UnitTestCase {
$get_absolute_url_method->setAccessible(TRUE);
$this->assertSame($expected_absolute_path, $get_absolute_url_method->invoke($web_test, $href));
unset($GLOBALS['base_url'], $GLOBALS['base_path']);
}
/**