summaryrefslogtreecommitdiff
path: root/modules/simpletest
diff options
context:
space:
mode:
Diffstat (limited to 'modules/simpletest')
-rw-r--r--modules/simpletest/drupal_web_test_case.php115
-rw-r--r--modules/simpletest/simpletest.module13
-rw-r--r--modules/simpletest/simpletest.pages.inc2
-rw-r--r--modules/simpletest/simpletest.test72
-rw-r--r--modules/simpletest/tests/ajax.test58
-rw-r--r--modules/simpletest/tests/ajax_forms_test.module41
-rw-r--r--modules/simpletest/tests/bootstrap.test12
-rw-r--r--modules/simpletest/tests/common.test56
-rw-r--r--modules/simpletest/tests/common_test.module28
-rw-r--r--modules/simpletest/tests/database_test.test155
-rw-r--r--modules/simpletest/tests/entity_cache_test_dependency.module2
-rw-r--r--modules/simpletest/tests/entity_query.test10
-rw-r--r--modules/simpletest/tests/file.test40
-rw-r--r--modules/simpletest/tests/file_test.module2
-rw-r--r--modules/simpletest/tests/form.test8
-rw-r--r--modules/simpletest/tests/form_test.module2
-rw-r--r--modules/simpletest/tests/mail.test24
-rw-r--r--modules/simpletest/tests/path.test8
-rw-r--r--modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info7
-rw-r--r--modules/simpletest/tests/system_incompatible_core_version_dependencies_test.module1
-rw-r--r--modules/simpletest/tests/system_incompatible_core_version_test.info6
-rw-r--r--modules/simpletest/tests/system_incompatible_core_version_test.module1
-rw-r--r--modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info8
-rw-r--r--modules/simpletest/tests/system_incompatible_module_version_dependencies_test.module1
-rw-r--r--modules/simpletest/tests/system_incompatible_module_version_test.info6
-rw-r--r--modules/simpletest/tests/system_incompatible_module_version_test.module1
-rw-r--r--modules/simpletest/tests/system_test.module8
-rw-r--r--modules/simpletest/tests/theme.test55
-rw-r--r--modules/simpletest/tests/theme_test.module22
-rw-r--r--modules/simpletest/tests/theme_test.template_test.tpl.php2
-rw-r--r--modules/simpletest/tests/themes/test_theme/template.php21
-rw-r--r--modules/simpletest/tests/themes/test_theme/test_theme.info16
-rw-r--r--modules/simpletest/tests/unicode.test2
-rw-r--r--modules/simpletest/tests/update_script_test.info6
-rw-r--r--modules/simpletest/tests/update_script_test.install45
-rw-r--r--modules/simpletest/tests/update_script_test.module18
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.node_type_broken.database.php34
-rw-r--r--modules/simpletest/tests/upgrade/drupal-7.bare.minimal.database.php.gzbin0 -> 39843 bytes
-rw-r--r--modules/simpletest/tests/upgrade/drupal-7.bare.standard_all.database.php.gzbin0 -> 77424 bytes
-rw-r--r--modules/simpletest/tests/upgrade/drupal-7.filled.minimal.database.php.gzbin0 -> 41805 bytes
-rw-r--r--modules/simpletest/tests/upgrade/drupal-7.filled.standard_all.database.php.gzbin0 -> 97562 bytes
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.node.test32
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.test417
-rw-r--r--modules/simpletest/tests/url_alter_test.module4
44 files changed, 1254 insertions, 107 deletions
diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php
index 2b23a3bb4..9095e22e1 100644
--- a/modules/simpletest/drupal_web_test_case.php
+++ b/modules/simpletest/drupal_web_test_case.php
@@ -74,6 +74,18 @@ abstract class DrupalTestCase {
protected $skipClasses = array(__CLASS__ => TRUE);
/**
+ * Flag to indicate whether the test has been set up.
+ *
+ * The setUp() method isolates the test from the parent Drupal site by
+ * creating a random prefix for the database and setting up a clean file
+ * storage directory. The tearDown() method then cleans up this test
+ * environment. We must ensure that setUp() has been run. Otherwise,
+ * tearDown() will act on the parent Drupal site rather than the test
+ * environment, destroying live data.
+ */
+ protected $setup = FALSE;
+
+ /**
* Constructor for DrupalTestCase.
*
* @param $test_id
@@ -127,7 +139,15 @@ abstract class DrupalTestCase {
);
// Store assertion for display after the test has completed.
- Database::getConnection('default', 'simpletest_original_default')
+ try {
+ $connection = Database::getConnection('default', 'simpletest_original_default');
+ }
+ catch (DatabaseConnectionNotDefinedException $e) {
+ // If the test was not set up, the simpletest_original_default
+ // connection does not exist.
+ $connection = Database::getConnection('default', 'default');
+ }
+ $connection
->insert('simpletest')
->fields($assertion)
->execute();
@@ -474,14 +494,19 @@ abstract class DrupalTestCase {
);
$completion_check_id = DrupalTestCase::insertAssert($this->testId, $class, FALSE, t('The test did not complete due to a fatal error.'), 'Completion check', $caller);
$this->setUp();
- try {
- $this->$method();
- // Finish up.
+ if ($this->setup) {
+ try {
+ $this->$method();
+ // Finish up.
+ }
+ catch (Exception $e) {
+ $this->exceptionHandler($e);
+ }
+ $this->tearDown();
}
- catch (Exception $e) {
- $this->exceptionHandler($e);
+ else {
+ $this->fail(t("The test cannot be executed because it has not been set up properly."));
}
- $this->tearDown();
// Remove the completion check record.
DrupalTestCase::deleteAssert($completion_check_id);
}
@@ -691,6 +716,7 @@ class DrupalUnitTestCase extends DrupalTestCase {
unset($module_list['locale']);
module_list(TRUE, FALSE, FALSE, $module_list);
}
+ $this->setup = TRUE;
}
protected function tearDown() {
@@ -1045,28 +1071,35 @@ class DrupalWebTestCase extends DrupalTestCase {
}
/**
- * Create a user with a given set of permissions. The permissions correspond to the
- * names given on the privileges page.
+ * Create a user with a given set of permissions.
*
- * @param $permissions
- * Array of permission names to assign to user.
- * @return
+ * @param array $permissions
+ * Array of permission names to assign to user. Note that the user always
+ * has the default permissions derived from the "authenticated users" role.
+ *
+ * @return object|false
* A fully loaded user object with pass_raw property, or FALSE if account
* creation fails.
*/
- protected function drupalCreateUser($permissions = array('access comments', 'access content', 'post comments', 'skip comment approval')) {
- // Create a role with the given permission set.
- if (!($rid = $this->drupalCreateRole($permissions))) {
- return FALSE;
+ protected function drupalCreateUser(array $permissions = array()) {
+ // Create a role with the given permission set, if any.
+ $rid = FALSE;
+ if ($permissions) {
+ $rid = $this->drupalCreateRole($permissions);
+ if (!$rid) {
+ return FALSE;
+ }
}
// Create a user assigned to that role.
$edit = array();
$edit['name'] = $this->randomName();
$edit['mail'] = $edit['name'] . '@example.com';
- $edit['roles'] = array($rid => $rid);
$edit['pass'] = user_password();
$edit['status'] = 1;
+ if ($rid) {
+ $edit['roles'] = array($rid => $rid);
+ }
$account = user_save(drupal_anonymous_user(), $edit);
@@ -1294,6 +1327,13 @@ class DrupalWebTestCase extends DrupalTestCase {
$test_info['test_run_id'] = $this->databasePrefix;
$test_info['in_child_site'] = FALSE;
+ // Preset the 'install_profile' system variable, so the first call into
+ // system_rebuild_module_data() (in drupal_install_system()) will register
+ // the test's profile as a module. Without this, the installation profile of
+ // the parent site (executing the test) is registered, and the test
+ // profile's hook_install() and other hook implementations are never invoked.
+ $conf['install_profile'] = $this->profile;
+
include_once DRUPAL_ROOT . '/includes/install.inc';
drupal_install_system();
@@ -1357,6 +1397,7 @@ class DrupalWebTestCase extends DrupalTestCase {
variable_set('mail_system', array('default-system' => 'TestingMailSystem'));
drupal_set_time_limit($this->timeLimit);
+ $this->setup = TRUE;
}
/**
@@ -1631,7 +1672,16 @@ class DrupalWebTestCase extends DrupalTestCase {
* An header.
*/
protected function curlHeaderCallback($curlHandler, $header) {
- $this->headers[] = $header;
+ // Header fields can be extended over multiple lines by preceding each
+ // extra line with at least one SP or HT. They should be joined on receive.
+ // Details are in RFC2616 section 4.
+ if ($header[0] == ' ' || $header[0] == "\t") {
+ // Normalize whitespace between chucks.
+ $this->headers[] = array_pop($this->headers) . ' ' . trim($header);
+ }
+ else {
+ $this->headers[] = $header;
+ }
// Errors are being sent via X-Drupal-Assertion-* headers,
// generated by _drupal_log_error() in the exact form required
@@ -1968,6 +2018,16 @@ class DrupalWebTestCase extends DrupalTestCase {
$id = (string) $element['id'];
$extra_post .= '&' . urlencode('ajax_html_ids[]') . '=' . urlencode($id);
}
+ if (isset($drupal_settings['ajaxPageState'])) {
+ $extra_post .= '&' . urlencode('ajax_page_state[theme]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme']);
+ $extra_post .= '&' . urlencode('ajax_page_state[theme_token]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme_token']);
+ foreach ($drupal_settings['ajaxPageState']['css'] as $key => $value) {
+ $extra_post .= '&' . urlencode("ajax_page_state[css][$key]") . '=1';
+ }
+ foreach ($drupal_settings['ajaxPageState']['js'] as $key => $value) {
+ $extra_post .= '&' . urlencode("ajax_page_state[js][$key]") . '=1';
+ }
+ }
// Unless a particular path is specified, use the one specified by the
// Ajax settings, or else 'system/ajax'.
@@ -1992,7 +2052,7 @@ class DrupalWebTestCase extends DrupalTestCase {
foreach ($return as $command) {
switch ($command['command']) {
case 'settings':
- $drupal_settings = array_merge_recursive($drupal_settings, $command['settings']);
+ $drupal_settings = drupal_array_merge_deep($drupal_settings, $command['settings']);
break;
case 'insert':
@@ -3059,8 +3119,21 @@ class DrupalWebTestCase extends DrupalTestCase {
* @return
* TRUE on pass, FALSE on fail.
*/
- protected function assertFieldByName($name, $value = '', $message = '') {
- return $this->assertFieldByXPath($this->constructFieldXpath('name', $name), $value, $message ? $message : t('Found field by name @name', array('@name' => $name)), t('Browser'));
+ protected function assertFieldByName($name, $value = NULL, $message = NULL) {
+ if (!isset($message)) {
+ if (!isset($value)) {
+ $message = t('Found field with name @name', array(
+ '@name' => var_export($name, TRUE),
+ ));
+ }
+ else {
+ $message = t('Found field with name @name and value @value', array(
+ '@name' => var_export($name, TRUE),
+ '@value' => var_export($value, TRUE),
+ ));
+ }
+ }
+ return $this->assertFieldByXPath($this->constructFieldXpath('name', $name), $value, $message, t('Browser'));
}
/**
diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module
index b820f5307..980bc1463 100644
--- a/modules/simpletest/simpletest.module
+++ b/modules/simpletest/simpletest.module
@@ -504,3 +504,16 @@ function simpletest_clean_results_table($test_id = NULL) {
}
return 0;
}
+
+/**
+ * Implements hook_mail_alter().
+ *
+ * Aborts sending of messages with ID 'simpletest_cancel_test'.
+ *
+ * @see MailTestCase::testCancelMessage()
+ */
+function simpletest_mail_alter(&$message) {
+ if ($message['id'] == 'simpletest_cancel_test') {
+ $message['send'] = FALSE;
+ }
+}
diff --git a/modules/simpletest/simpletest.pages.inc b/modules/simpletest/simpletest.pages.inc
index 696d14718..d1a7e4ade 100644
--- a/modules/simpletest/simpletest.pages.inc
+++ b/modules/simpletest/simpletest.pages.inc
@@ -254,7 +254,7 @@ function simpletest_result_form($form, &$form_state, $test_id) {
$row = array();
$row[] = $assertion->message;
$row[] = $assertion->message_group;
- $row[] = basename($assertion->file);
+ $row[] = drupal_basename($assertion->file);
$row[] = $assertion->line;
$row[] = $assertion->function;
$row[] = simpletest_result_status_image($assertion->status);
diff --git a/modules/simpletest/simpletest.test b/modules/simpletest/simpletest.test
index e5b6042ac..dbe2ec0f3 100644
--- a/modules/simpletest/simpletest.test
+++ b/modules/simpletest/simpletest.test
@@ -20,10 +20,7 @@ class SimpleTestFunctionalTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'SimpleTest functionality',
- 'description' => 'Test SimpleTest\'s web interface: check that the intended tests were
- run and ensure that test reports display the intended results. Also
- test SimpleTest\'s internal browser and API\'s both explicitly and
- implicitly.',
+ 'description' => "Test SimpleTest's web interface: check that the intended tests were run and ensure that test reports display the intended results. Also test SimpleTest's internal browser and API's both explicitly and implicitly.",
'group' => 'SimpleTest'
);
}
@@ -503,3 +500,70 @@ class SimpleTestMissingDependentModuleUnitTest extends DrupalUnitTestCase {
$this->fail(t('Running test with missing required module.'));
}
}
+
+/**
+ * Tests a test case that does not run parent::setUp() in its setUp() method.
+ *
+ * If a test case does not call parent::setUp(), running
+ * DrupalTestCase::tearDown() would destroy the main site's database tables.
+ * Therefore, we ensure that tests which are not set up properly are skipped.
+ *
+ * @see DrupalTestCase
+ */
+class SimpleTestBrokenSetUp extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Broken SimpleTest method',
+ 'description' => 'Tests a test case that does not call parent::setUp().',
+ 'group' => 'SimpleTest'
+ );
+ }
+
+ function setUp() {
+ // If the test is being run from the main site, set up normally.
+ if (!drupal_valid_test_ua()) {
+ parent::setUp('simpletest');
+ // Create and log in user.
+ $admin_user = $this->drupalCreateUser(array('administer unit tests'));
+ $this->drupalLogin($admin_user);
+ }
+ // If the test is being run from within simpletest, set up the broken test.
+ else {
+ $this->pass(t('The test setUp() method has been run.'));
+ // Don't call parent::setUp(). This should trigger an error message.
+ }
+ }
+
+ function tearDown() {
+ // If the test is being run from the main site, tear down normally.
+ if (!drupal_valid_test_ua()) {
+ parent::tearDown();
+ }
+ else {
+ // If the test is being run from within simpletest, output a message.
+ $this->pass(t('The tearDown() method has run.'));
+ }
+ }
+
+ /**
+ * Runs this test case from within the simpletest child site.
+ */
+ function testBreakSetUp() {
+ // If the test is being run from the main site, run it again from the web
+ // interface within the simpletest child site.
+ if (!drupal_valid_test_ua()) {
+ $edit['SimpleTestBrokenSetUp'] = TRUE;
+ $this->drupalPost('admin/config/development/testing', $edit, t('Run tests'));
+
+ // Verify that the broken test and its tearDown() method are skipped.
+ $this->assertRaw(t('The test setUp() method has been run.'));
+ $this->assertRaw(t('The test cannot be executed because it has not been set up properly.'));
+ $this->assertNoRaw(t('The test method has run.'));
+ $this->assertNoRaw(t('The tearDown() method has run.'));
+ }
+ // If the test is being run from within simpletest, output a message.
+ else {
+ $this->pass(t('The test method has run.'));
+ }
+ }
+}
diff --git a/modules/simpletest/tests/ajax.test b/modules/simpletest/tests/ajax.test
index 957979249..9a76b9692 100644
--- a/modules/simpletest/tests/ajax.test
+++ b/modules/simpletest/tests/ajax.test
@@ -116,6 +116,64 @@ class AJAXFrameworkTestCase extends AJAXTestCase {
);
$this->assertCommand($commands, $expected, t('Custom error message is output.'));
}
+
+ /**
+ * Test that new JavaScript and CSS files added during an AJAX request are returned.
+ */
+ function testLazyLoad() {
+ $expected = array(
+ 'setting_name' => 'ajax_forms_test_lazy_load_form_submit',
+ 'setting_value' => 'executed',
+ 'css' => drupal_get_path('module', 'system') . '/system.admin.css',
+ 'js' => drupal_get_path('module', 'system') . '/system.js',
+ );
+
+ // Get the base page.
+ $this->drupalGet('ajax_forms_test_lazy_load_form');
+ $original_settings = $this->drupalGetSettings();
+ $original_css = $original_settings['ajaxPageState']['css'];
+ $original_js = $original_settings['ajaxPageState']['js'];
+
+ // Verify that the base page doesn't have the settings and files that are to
+ // be lazy loaded as part of the next request.
+ $this->assertTrue(!isset($original_settings[$expected['setting_name']]), t('Page originally lacks the %setting, as expected.', array('%setting' => $expected['setting_name'])));
+ $this->assertTrue(!isset($original_settings[$expected['css']]), t('Page originally lacks the %css file, as expected.', array('%css' => $expected['css'])));
+ $this->assertTrue(!isset($original_settings[$expected['js']]), t('Page originally lacks the %js file, as expected.', array('%js' => $expected['js'])));
+
+ // Submit the AJAX request.
+ $commands = $this->drupalPostAJAX(NULL, array(), array('op' => t('Submit')));
+ $new_settings = $this->drupalGetSettings();
+ $new_css = $new_settings['ajaxPageState']['css'];
+ $new_js = $new_settings['ajaxPageState']['js'];
+
+ // Verify the expected setting was added.
+ $this->assertIdentical($new_settings[$expected['setting_name']], $expected['setting_value'], t('Page now has the %setting.', array('%setting' => $expected['setting_name'])));
+
+ // Verify the expected CSS file was added, both to Drupal.settings, and as
+ // an AJAX command for inclusion into the HTML.
+ // @todo A drupal_css_defaults() function in Drupal 8 would be nice.
+ $expected_css_html = drupal_get_css(array($expected['css'] => array(
+ 'type' => 'file',
+ 'group' => CSS_DEFAULT,
+ 'weight' => 0,
+ 'every_page' => FALSE,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => $expected['css'],
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ )), TRUE);
+ $this->assertEqual($new_css, $original_css + array($expected['css'] => 1), t('Page state now has the %css file.', array('%css' => $expected['css'])));
+ $this->assertCommand($commands, array('data' => $expected_css_html), t('Page now has the %css file.', array('%css' => $expected['css'])));
+
+ // Verify the expected JS file was added, both to Drupal.settings, and as
+ // an AJAX command for inclusion into the HTML. By testing for an exact HTML
+ // string containing the SCRIPT tag, we also ensure that unexpected
+ // JavaScript code, such as a jQuery.extend() that would potentially clobber
+ // rather than properly merge settings, didn't accidentally get added.
+ $expected_js_html = drupal_get_js('header', array($expected['js'] => drupal_js_defaults($expected['js'])), TRUE);
+ $this->assertEqual($new_js, $original_js + array($expected['js'] => 1), t('Page state now has the %js file.', array('%js' => $expected['js'])));
+ $this->assertCommand($commands, array('data' => $expected_js_html), t('Page now has the %js file.', array('%js' => $expected['js'])));
+ }
}
/**
diff --git a/modules/simpletest/tests/ajax_forms_test.module b/modules/simpletest/tests/ajax_forms_test.module
index d38cbbb90..075b005ea 100644
--- a/modules/simpletest/tests/ajax_forms_test.module
+++ b/modules/simpletest/tests/ajax_forms_test.module
@@ -29,6 +29,12 @@ function ajax_forms_test_menu() {
'page arguments' => array('ajax_forms_test_validation_form'),
'access callback' => TRUE,
);
+ $items['ajax_forms_test_lazy_load_form'] = array(
+ 'title' => 'AJAX forms lazy load test',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('ajax_forms_test_lazy_load_form'),
+ 'access callback' => TRUE,
+ );
return $items;
}
@@ -457,3 +463,38 @@ function ajax_forms_test_validation_form_callback($form, $form_state) {
drupal_set_message(t("Callback: drivertext=%drivertext, spare_required_field=%spare_required_field", array('%drivertext' => $form_state['values']['drivertext'], '%spare_required_field' => $form_state['values']['spare_required_field'])));
return '<div id="message_area">ajax_forms_test_validation_form_callback at ' . date('c') . '</div>';
}
+
+/**
+ * Form builder: Builds a form that triggers a simple AJAX callback.
+ */
+function ajax_forms_test_lazy_load_form($form, &$form_state) {
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Submit'),
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_lazy_load_form_ajax',
+ ),
+ );
+ return $form;
+}
+
+/**
+ * Form submit handler: Adds JavaScript and CSS that wasn't on the original form.
+ */
+function ajax_forms_test_lazy_load_form_submit($form, &$form_state) {
+ drupal_add_js(array('ajax_forms_test_lazy_load_form_submit' => 'executed'), 'setting');
+ drupal_add_css(drupal_get_path('module', 'system') . '/system.admin.css');
+ drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
+ $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * AJAX callback for the ajax_forms_test_lazy_load_form() form.
+ *
+ * This function returns nothing, because all we're interested in testing is
+ * ajax_render() adding commands for JavaScript and CSS added during the page
+ * request, such as the ones added in ajax_forms_test_lazy_load_form_submit().
+ */
+function ajax_forms_test_lazy_load_form_ajax($form, &$form_state) {
+ return NULL;
+}
diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test
index cb4fe8e2a..014fc9488 100644
--- a/modules/simpletest/tests/bootstrap.test
+++ b/modules/simpletest/tests/bootstrap.test
@@ -350,8 +350,18 @@ class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {
// Retrieving the location of a theme engine.
$this->assertIdentical(drupal_get_filename('theme_engine', 'phptemplate'), 'themes/engines/phptemplate/phptemplate.engine', t('Retrieve theme engine location.'));
- // Retrieving a file that is definitely not stored in the database.
+ // Retrieving the location of a profile. Profiles are a special case with
+ // a fixed location and naming.
$this->assertIdentical(drupal_get_filename('profile', 'standard'), 'profiles/standard/standard.profile', t('Retrieve install profile location.'));
+
+ // When a file is not found in the database cache, drupal_get_filename()
+ // searches several locations on the filesystem, including the DRUPAL_ROOT
+ // directory. We use the '.script' extension below because this is a
+ // non-existent filetype that will definitely not exist in the database.
+ // Since there is already a scripts directory, drupal_get_filename() will
+ // automatically check there for 'script' files, just as it does for (e.g.)
+ // 'module' files in modules.
+ $this->assertIdentical(drupal_get_filename('script', 'test'), 'scripts/test.script', t('Retrieve test script location.'));
}
}
diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test
index c266dc3b4..5650d8c01 100644
--- a/modules/simpletest/tests/common.test
+++ b/modules/simpletest/tests/common.test
@@ -56,6 +56,14 @@ class DrupalAlterTestCase extends DrupalWebTestCase {
$this->assertEqual($array_copy, $array_expected, t('First argument to drupal_alter() was altered.'));
$this->assertEqual($entity_copy, $entity_expected, t('Second argument to drupal_alter() was altered.'));
$this->assertEqual($array2_copy, $array2_expected, t('Third argument to drupal_alter() was altered.'));
+
+ // Verify alteration order when passing an array of types to drupal_alter().
+ // common_test_module_implements_alter() places 'block' implementation after
+ // other modules.
+ $array_copy = $array;
+ $array_expected = array('foo' => 'Drupal block theme');
+ drupal_alter(array('drupal_alter', 'drupal_alter_foo'), $array_copy);
+ $this->assertEqual($array_copy, $array_expected, t('hook_TYPE_alter() implementations ran in correct order.'));
}
}
@@ -516,7 +524,7 @@ class CommonSizeTestCase extends DrupalUnitTestCase {
/**
* Test drupal_explode_tags() and drupal_implode_tags().
*/
-class DrupalTagsHandlingTestCase extends DrupalWebTestCase {
+class DrupalTagsHandlingTestCase extends DrupalUnitTestCase {
var $validTags = array(
'Drupal' => 'Drupal',
'Drupal with some spaces' => 'Drupal with some spaces',
@@ -1739,6 +1747,37 @@ class DrupalRenderTestCase extends DrupalWebTestCase {
'@type' => var_export($element['#type'], TRUE),
)));
}
+
+ /**
+ * Tests caching of an empty render item.
+ */
+ function testDrupalRenderCache() {
+ // Force a request via GET.
+ $request_method = $_SERVER['REQUEST_METHOD'];
+ $_SERVER['REQUEST_METHOD'] = 'GET';
+ // Create an empty element.
+ $test_element = array(
+ '#cache' => array(
+ 'cid' => 'render_cache_test',
+ ),
+ '#markup' => '',
+ );
+
+ // Render the element and confirm that it goes through the rendering
+ // process (which will set $element['#printed']).
+ $element = $test_element;
+ drupal_render($element);
+ $this->assertTrue(isset($element['#printed']), t('No cache hit'));
+
+ // Render the element again and confirm that it is retrieved from the cache
+ // instead (so $element['#printed'] will not be set).
+ $element = $test_element;
+ drupal_render($element);
+ $this->assertFalse(isset($element['#printed']), t('Cache hit'));
+
+ // Restore the previous request method.
+ $_SERVER['REQUEST_METHOD'] = $request_method;
+ }
}
/**
@@ -2064,7 +2103,7 @@ class DrupalErrorCollectionUnitTest extends DrupalWebTestCase {
function assertError($error, $group, $function, $file, $message = NULL) {
$this->assertEqual($error['group'], $group, t("Group was %group", array('%group' => $group)));
$this->assertEqual($error['caller']['function'], $function, t("Function was %function", array('%function' => $function)));
- $this->assertEqual(basename($error['caller']['file']), $file, t("File was %file", array('%file' => $file)));
+ $this->assertEqual(drupal_basename($error['caller']['file']), $file, t("File was %file", array('%file' => $file)));
if (isset($message)) {
$this->assertEqual($error['message'], $message, t("Message was %message", array('%message' => $message)));
}
@@ -2074,7 +2113,7 @@ class DrupalErrorCollectionUnitTest extends DrupalWebTestCase {
/**
* Test the drupal_parse_info_file() API function.
*/
-class ParseInfoFilesTestCase extends DrupalWebTestCase {
+class ParseInfoFilesTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Parsing .info files',
@@ -2333,8 +2372,10 @@ class DrupalJSONTest extends DrupalUnitTestCase {
$str .= chr($i);
}
// Characters that must be escaped.
- $html_unsafe = array('<', '>', '&');
- $html_unsafe_escaped = array('\u003c', '\u003e', '\u0026');
+ // We check for unescaped " separately.
+ $html_unsafe = array('<', '>', '\'', '&');
+ // The following are the encoded forms of: < > ' & "
+ $html_unsafe_escaped = array('\u003C', '\u003E', '\u0027', '\u0026', '\u0022');
// Verify there aren't character encoding problems with the source string.
$this->assertIdentical(strlen($str), 128, t('A string with the full ASCII table has the correct length.'));
@@ -2346,6 +2387,11 @@ class DrupalJSONTest extends DrupalUnitTestCase {
$json = drupal_json_encode($str);
$this->assertTrue(strlen($json) > strlen($str), t('A JSON encoded string is larger than the source string.'));
+ // The first and last characters should be ", and no others.
+ $this->assertTrue($json[0] == '"', t('A JSON encoded string begins with ".'));
+ $this->assertTrue($json[strlen($json) - 1] == '"', t('A JSON encoded string ends with ".'));
+ $this->assertTrue(substr_count($json, '"') == 2, t('A JSON encoded string contains exactly two ".'));
+
// Verify that encoding/decoding is reversible.
$json_decoded = drupal_json_decode($json);
$this->assertIdentical($str, $json_decoded, t('Encoding a string to JSON and decoding back results in the original string.'));
diff --git a/modules/simpletest/tests/common_test.module b/modules/simpletest/tests/common_test.module
index c400eaed1..e75b45237 100644
--- a/modules/simpletest/tests/common_test.module
+++ b/modules/simpletest/tests/common_test.module
@@ -166,6 +166,34 @@ function bartik_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
}
/**
+ * Implements hook_TYPE_alter() on behalf of block module.
+ *
+ * This is for verifying that drupal_alter(array(TYPE1, TYPE2), ...) allows
+ * hook_module_implements_alter() to affect the order in which module
+ * implementations are executed.
+ */
+function block_drupal_alter_foo_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
+ $data['foo'] .= ' block';
+}
+
+/**
+ * Implements hook_module_implements_alter().
+ *
+ * @see block_drupal_alter_foo_alter()
+ */
+function common_test_module_implements_alter(&$implementations, $hook) {
+ // For drupal_alter(array('drupal_alter', 'drupal_alter_foo'), ...), make the
+ // block module implementations run after all the other modules. Note that
+ // when drupal_alter() is called with an array of types, the first type is
+ // considered primary and controls the module order.
+ if ($hook == 'drupal_alter_alter' && isset($implementations['block'])) {
+ $group = $implementations['block'];
+ unset($implementations['block']);
+ $implementations['block'] = $group;
+ }
+}
+
+/**
* Implements hook_theme().
*/
function common_test_theme() {
diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test
index 87d386aa7..7b15cf3fa 100644
--- a/modules/simpletest/tests/database_test.test
+++ b/modules/simpletest/tests/database_test.test
@@ -2062,6 +2062,16 @@ class DatabaseSelectComplexTestCase extends DatabaseTestCase {
$this->assertEqual($record->$age_field, 27, t('Correct data retrieved.'));
}
+ function testHavingCountQuery() {
+ $query = db_select('test')
+ ->extend('PagerDefault')
+ ->having('age + 1 > 0');
+ $query->addField('test', 'age');
+ $query->addExpression('age + 1');
+ $count = count($query->execute()->fetchCol());
+ $this->assertEqual($count, 4, t('Counted the correct number of records.'));
+ }
+
/**
* Test that countQuery properly removes 'all_fields' statements and
* ordering clauses.
@@ -3426,35 +3436,89 @@ class DatabaseTransactionTestCase extends DatabaseTestCase {
*/
function testTransactionWithDdlStatement() {
// First, test that a commit works normally, even with DDL statements.
- try {
- $this->transactionOuterLayer('D', FALSE, TRUE);
-
- // Because we committed, the inserted rows should both be present.
- $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidD'))->fetchField();
- $this->assertIdentical($saved_age, '24', t('Can retrieve DavidD row after commit.'));
- $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielD'))->fetchField();
- $this->assertIdentical($saved_age, '19', t('Can retrieve DanielD row after commit.'));
- // The created table should also exist.
- $count = db_query('SELECT COUNT(id) FROM {database_test_1}')->fetchField();
- $this->assertIdentical($count, '0', t('Table was successfully created inside a transaction.'));
- }
- catch (Exception $e) {
- $this->fail((string) $e);
- }
+ $transaction = db_transaction();
+ $this->insertRow('row');
+ $this->executeDDLStatement();
+ unset($transaction);
+ $this->assertRowPresent('row');
- // If we rollback the transaction, an exception might be thrown.
- try {
- $this->transactionOuterLayer('E', TRUE, TRUE);
+ // Even in different order.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->executeDDLStatement();
+ $this->insertRow('row');
+ unset($transaction);
+ $this->assertRowPresent('row');
+
+ // Even with stacking.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $transaction2 = db_transaction();
+ $this->executeDDLStatement();
+ unset($transaction2);
+ $transaction3 = db_transaction();
+ $this->insertRow('row');
+ unset($transaction3);
+ unset($transaction);
+ $this->assertRowPresent('row');
+
+ // A transaction after a DDL statement should still work the same.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $transaction2 = db_transaction();
+ $this->executeDDLStatement();
+ unset($transaction2);
+ $transaction3 = db_transaction();
+ $this->insertRow('row');
+ $transaction3->rollback();
+ unset($transaction3);
+ unset($transaction);
+ $this->assertRowAbsent('row');
+
+ // The behavior of a rollback depends on the type of database server.
+ if (Database::getConnection()->supportsTransactionalDDL()) {
+ // For database servers that support transactional DDL, a rollback
+ // of a transaction including DDL statements should be possible.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->insertRow('row');
+ $this->executeDDLStatement();
+ $transaction->rollback();
+ unset($transaction);
+ $this->assertRowAbsent('row');
- // Because we rolled back, the inserted rows shouldn't be present.
- $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidE'))->fetchField();
- $this->assertNotIdentical($saved_age, '24', t('Cannot retrieve DavidE row after rollback.'));
- $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielE'))->fetchField();
- $this->assertNotIdentical($saved_age, '19', t('Cannot retrieve DanielE row after rollback.'));
+ // Including with stacking.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $transaction2 = db_transaction();
+ $this->executeDDLStatement();
+ unset($transaction2);
+ $transaction3 = db_transaction();
+ $this->insertRow('row');
+ unset($transaction3);
+ $transaction->rollback();
+ unset($transaction);
+ $this->assertRowAbsent('row');
}
- catch (Exception $e) {
- // An exception also lets the test pass.
- $this->assertTrue(true, t('Exception thrown on rollback after a DDL statement was executed.'));
+ else {
+ // For database servers that do not support transactional DDL,
+ // the DDL statement should commit the transaction stack.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->insertRow('row');
+ $this->executeDDLStatement();
+ // Rollback the outer transaction.
+ try {
+ $transaction->rollback();
+ unset($transaction);
+ // @TODO: an exception should be triggered here, but is not, because
+ // "ROLLBACK" fails silently in MySQL if there is no transaction active.
+ // $this->fail(t('Rolling back a transaction containing DDL should fail.'));
+ }
+ catch (DatabaseTransactionNoActiveException $e) {
+ $this->pass(t('Rolling back a transaction containing DDL should fail.'));
+ }
+ $this->assertRowPresent('row');
}
}
@@ -3470,6 +3534,24 @@ class DatabaseTransactionTestCase extends DatabaseTestCase {
}
/**
+ * Execute a DDL statement.
+ */
+ protected function executeDDLStatement() {
+ static $count = 0;
+ $table = array(
+ 'fields' => array(
+ 'id' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('id'),
+ );
+ db_create_table('database_test_' . ++$count, $table);
+ }
+
+ /**
* Start over for a new test.
*/
protected function cleanUp() {
@@ -3513,8 +3595,8 @@ class DatabaseTransactionTestCase extends DatabaseTestCase {
* Test transaction stacking and commit / rollback.
*/
function testTransactionStacking() {
- // This test won't work right if transactions are supported.
- if (Database::getConnection()->supportsTransactions()) {
+ // This test won't work right if transactions are not supported.
+ if (!Database::getConnection()->supportsTransactions()) {
return;
}
@@ -3593,26 +3675,33 @@ class DatabaseTransactionTestCase extends DatabaseTestCase {
$this->insertRow('outer');
$transaction2 = db_transaction();
$this->insertRow('inner');
+ $transaction3 = db_transaction();
+ $this->insertRow('inner2');
// Rollback the outer transaction.
try {
$transaction->rollback();
unset($transaction);
$this->fail(t('Rolling back the outer transaction while the inner transaction is active resulted in an exception.'));
}
- catch (Exception $e) {
+ catch (DatabaseTransactionOutOfOrderException $e) {
$this->pass(t('Rolling back the outer transaction while the inner transaction is active resulted in an exception.'));
}
$this->assertFalse($database->inTransaction(), t('No more in a transaction after rolling back the outer transaction'));
- // Try to commit the inner transaction.
+ // Try to commit one inner transaction.
+ unset($transaction3);
+ $this->pass(t('Trying to commit an inner transaction resulted in an exception.'));
+ // Try to rollback one inner transaction.
try {
+ $transaction->rollback();
unset($transaction2);
- $this->fail(t('Trying to commit the inner transaction resulted in an exception.'));
+ $this->fail(t('Trying to commit an inner transaction resulted in an exception.'));
}
- catch (Exception $e) {
- $this->pass(t('Trying to commit the inner transaction resulted in an exception.'));
+ catch (DatabaseTransactionNoActiveException $e) {
+ $this->pass(t('Trying to commit an inner transaction resulted in an exception.'));
}
$this->assertRowAbsent('outer');
$this->assertRowAbsent('inner');
+ $this->assertRowAbsent('inner2');
}
}
diff --git a/modules/simpletest/tests/entity_cache_test_dependency.module b/modules/simpletest/tests/entity_cache_test_dependency.module
index 73a11495f..2d4b3be4d 100644
--- a/modules/simpletest/tests/entity_cache_test_dependency.module
+++ b/modules/simpletest/tests/entity_cache_test_dependency.module
@@ -11,7 +11,7 @@
function entity_cache_test_dependency_entity_info() {
return array(
'entity_cache_test' => array(
- 'label' => 'Entity Cache Test',
+ 'label' => variable_get('entity_cache_test_label', 'Entity Cache Test'),
),
);
}
diff --git a/modules/simpletest/tests/entity_query.test b/modules/simpletest/tests/entity_query.test
index ec951f8e2..d5e5524f2 100644
--- a/modules/simpletest/tests/entity_query.test
+++ b/modules/simpletest/tests/entity_query.test
@@ -22,10 +22,10 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase {
function setUp() {
parent::setUp(array('field_test'));
- field_attach_create_bundle('test_entity_bundle_key', 'bundle1');
- field_attach_create_bundle('test_entity_bundle_key', 'bundle2');
- field_attach_create_bundle('test_entity', 'test_bundles');
- field_attach_create_bundle('test_entity_bundle', 'test_entity_bundle');
+ field_test_create_bundle('bundle1');
+ field_test_create_bundle('bundle2');
+ field_test_create_bundle('test_bundle');
+ field_test_create_bundle('test_entity_bundle');
$instances = array();
$this->fields = array();
@@ -1084,7 +1084,6 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase {
$this->fields[0]['cardinality'] = 1;
field_update_field($this->fields[0]);
field_test_entity_info_translatable('test_entity', TRUE);
- drupal_static_reset('field_available_languages');
// Create more items with different languages.
$entity = new stdClass();
@@ -1121,7 +1120,6 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase {
$this->fields[0]['translatable'] = TRUE;
field_update_field($this->fields[0]);
field_test_entity_info_translatable('test_entity', TRUE);
- drupal_static_reset('field_available_languages');
// Create more items with different languages.
$entity = new stdClass();
diff --git a/modules/simpletest/tests/file.test b/modules/simpletest/tests/file.test
index 55e3b0aa3..4c56bfcce 100644
--- a/modules/simpletest/tests/file.test
+++ b/modules/simpletest/tests/file.test
@@ -198,7 +198,9 @@ class FileTestCase extends DrupalWebTestCase {
*/
function createFile($filepath = NULL, $contents = NULL, $scheme = NULL) {
if (!isset($filepath)) {
- $filepath = $this->randomName();
+ // Prefix with non-latin characters to ensure that all file-related
+ // tests work with international filenames.
+ $filepath = 'Файл для тестирования ' . $this->randomName();
}
if (!isset($scheme)) {
$scheme = file_default_scheme();
@@ -214,7 +216,7 @@ class FileTestCase extends DrupalWebTestCase {
$file = new stdClass();
$file->uri = $filepath;
- $file->filename = basename($file->uri);
+ $file->filename = drupal_basename($file->uri);
$file->filemime = 'text/plain';
$file->uid = 1;
$file->timestamp = REQUEST_TIME;
@@ -372,11 +374,11 @@ class FileValidatorTest extends DrupalWebTestCase {
$this->image = new stdClass();
$this->image->uri = 'misc/druplicon.png';
- $this->image->filename = basename($this->image->uri);
+ $this->image->filename = drupal_basename($this->image->uri);
$this->non_image = new stdClass();
$this->non_image->uri = 'misc/jquery.js';
- $this->non_image->filename = basename($this->non_image->uri);
+ $this->non_image->filename = drupal_basename($this->non_image->uri);
}
/**
@@ -539,7 +541,7 @@ class FileUnmanagedSaveDataTest extends FileTestCase {
// Provide a filename.
$filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE);
$this->assertTrue($filepath, t('Unnamed file saved correctly.'));
- $this->assertEqual('asdf.txt', basename($filepath), t('File was named correctly.'));
+ $this->assertEqual('asdf.txt', drupal_basename($filepath), t('File was named correctly.'));
$this->assertEqual($contents, file_get_contents($filepath), t('Contents of the file are correct.'));
$this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664));
}
@@ -666,7 +668,7 @@ class FileSaveUploadTest extends FileHookTestCase {
$this->drupalPost('file-test/upload', $edit, t('Submit'));
$this->assertResponse(200, t('Received a 200 response for posted test file.'));
$this->assertRaw(t('You WIN!'));
- $this->assertTrue(is_file('temporary://' . $dir . '/' . trim(basename($image3_realpath))));
+ $this->assertTrue(is_file('temporary://' . $dir . '/' . trim(drupal_basename($image3_realpath))));
// Check that file_load_multiple() with no arguments returns FALSE.
$this->assertFalse(file_load_multiple(), t('No files were loaded.'));
@@ -2202,7 +2204,7 @@ class FileSaveDataTest extends FileHookTestCase {
$this->assertTrue($result, t('Unnamed file saved correctly.'));
$this->assertEqual(file_default_scheme(), file_uri_scheme($result->uri), t("File was placed in Drupal's files directory."));
- $this->assertEqual($result->filename, basename($result->uri), t("Filename was set to the file's basename."));
+ $this->assertEqual($result->filename, drupal_basename($result->uri), t("Filename was set to the file's basename."));
$this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.'));
$this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.'));
$this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
@@ -2220,11 +2222,14 @@ class FileSaveDataTest extends FileHookTestCase {
function testWithFilename() {
$contents = $this->randomName(8);
- $result = file_save_data($contents, 'public://' . 'asdf.txt');
+ // Using filename with non-latin characters.
+ $filename = 'Текстовый файл.txt';
+
+ $result = file_save_data($contents, 'public://' . $filename);
$this->assertTrue($result, t('Unnamed file saved correctly.'));
$this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory."));
- $this->assertEqual('asdf.txt', basename($result->uri), t('File was named correctly.'));
+ $this->assertEqual($filename, drupal_basename($result->uri), t('File was named correctly.'));
$this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.'));
$this->assertEqual($result->filemime, 'text/plain', t('A MIME type was set.'));
$this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
@@ -2336,7 +2341,10 @@ class FileDownloadTest extends FileTestCase {
// Test generating an URL to a created file.
$file = $this->createFile();
$url = file_create_url($file->uri);
- $this->assertEqual($GLOBALS['base_url'] . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $file->filename, $url, t('Correctly generated a URL for a created file.'));
+ // URLs can't contain characters outside the ASCII set so $filename has to be
+ // encoded.
+ $filename = $GLOBALS['base_url'] . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . rawurlencode($file->filename);
+ $this->assertEqual($filename, $url, t('Correctly generated a URL for a created file.'));
$this->drupalHead($url);
$this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the created file.'));
@@ -2356,16 +2364,20 @@ class FileDownloadTest extends FileTestCase {
// Set file downloads to private so handler functions get called.
// Create a file.
- $file = $this->createFile(NULL, NULL, 'private');
+ $contents = $this->randomName(8);
+ $file = $this->createFile(NULL, $contents, 'private');
$url = file_create_url($file->uri);
// Set file_test access header to allow the download.
file_test_set_return('download', array('x-foo' => 'Bar'));
- $this->drupalHead($url);
+ $this->drupalGet($url);
$headers = $this->drupalGetHeaders();
- $this->assertEqual($headers['x-foo'] , 'Bar', t('Found header set by file_test module on private download.'));
+ $this->assertEqual($headers['x-foo'], 'Bar', t('Found header set by file_test module on private download.'));
$this->assertResponse(200, t('Correctly allowed access to a file when file_test provides headers.'));
+ // Test that the file transfered correctly.
+ $this->assertEqual($contents, $this->content, t('Contents of the file are correct.'));
+
// Deny access to all downloads via a -1 header.
file_test_set_return('download', -1);
$this->drupalHead($url);
@@ -2602,7 +2614,7 @@ class FileMimeTypeTest extends DrupalWebTestCase {
* Test mapping of mimetypes from filenames.
*/
public function testFileMimeTypeDetection() {
- $prefix = 'simpletest://';
+ $prefix = 'public://';
$test_case = array(
'test.jar' => 'application/java-archive',
diff --git a/modules/simpletest/tests/file_test.module b/modules/simpletest/tests/file_test.module
index b3c43e071..1b11316f9 100644
--- a/modules/simpletest/tests/file_test.module
+++ b/modules/simpletest/tests/file_test.module
@@ -138,7 +138,7 @@ function _file_test_form_submit(&$form, &$form_state) {
/**
* Reset/initialize the history of calls to the file_* hooks.
*
- * @see file_test_get_calls()
+ * @see file_test_get_calls()
* @see file_test_reset()
*/
function file_test_reset() {
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test
index fe2c1bbfb..13fdca201 100644
--- a/modules/simpletest/tests/form.test
+++ b/modules/simpletest/tests/form.test
@@ -588,9 +588,17 @@ class FormsElementsLabelsTestCase extends DrupalWebTestCase {
$elements = $this->xpath('//input[@id="edit-form-checkboxes-test-third-checkbox"]/following-sibling::label[@for="edit-form-checkboxes-test-third-checkbox" and @class="option"]');
$this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular checkboxes."));
+ // Make sure the label is rendered for checkboxes.
+ $elements = $this->xpath('//input[@id="edit-form-checkboxes-test-0"]/following-sibling::label[@for="edit-form-checkboxes-test-0" and @class="option"]');
+ $this->assertTrue(isset($elements[0]), t("Label 0 found checkbox."));
+
$elements = $this->xpath('//input[@id="edit-form-radios-test-second-radio"]/following-sibling::label[@for="edit-form-radios-test-second-radio" and @class="option"]');
$this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular radios."));
+ // Make sure the label is rendered for radios.
+ $elements = $this->xpath('//input[@id="edit-form-radios-test-0"]/following-sibling::label[@for="edit-form-radios-test-0" and @class="option"]');
+ $this->assertTrue(isset($elements[0]), t("Label 0 found radios."));
+
// Exercise various defaults for checkboxes and modifications to ensure
// appropriate override and correct behaviour.
$elements = $this->xpath('//input[@id="edit-form-checkbox-test"]/following-sibling::label[@for="edit-form-checkbox-test" and @class="option"]');
diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module
index 23aca244b..0a748d2df 100644
--- a/modules/simpletest/tests/form_test.module
+++ b/modules/simpletest/tests/form_test.module
@@ -688,6 +688,7 @@ function form_label_test_form() {
'first-checkbox' => t('First checkbox'),
'second-checkbox' => t('Second checkbox'),
'third-checkbox' => t('Third checkbox'),
+ '0' => t('0'),
),
);
$form['form_radios_test'] = array(
@@ -697,6 +698,7 @@ function form_label_test_form() {
'first-radio' => t('First radio'),
'second-radio' => t('Second radio'),
'third-radio' => t('Third radio'),
+ '0' => t('0'),
),
// Test #field_prefix and #field_suffix placement.
'#field_prefix' => '<span id="form-test-radios-field-prefix">' . t('Radios #field_prefix element') . '</span>',
diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test
index a6c7b40e5..09dcde60c 100644
--- a/modules/simpletest/tests/mail.test
+++ b/modules/simpletest/tests/mail.test
@@ -22,7 +22,7 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
}
function setUp() {
- parent::setUp();
+ parent::setUp(array('simpletest'));
// Set MailTestCase (i.e. this class) as the SMTP library
variable_set('mail_system', array('default-system' => 'MailTestCase'));
@@ -35,10 +35,28 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
global $language;
// Use MailTestCase for sending a message.
- $message = drupal_mail('simpletest', 'mail_test', 'testing@drupal.org', $language);
+ $message = drupal_mail('simpletest', 'mail_test', 'testing@example.com', $language);
// Assert whether the message was sent through the send function.
- $this->assertEqual(self::$sent_message['to'], 'testing@drupal.org', t('Pluggable mail system is extendable.'));
+ $this->assertEqual(self::$sent_message['to'], 'testing@example.com', t('Pluggable mail system is extendable.'));
+ }
+
+ /**
+ * Test that message sending may be canceled.
+ *
+ * @see simpletest_mail_alter()
+ */
+ function testCancelMessage() {
+ global $language;
+
+ // Reset the class variable holding a copy of the last sent message.
+ self::$sent_message = NULL;
+
+ // Send a test message that simpletest_mail_alter should cancel.
+ $message = drupal_mail('simpletest', 'cancel_test', 'cancel@example.com', $language);
+
+ // Assert that the message was not actually sent.
+ $this->assertNull(self::$sent_message, 'Message was canceled.');
}
/**
diff --git a/modules/simpletest/tests/path.test b/modules/simpletest/tests/path.test
index 4998ffa38..8b3e6dc48 100644
--- a/modules/simpletest/tests/path.test
+++ b/modules/simpletest/tests/path.test
@@ -201,6 +201,14 @@ class UrlAlterFunctionalTest extends DrupalWebTestCase {
}
/**
+ * Tests that $_GET['q'] is initialized when the request path is empty.
+ */
+ function testGetQInitialized() {
+ $this->drupalGet('');
+ $this->assertText("\$_GET['q'] is non-empty with an empty request path.", "\$_GET['q'] is initialized with an empty request path.");
+ }
+
+ /**
* Assert that an outbound path is altered to an expected value.
*
* @param $original
diff --git a/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info b/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info
new file mode 100644
index 000000000..002c0d286
--- /dev/null
+++ b/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info
@@ -0,0 +1,7 @@
+name = "System incompatible core version dependencies test"
+description = "Support module for testing system dependencies."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+dependencies[] = system_incompatible_core_version_test
diff --git a/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.module b/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.module
new file mode 100644
index 000000000..b3d9bbc7f
--- /dev/null
+++ b/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.module
@@ -0,0 +1 @@
+<?php
diff --git a/modules/simpletest/tests/system_incompatible_core_version_test.info b/modules/simpletest/tests/system_incompatible_core_version_test.info
new file mode 100644
index 000000000..ced53e9d5
--- /dev/null
+++ b/modules/simpletest/tests/system_incompatible_core_version_test.info
@@ -0,0 +1,6 @@
+name = "System incompatible core version test"
+description = "Support module for testing system dependencies."
+package = Testing
+version = VERSION
+core = 5.x
+hidden = TRUE
diff --git a/modules/simpletest/tests/system_incompatible_core_version_test.module b/modules/simpletest/tests/system_incompatible_core_version_test.module
new file mode 100644
index 000000000..b3d9bbc7f
--- /dev/null
+++ b/modules/simpletest/tests/system_incompatible_core_version_test.module
@@ -0,0 +1 @@
+<?php
diff --git a/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info b/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info
new file mode 100644
index 000000000..48db9eae3
--- /dev/null
+++ b/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info
@@ -0,0 +1,8 @@
+name = "System incompatible module version dependencies test"
+description = "Support module for testing system dependencies."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+; system_incompatible_module_version_test declares version 1.0
+dependencies[] = system_incompatible_module_version_test (>2.0)
diff --git a/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.module b/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.module
new file mode 100644
index 000000000..b3d9bbc7f
--- /dev/null
+++ b/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.module
@@ -0,0 +1 @@
+<?php
diff --git a/modules/simpletest/tests/system_incompatible_module_version_test.info b/modules/simpletest/tests/system_incompatible_module_version_test.info
new file mode 100644
index 000000000..9dfc686cf
--- /dev/null
+++ b/modules/simpletest/tests/system_incompatible_module_version_test.info
@@ -0,0 +1,6 @@
+name = "System incompatible module version test"
+description = "Support module for testing system dependencies."
+package = Testing
+version = 1.0
+core = 7.x
+hidden = TRUE
diff --git a/modules/simpletest/tests/system_incompatible_module_version_test.module b/modules/simpletest/tests/system_incompatible_module_version_test.module
new file mode 100644
index 000000000..b3d9bbc7f
--- /dev/null
+++ b/modules/simpletest/tests/system_incompatible_module_version_test.module
@@ -0,0 +1 @@
+<?php
diff --git a/modules/simpletest/tests/system_test.module b/modules/simpletest/tests/system_test.module
index 9516c9183..8cb0e837f 100644
--- a/modules/simpletest/tests/system_test.module
+++ b/modules/simpletest/tests/system_test.module
@@ -264,6 +264,14 @@ function system_test_system_info_alter(&$info, $file, $type) {
if ($file->name == 'system_dependencies_test') {
$info['hidden'] = FALSE;
}
+ if (in_array($file->name, array(
+ 'system_incompatible_module_version_dependencies_test',
+ 'system_incompatible_core_version_dependencies_test',
+ 'system_incompatible_module_version_test',
+ 'system_incompatible_core_version_test',
+ ))) {
+ $info['hidden'] = FALSE;
+ }
if ($file->name == 'requirements1_test' || $file->name == 'requirements2_test') {
$info['hidden'] = FALSE;
}
diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test
index 53557e361..af1141124 100644
--- a/modules/simpletest/tests/theme.test
+++ b/modules/simpletest/tests/theme.test
@@ -9,6 +9,8 @@
* Unit tests for the Theme API.
*/
class ThemeUnitTest extends DrupalWebTestCase {
+ protected $profile = 'testing';
+
public static function getInfo() {
return array(
'name' => 'Theme API',
@@ -194,7 +196,7 @@ class ThemeItemListUnitTest extends DrupalWebTestCase {
/**
* Unit tests for theme_links().
*/
-class ThemeLinksUnitTest extends DrupalUnitTestCase {
+class ThemeLinksTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Links',
@@ -380,3 +382,54 @@ class ThemeHtmlTag extends DrupalUnitTestCase {
$this->assertEqual('<title>title test</title>'."\n", theme_html_tag($tag), t('Test title tag generation.'));
}
}
+
+/**
+ * Tests for the ThemeRegistry class.
+ */
+class ThemeRegistryTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'ThemeRegistry',
+ 'description' => 'Tests the behavior of the ThemeRegistry class',
+ 'group' => 'Theme',
+ );
+ }
+ function setUp() {
+ parent::setUp('theme_test');
+ }
+
+ /**
+ * Tests the behavior of the theme registry class.
+ */
+ function testRaceCondition() {
+ $_SERVER['REQUEST_METHOD'] = 'GET';
+ $cid = 'test_theme_registry';
+
+ // Directly instantiate the theme registry, this will cause a base cache
+ // entry to be written in __construct().
+ $registry = new ThemeRegistry($cid, 'cache');
+
+ $this->assertTrue(cache_get($cid), 'Cache entry was created.');
+
+ // Trigger a cache miss for an offset.
+ $this->assertTrue($registry['theme_test_template_test'], 'Offset was returned correctly from the theme registry.');
+ // This will cause the ThemeRegistry class to write an updated version of
+ // the cache entry when it is destroyed, usually at the end of the request.
+ // Before that happens, manually delete the cache entry we created earlier
+ // so that the new entry is written from scratch.
+ cache_clear_all($cid, 'cache');
+
+ // Destroy the class so that it triggers a cache write for the offset.
+ unset($registry);
+
+ $this->assertTrue(cache_get($cid), 'Cache entry was created.');
+
+ // Create a new instance of the class. Confirm that both the offset
+ // requested previously, and one that has not yet been requested are both
+ // available.
+ $registry = new ThemeRegistry($cid, 'cache');
+
+ $this->assertTrue($registry['theme_test_template_test'], 'Offset was returned correctly from the theme registry');
+ $this->assertTrue($registry['theme_test_template_test_2'], 'Offset was returned correctly from the theme registry');
+ }
+}
diff --git a/modules/simpletest/tests/theme_test.module b/modules/simpletest/tests/theme_test.module
index 160d192dd..48e2e83c6 100644
--- a/modules/simpletest/tests/theme_test.module
+++ b/modules/simpletest/tests/theme_test.module
@@ -1,6 +1,28 @@
<?php
/**
+ * Implements hook_theme().
+ */
+function theme_test_theme($existing, $type, $theme, $path) {
+ $items['theme_test_template_test'] = array(
+ 'template' => 'theme_test.template_test',
+ );
+ $items['theme_test_template_test_2'] = array(
+ 'template' => 'theme_test.template_test',
+ );
+
+ return $items;
+}
+
+/**
+ * Implements hook_system_theme_info().
+ */
+function theme_test_system_theme_info() {
+ $themes['test_theme'] = drupal_get_path('module', 'theme_test') . '/themes/test_theme/test_theme.info';
+ return $themes;
+}
+
+/**
* Implements hook_menu().
*/
function theme_test_menu() {
diff --git a/modules/simpletest/tests/theme_test.template_test.tpl.php b/modules/simpletest/tests/theme_test.template_test.tpl.php
new file mode 100644
index 000000000..cde8faadd
--- /dev/null
+++ b/modules/simpletest/tests/theme_test.template_test.tpl.php
@@ -0,0 +1,2 @@
+<!-- Output for Theme API test -->
+Fail: Template not overridden.
diff --git a/modules/simpletest/tests/themes/test_theme/template.php b/modules/simpletest/tests/themes/test_theme/template.php
new file mode 100644
index 000000000..ef8118a6d
--- /dev/null
+++ b/modules/simpletest/tests/themes/test_theme/template.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * Tests a theme overriding a suggestion of a base theme hook.
+ */
+function test_theme_breadcrumb__suggestion($variables) {
+ // Tests that preprocess functions for the base theme hook get called even
+ // when the suggestion has an implementation.
+ return 'test_theme_breadcrumb__suggestion: ' . $variables['theme_test_preprocess_breadcrumb'];
+}
+
+/**
+ * Tests a theme implementing an alter hook.
+ *
+ * The confusing function name here is due to this being an implementation of
+ * the alter hook invoked when the 'theme_test' module calls
+ * drupal_alter('theme_test_alter').
+ */
+function test_theme_theme_test_alter_alter(&$data) {
+ $data = 'test_theme_theme_test_alter_alter was invoked';
+}
diff --git a/modules/simpletest/tests/themes/test_theme/test_theme.info b/modules/simpletest/tests/themes/test_theme/test_theme.info
new file mode 100644
index 000000000..dd5584b0b
--- /dev/null
+++ b/modules/simpletest/tests/themes/test_theme/test_theme.info
@@ -0,0 +1,16 @@
+name = Test theme
+description = Theme for testing the theme system
+core = 7.x
+hidden = TRUE
+
+; Normally, themes may list CSS files like this, and if they exist in the theme
+; folder, then they get added to the page. If they have the same file name as a
+; module CSS file, then the theme's version overrides the module's version, so
+; that the module's version is not added to the page. Additionally, a theme may
+; have an entry like this one, without having the corresponding CSS file in the
+; theme's folder, and in this case, it just stops the module's version from
+; being loaded, and does not replace it with an alternate version. We have this
+; here in order for a test to ensure that this correctly prevents the module
+; version from being loaded, and that errors aren't caused by the lack of this
+; file within the theme folder.
+stylesheets[all][] = system.base.css
diff --git a/modules/simpletest/tests/unicode.test b/modules/simpletest/tests/unicode.test
index 47a4938fe..cf7991b6c 100644
--- a/modules/simpletest/tests/unicode.test
+++ b/modules/simpletest/tests/unicode.test
@@ -8,7 +8,7 @@
/**
* Test unicode handling features implemented in unicode.inc.
*/
-class UnicodeUnitTest extends DrupalWebTestCase {
+class UnicodeUnitTest extends DrupalUnitTestCase {
/**
* Whether to run the extended version of the tests (including non latin1 characters).
diff --git a/modules/simpletest/tests/update_script_test.info b/modules/simpletest/tests/update_script_test.info
new file mode 100644
index 000000000..be1e3d849
--- /dev/null
+++ b/modules/simpletest/tests/update_script_test.info
@@ -0,0 +1,6 @@
+name = "Update script test"
+description = "Support module for update script testing."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
diff --git a/modules/simpletest/tests/update_script_test.install b/modules/simpletest/tests/update_script_test.install
new file mode 100644
index 000000000..6955ef11d
--- /dev/null
+++ b/modules/simpletest/tests/update_script_test.install
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the update_script_test module.
+ */
+
+/**
+ * Implements hook_requirements().
+ */
+function update_script_test_requirements($phase) {
+ $requirements = array();
+
+ if ($phase == 'update') {
+ // Set a requirements warning or error when the test requests it.
+ $requirement_type = variable_get('update_script_test_requirement_type');
+ switch ($requirement_type) {
+ case REQUIREMENT_WARNING:
+ $requirements['update_script_test'] = array(
+ 'title' => 'Update script test',
+ 'value' => 'Warning',
+ 'description' => 'This is a requirements warning provided by the update_script_test module.',
+ 'severity' => REQUIREMENT_WARNING,
+ );
+ break;
+ case REQUIREMENT_ERROR:
+ $requirements['update_script_test'] = array(
+ 'title' => 'Update script test',
+ 'value' => 'Error',
+ 'description' => 'This is a requirements error provided by the update_script_test module.',
+ 'severity' => REQUIREMENT_ERROR,
+ );
+ break;
+ }
+ }
+
+ return $requirements;
+}
+
+/**
+ * Dummy update function to run during the tests.
+ */
+function update_script_test_update_7000() {
+ return t('The update_script_test_update_7000() update was executed successfully.');
+}
diff --git a/modules/simpletest/tests/update_script_test.module b/modules/simpletest/tests/update_script_test.module
new file mode 100644
index 000000000..beb5a71ec
--- /dev/null
+++ b/modules/simpletest/tests/update_script_test.module
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * This file provides testing functionality for update.php.
+ */
+
+/**
+ * Implements hook_flush_caches().
+ *
+ * This sets a message to confirm that all caches are cleared whenever
+ * update.php completes.
+ *
+ * @see UpdateScriptFunctionalTest::testRequirements()
+ */
+function update_script_test_flush_caches() {
+ drupal_set_message(t('hook_flush_caches() invoked for update_script_test.module.'));
+}
diff --git a/modules/simpletest/tests/upgrade/drupal-6.node_type_broken.database.php b/modules/simpletest/tests/upgrade/drupal-6.node_type_broken.database.php
new file mode 100644
index 000000000..1dc1946b3
--- /dev/null
+++ b/modules/simpletest/tests/upgrade/drupal-6.node_type_broken.database.php
@@ -0,0 +1,34 @@
+<?php
+db_insert('comments')->fields(array(
+ 'cid',
+ 'pid',
+ 'nid',
+ 'uid',
+ 'subject',
+ 'comment',
+ 'hostname',
+ 'timestamp',
+ 'status',
+ 'format',
+ 'thread',
+ 'name',
+ 'mail',
+ 'homepage',
+))
+->values(array(
+ 'cid' => 1,
+ 'pid' => 0,
+ 'nid' => 37,
+ 'uid' => 3,
+ 'subject' => 'Comment title 1',
+ 'comment' => 'Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1',
+ 'hostname' => '127.0.0.1',
+ 'timestamp' => 1008617630,
+ 'status' => 0,
+ 'format' => 1,
+ 'thread' => '01/',
+ 'name' => NULL,
+ 'mail' => NULL,
+ 'homepage' => '',
+))
+->execute();
diff --git a/modules/simpletest/tests/upgrade/drupal-7.bare.minimal.database.php.gz b/modules/simpletest/tests/upgrade/drupal-7.bare.minimal.database.php.gz
new file mode 100644
index 000000000..41be271f5
--- /dev/null
+++ b/modules/simpletest/tests/upgrade/drupal-7.bare.minimal.database.php.gz
Binary files differ
diff --git a/modules/simpletest/tests/upgrade/drupal-7.bare.standard_all.database.php.gz b/modules/simpletest/tests/upgrade/drupal-7.bare.standard_all.database.php.gz
new file mode 100644
index 000000000..c47ae8783
--- /dev/null
+++ b/modules/simpletest/tests/upgrade/drupal-7.bare.standard_all.database.php.gz
Binary files differ
diff --git a/modules/simpletest/tests/upgrade/drupal-7.filled.minimal.database.php.gz b/modules/simpletest/tests/upgrade/drupal-7.filled.minimal.database.php.gz
new file mode 100644
index 000000000..de2dceb17
--- /dev/null
+++ b/modules/simpletest/tests/upgrade/drupal-7.filled.minimal.database.php.gz
Binary files differ
diff --git a/modules/simpletest/tests/upgrade/drupal-7.filled.standard_all.database.php.gz b/modules/simpletest/tests/upgrade/drupal-7.filled.standard_all.database.php.gz
new file mode 100644
index 000000000..5cc5690e1
--- /dev/null
+++ b/modules/simpletest/tests/upgrade/drupal-7.filled.standard_all.database.php.gz
Binary files differ
diff --git a/modules/simpletest/tests/upgrade/upgrade.node.test b/modules/simpletest/tests/upgrade/upgrade.node.test
index cd44790c7..b49a40b0d 100644
--- a/modules/simpletest/tests/upgrade/upgrade.node.test
+++ b/modules/simpletest/tests/upgrade/upgrade.node.test
@@ -51,6 +51,38 @@ class NodeBodyUpgradePathTestCase extends UpgradePathTestCase {
}
/**
+ * Tests the upgrade path for node disabled node types.
+ *
+ * Load a filled installation of Drupal 6 and run the upgrade process on it.
+ */
+class DisabledNodeTypeTestCase extends UpgradePathTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Disabled node type upgrade path',
+ 'description' => 'Disabled node type upgrade path tests.',
+ 'group' => 'Upgrade path',
+ );
+ }
+
+ public function setUp() {
+ // Path to the database dump.
+ $this->databaseDumpFiles = array(
+ drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.filled.database.php',
+ drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.node_type_broken.database.php',
+ );
+ parent::setUp();
+ }
+
+ /**
+ * Tests a successful upgrade.
+ */
+ public function testDisabledNodeTypeUpgrade() {
+ $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
+ $this->assertTrue(field_info_instance('comment', 'comment_body', 'comment_node_broken'), 'Comment body field instance was created for comments attached to the disabled broken node type');
+ }
+}
+
+/**
* Upgrade test for node type poll.
*
* Load a bare installation of Drupal 6 and run the upgrade process on it.
diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test
index 7f934fe7b..2602b09a2 100644
--- a/modules/simpletest/tests/upgrade/upgrade.test
+++ b/modules/simpletest/tests/upgrade/upgrade.test
@@ -28,9 +28,60 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
var $loadedModules = array();
/**
+ * Flag to indicate whether zlib is installed or not.
+ */
+ var $zlibInstalled = TRUE;
+
+ /**
+ * Flag to indicate whether there are pending updates or not.
+ */
+ var $pendingUpdates = TRUE;
+
+ /**
+ * Constructs an UpgradePathTestCase object.
+ *
+ * @param $test_id
+ * (optional) The ID of the test. Tests with the same id are reported
+ * together.
+ */
+ function __construct($test_id = NULL) {
+ parent::__construct($test_id);
+ $this->zlibInstalled = function_exists('gzopen');
+ }
+
+ /**
+ * Prepares the appropriate session for the release of Drupal being upgraded.
+ */
+ protected function prepareD7Session() {
+ // Generate and set a D6-compatible session cookie.
+ $this->curlInitialize();
+ $sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
+ $session_name = update_get_d6_session_name();
+ curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode($session_name) . '=' . rawurlencode($sid));
+
+ // Force our way into the session of the child site.
+ drupal_save_session(TRUE);
+ // A session cannot be written without the ssid column which is missing on
+ // Drupal 6 sites.
+ db_add_field('sessions', 'ssid', array('description' => "Secure session ID. The value is generated by Drupal's session handlers.", 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''));
+ _drupal_session_write($sid, '');
+ // Remove the temporarily added ssid column.
+ db_drop_field('sessions', 'ssid');
+ drupal_save_session(FALSE);
+ }
+
+ /**
* Override of DrupalWebTestCase::setUp() specialized for upgrade testing.
*/
protected function setUp() {
+ // We are going to set a missing zlib requirement property for usage
+ // during the performUpgrade() and tearDown() methods. Also set that the
+ // tests failed.
+ if (!$this->zlibInstalled) {
+ parent::setUp();
+ return;
+ }
+
global $user, $language, $conf;
// Load the Update API.
@@ -92,7 +143,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
$conf = array();
// Load the database from the portable PHP dump.
+ // The files may be gzipped.
foreach ($this->databaseDumpFiles as $file) {
+ if (substr($file, -3) == '.gz') {
+ $file = "compress.zlib://$file";
+ }
require $file;
}
@@ -109,26 +164,14 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
$user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject();
// Generate and set a D6-compatible session cookie.
- $this->curlInitialize();
- $sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
- $session_name = update_get_d6_session_name();
- curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode($session_name) . '=' . rawurlencode($sid));
-
- // Force our way into the session of the child site.
- drupal_save_session(TRUE);
- // A session cannot be written without the ssid column which is missing on
- // Drupal 6 sites.
- db_add_field('sessions', 'ssid', array('description' => "Secure session ID. The value is generated by Drupal's session handlers.", 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''));
- _drupal_session_write($sid, '');
- // Remove the temporarily added ssid column.
- db_drop_field('sessions', 'ssid');
- drupal_save_session(FALSE);
+ $this->prepareD7Session();
// Restore necessary variables.
$this->variable_set('clean_url', $clean_url_original);
$this->variable_set('site_mail', 'simpletest@example.com');
drupal_set_time_limit($this->timeLimit);
+ $this->setup = TRUE;
}
/**
@@ -137,6 +180,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
protected function tearDown() {
global $user, $language;
+ if (!$this->zlibInstalled) {
+ parent::tearDown();
+ return;
+ }
+
// In case a fatal error occurred that was not in the test process read the
// log to pick up any fatal errors.
simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
@@ -231,6 +279,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
* TRUE if the upgrade succeeded, FALSE otherwise.
*/
protected function performUpgrade($register_errors = TRUE) {
+ if (!$this->zlibInstalled) {
+ $this->fail(t('Missing zlib requirement for upgrade tests.'));
+ return FALSE;
+ }
+
$update_url = $GLOBALS['base_url'] . '/update.php';
// Load the first update screen.
@@ -245,6 +298,14 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
return FALSE;
}
+ // The test should pass if there are no pending updates.
+ $content = $this->drupalGetContent();
+ if (strpos($content, t('No pending updates.')) !== FALSE) {
+ $this->pass(t('No pending updates and therefore no upgrade process to test.'));
+ $this->pendingUpdates = FALSE;
+ return TRUE;
+ }
+
// Go!
$this->drupalPost(NULL, array(), t('Apply pending updates'));
if (!$this->assertResponse(200)) {
@@ -319,6 +380,26 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
}
/**
+ * Performs end-to-end point test of the release update path.
+ */
+abstract class UpdatePathTestCase extends UpgradePathTestCase {
+ /**
+ * Overrides UpgradePathTestCase::prepareD7Session().
+ */
+ protected function prepareD7Session() {
+ // Generate and set a D7-compatible session cookie.
+ $this->curlInitialize();
+ $sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
+ curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode(session_name()) . '=' . rawurlencode($sid));
+
+ // Force our way into the session of the child site.
+ drupal_save_session(TRUE);
+ _drupal_session_write($sid, '');
+ drupal_save_session(FALSE);
+ }
+}
+
+/**
* Perform basic upgrade tests.
*
* Load a bare installation of Drupal 6 and run the upgrade process on it.
@@ -351,7 +432,7 @@ class BasicUpgradePath extends UpgradePathTestCase {
// Destroy a table that the upgrade process needs.
db_drop_table('access');
// Assert that the upgrade fails.
- $this->assertFalse($this->performUpgrade(FALSE), t('A failed upgrade should return messages.'));
+ $this->assertFalse($this->performUpgrade(FALSE) && $this->pendingUpdates, t('A failed upgrade should return messages.'));
}
/**
@@ -408,3 +489,309 @@ class BasicUpgradePath extends UpgradePathTestCase {
$this->assertFalse($update_d6, t('The D6 upgrade flag variable has been correctly disabled.'));
}
}
+
+/**
+ * Performs point release update tests on a bare database.
+ *
+ * Loads an installation of Drupal 7.0 and runs the update process on it.
+ *
+ * The install contains the standard profile (plus all optional) modules
+ * without any content so that an update from any of the modules under this
+ * profile installation can be wholly tested.
+ */
+class BasicStandardUpdatePath extends UpdatePathTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Basic standard + all profile update path',
+ 'description' => 'Basic update path tests for a standard profile install with all enabled modules.',
+ 'group' => 'Upgrade path',
+ );
+ }
+
+ public function setUp() {
+ // Path to the database dump files.
+ $this->databaseDumpFiles = array(
+ drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.bare.standard_all.database.php.gz',
+ );
+ parent::setUp();
+ }
+
+ /**
+ * Tests a successful point release update.
+ */
+ public function testBasicStandardUpdate() {
+ $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
+
+ // Hit the frontpage.
+ $this->drupalGet('');
+ $this->assertResponse(200);
+
+ // Verify that we are still logged in.
+ $this->drupalGet('user');
+ $this->clickLink(t('Edit'));
+ $this->assertEqual($this->getUrl(), url('user/1/edit', array('absolute' => TRUE)), t('We are still logged in as admin at the end of the upgrade.'));
+
+ // Logout and verify that we can login back in with our initial password.
+ $this->drupalLogout();
+ $this->drupalLogin((object) array(
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'pass_raw' => 'admin',
+ ));
+
+ // The previous login should've triggered a password rehash, so login one
+ // more time to make sure the new hash is readable.
+ $this->drupalLogout();
+ $this->drupalLogin((object) array(
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'pass_raw' => 'admin',
+ ));
+
+ // Test that the site name is correctly displayed.
+ $this->assertText('drupal', t('The site name is correctly displayed.'));
+
+ // Verify that the main admin sections are available.
+ $this->drupalGet('admin');
+ $this->assertText(t('Content'));
+ $this->assertText(t('Appearance'));
+ $this->assertText(t('People'));
+ $this->assertText(t('Configuration'));
+ $this->assertText(t('Reports'));
+ $this->assertText(t('Structure'));
+ $this->assertText(t('Modules'));
+
+ // Confirm that no {menu_links} entry exists for user/autocomplete.
+ $result = db_query('SELECT COUNT(*) FROM {menu_links} WHERE link_path = :user_autocomplete', array(':user_autocomplete' => 'user/autocomplete'))->fetchField();
+ $this->assertFalse($result, t('No {menu_links} entry exists for user/autocomplete'));
+ }
+}
+
+/**
+ * Performs point release update tests on a bare database.
+ *
+ * Loads an installation of Drupal 7.0 and runs the update process on it.
+ *
+ * The install contains the minimal profile modules (without any generated
+ * content) so that an update from of a site under this profile may be tested.
+ */
+class BasicMinimalUpdatePath extends UpdatePathTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Basic minimal profile update path',
+ 'description' => 'Basic update path tests for a minimal profile install.',
+ 'group' => 'Upgrade path',
+ );
+ }
+
+ public function setUp() {
+ // Path to the database dump files.
+ $this->databaseDumpFiles = array(
+ drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.bare.minimal.database.php.gz',
+ );
+ parent::setUp();
+ }
+
+ /**
+ * Tests a successful point release update.
+ */
+ public function testBasicMinimalUpdate() {
+ $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
+
+ // Hit the frontpage.
+ $this->drupalGet('');
+ $this->assertResponse(200);
+
+ // Verify that we are still logged in.
+ $this->drupalGet('user');
+ $this->clickLink(t('Edit'));
+ $this->assertEqual($this->getUrl(), url('user/1/edit', array('absolute' => TRUE)), t('We are still logged in as admin at the end of the upgrade.'));
+
+ // Logout and verify that we can login back in with our initial password.
+ $this->drupalLogout();
+ $this->drupalLogin((object) array(
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'pass_raw' => 'admin',
+ ));
+
+ // The previous login should've triggered a password rehash, so login one
+ // more time to make sure the new hash is readable.
+ $this->drupalLogout();
+ $this->drupalLogin((object) array(
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'pass_raw' => 'admin',
+ ));
+
+ // Test that the site name is correctly displayed.
+ $this->assertText('drupal', t('The site name is correctly displayed.'));
+
+ // Verify that the main admin sections are available.
+ $this->drupalGet('admin');
+ $this->assertText(t('Content'));
+ $this->assertText(t('Appearance'));
+ $this->assertText(t('People'));
+ $this->assertText(t('Configuration'));
+ $this->assertText(t('Reports'));
+ $this->assertText(t('Structure'));
+ $this->assertText(t('Modules'));
+
+ // Confirm that no {menu_links} entry exists for user/autocomplete.
+ $result = db_query('SELECT COUNT(*) FROM {menu_links} WHERE link_path = :user_autocomplete', array(':user_autocomplete' => 'user/autocomplete'))->fetchField();
+ $this->assertFalse($result, t('No {menu_links} entry exists for user/autocomplete'));
+ }
+}
+
+/**
+ * Performs point release update tests on a 'filled' database.
+ *
+ * Loads an installation of Drupal 7.0 and runs the update process on it.
+ *
+ * The install contains the standard profile (plus all optional) modules
+ * with generated content so that an update from any of the modules under this
+ * profile installation can be wholly tested.
+ */
+class FilledStandardUpdatePath extends UpdatePathTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Basic standard + all profile update path, populated database',
+ 'description' => 'Basic update path tests for a standard profile install with all enabled modules and a populated database.',
+ 'group' => 'Upgrade path',
+ );
+ }
+
+ public function setUp() {
+ // Path to the database dump files.
+ $this->databaseDumpFiles = array(
+ drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.filled.standard_all.database.php.gz',
+ );
+ parent::setUp();
+ }
+
+ /**
+ * Tests a successful point release update.
+ */
+ public function testFilledStandardUpdate() {
+ $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
+
+ // Hit the frontpage.
+ $this->drupalGet('');
+ $this->assertResponse(200);
+
+ // Verify that we are still logged in.
+ $this->drupalGet('user');
+ $this->clickLink(t('Edit'));
+ $this->assertEqual($this->getUrl(), url('user/1/edit', array('absolute' => TRUE)), t('We are still logged in as admin at the end of the upgrade.'));
+
+ // Logout and verify that we can login back in with our initial password.
+ $this->drupalLogout();
+ $this->drupalLogin((object) array(
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'pass_raw' => 'admin',
+ ));
+
+ // The previous login should've triggered a password rehash, so login one
+ // more time to make sure the new hash is readable.
+ $this->drupalLogout();
+ $this->drupalLogin((object) array(
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'pass_raw' => 'admin',
+ ));
+
+ // Test that the site name is correctly displayed.
+ $this->assertText('drupal', t('The site name is correctly displayed.'));
+
+ // Verify that the main admin sections are available.
+ $this->drupalGet('admin');
+ $this->assertText(t('Content'));
+ $this->assertText(t('Appearance'));
+ $this->assertText(t('People'));
+ $this->assertText(t('Configuration'));
+ $this->assertText(t('Reports'));
+ $this->assertText(t('Structure'));
+ $this->assertText(t('Modules'));
+
+ // Confirm that no {menu_links} entry exists for user/autocomplete.
+ $result = db_query('SELECT COUNT(*) FROM {menu_links} WHERE link_path = :user_autocomplete', array(':user_autocomplete' => 'user/autocomplete'))->fetchField();
+ $this->assertFalse($result, t('No {menu_links} entry exists for user/autocomplete'));
+ }
+}
+
+/**
+ * Performs point release update tests on a populated database.
+ *
+ * Loads an installation of Drupal 7.0 and runs the update process on it.
+ *
+ * The install contains the minimal profile modules (along with generated
+ * content) so that an update from of a site under this profile may be tested.
+ */
+class FilledMinimalUpdatePath extends UpdatePathTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Basic minimal profile update path, populated database',
+ 'description' => 'Basic update path tests for a minimal profile install with a populated database.',
+ 'group' => 'Upgrade path',
+ );
+ }
+
+ public function setUp() {
+ // Path to the database dump files.
+ $this->databaseDumpFiles = array(
+ drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.filled.minimal.database.php.gz',
+ );
+ parent::setUp();
+ }
+
+ /**
+ * Tests a successful point release update.
+ */
+ public function testFilledStandardUpdate() {
+ $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
+
+ // Hit the frontpage.
+ $this->drupalGet('');
+ $this->assertResponse(200);
+
+ // Verify that we are still logged in.
+ $this->drupalGet('user');
+ $this->clickLink(t('Edit'));
+ $this->assertEqual($this->getUrl(), url('user/1/edit', array('absolute' => TRUE)), t('We are still logged in as admin at the end of the upgrade.'));
+
+ // Logout and verify that we can login back in with our initial password.
+ $this->drupalLogout();
+ $this->drupalLogin((object) array(
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'pass_raw' => 'admin',
+ ));
+
+ // The previous login should've triggered a password rehash, so login one
+ // more time to make sure the new hash is readable.
+ $this->drupalLogout();
+ $this->drupalLogin((object) array(
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'pass_raw' => 'admin',
+ ));
+
+ // Test that the site name is correctly displayed.
+ $this->assertText('drupal', t('The site name is correctly displayed.'));
+
+ // Verify that the main admin sections are available.
+ $this->drupalGet('admin');
+ $this->assertText(t('Content'));
+ $this->assertText(t('Appearance'));
+ $this->assertText(t('People'));
+ $this->assertText(t('Configuration'));
+ $this->assertText(t('Reports'));
+ $this->assertText(t('Structure'));
+ $this->assertText(t('Modules'));
+
+ // Confirm that no {menu_links} entry exists for user/autocomplete.
+ $result = db_query('SELECT COUNT(*) FROM {menu_links} WHERE link_path = :user_autocomplete', array(':user_autocomplete' => 'user/autocomplete'))->fetchField();
+ $this->assertFalse($result, t('No {menu_links} entry exists for user/autocomplete'));
+ }
+}
diff --git a/modules/simpletest/tests/url_alter_test.module b/modules/simpletest/tests/url_alter_test.module
index e229ab986..9287ff523 100644
--- a/modules/simpletest/tests/url_alter_test.module
+++ b/modules/simpletest/tests/url_alter_test.module
@@ -30,6 +30,10 @@ function url_alter_test_foo() {
* Implements hook_url_inbound_alter().
*/
function url_alter_test_url_inbound_alter(&$path, $original_path, $path_language) {
+ if (!request_path() && !empty($_GET['q'])) {
+ drupal_set_message("\$_GET['q'] is non-empty with an empty request path.");
+ }
+
// Rewrite user/username to user/uid.
if (preg_match('!^user/([^/]+)(/.*)?!', $path, $matches)) {
if ($account = user_load_by_name($matches[1])) {