First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,126 @@
<?php
class ActionsConfigurationTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Actions configuration',
'description' => 'Tests complex actions configuration by adding, editing, and deleting a complex action.',
'group' => 'Actions',
);
}
/**
* Test the configuration of advanced actions through the administration
* interface.
*/
function testActionConfiguration() {
// Create a user with permission to view the actions administration pages.
$user = $this->drupalCreateUser(array('administer actions'));
$this->drupalLogin($user);
// Make a POST request to admin/config/system/actions/manage.
$edit = array();
$edit['action'] = drupal_hash_base64('system_goto_action');
$this->drupalPost('admin/config/system/actions/manage', $edit, t('Create'));
// Make a POST request to the individual action configuration page.
$edit = array();
$action_label = $this->randomName();
$edit['actions_label'] = $action_label;
$edit['url'] = 'admin';
$this->drupalPost('admin/config/system/actions/configure/' . drupal_hash_base64('system_goto_action'), $edit, t('Save'));
// Make sure that the new complex action was saved properly.
$this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully saved the complex action."));
$this->assertText($action_label, t("Make sure the action label appears on the configuration page after we've saved the complex action."));
// Make another POST request to the action edit page.
$this->clickLink(t('configure'));
preg_match('|admin/config/system/actions/configure/(\d+)|', $this->getUrl(), $matches);
$aid = $matches[1];
$edit = array();
$new_action_label = $this->randomName();
$edit['actions_label'] = $new_action_label;
$edit['url'] = 'admin';
$this->drupalPost(NULL, $edit, t('Save'));
// Make sure that the action updated properly.
$this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully updated the complex action."));
$this->assertNoText($action_label, t("Make sure the old action label does NOT appear on the configuration page after we've updated the complex action."));
$this->assertText($new_action_label, t("Make sure the action label appears on the configuration page after we've updated the complex action."));
// Make sure that deletions work properly.
$this->clickLink(t('delete'));
$edit = array();
$this->drupalPost("admin/config/system/actions/delete/$aid", $edit, t('Delete'));
// Make sure that the action was actually deleted.
$this->assertRaw(t('Action %action was deleted', array('%action' => $new_action_label)), t('Make sure that we get a delete confirmation message.'));
$this->drupalGet('admin/config/system/actions/manage');
$this->assertNoText($new_action_label, t("Make sure the action label does not appear on the overview page after we've deleted the action."));
$exists = db_query('SELECT aid FROM {actions} WHERE callback = :callback', array(':callback' => 'drupal_goto_action'))->fetchField();
$this->assertFalse($exists, t('Make sure the action is gone from the database after being deleted.'));
}
}
/**
* Test actions executing in a potential loop, and make sure they abort properly.
*/
class ActionLoopTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Actions executing in a potentially infinite loop',
'description' => 'Tests actions executing in a loop, and makes sure they abort properly.',
'group' => 'Actions',
);
}
function setUp() {
parent::setUp('dblog', 'trigger', 'actions_loop_test');
}
/**
* Set up a loop with 3 - 12 recursions, and see if it aborts properly.
*/
function testActionLoop() {
$user = $this->drupalCreateUser(array('administer actions'));
$this->drupalLogin($user);
$hash = drupal_hash_base64('actions_loop_test_log');
$edit = array('aid' => $hash);
$this->drupalPost('admin/structure/trigger/actions_loop_test', $edit, t('Assign'));
// Delete any existing watchdog messages to clear the plethora of
// "Action added" messages from when Drupal was installed.
db_delete('watchdog')->execute();
// To prevent this test from failing when xdebug is enabled, the maximum
// recursion level should be kept low enough to prevent the xdebug
// infinite recursion protection mechanism from aborting the request.
// See http://drupal.org/node/587634.
variable_set('actions_max_stack', 7);
$this->triggerActions();
}
/**
* Create an infinite loop by causing a watchdog message to be set,
* which causes the actions to be triggered again, up to actions_max_stack
* times.
*/
protected function triggerActions() {
$this->drupalGet('<front>', array('query' => array('trigger_actions_on_watchdog' => TRUE)));
$expected = array();
$expected[] = 'Triggering action loop';
for ($i = 1; $i <= variable_get('actions_max_stack', 35); $i++) {
$expected[] = "Test log #$i";
}
$expected[] = 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.';
$result = db_query("SELECT message FROM {watchdog} WHERE type = 'actions_loop_test' OR type = 'actions' ORDER BY wid");
$loop_started = FALSE;
foreach ($result as $row) {
$expected_message = array_shift($expected);
$this->assertEqual($row->message, $expected_message, t('Expected message %expected, got %message.', array('%expected' => $expected_message, '%message' => $row->message)));
}
$this->assertTrue(empty($expected), t('All expected messages found.'));
}
}

View file

@ -0,0 +1,12 @@
name = Actions loop test
description = Support module for action loop testing.
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,11 @@
<?php
/**
* Implements hook_install().
*/
function actions_loop_test_install() {
db_update('system')
->fields(array('weight' => 1))
->condition('name', 'actions_loop_test')
->execute();
}

View file

@ -0,0 +1,95 @@
<?php
/**
* Implements hook_trigger_info().
*/
function actions_loop_test_trigger_info() {
return array(
'actions_loop_test' => array(
'watchdog' => array(
'label' => t('When a message is logged'),
),
),
);
}
/**
* Implements hook_watchdog().
*/
function actions_loop_test_watchdog(array $log_entry) {
// If the triggering actions are not explicitly enabled, abort.
if (empty($_GET['trigger_actions_on_watchdog'])) {
return;
}
// Get all the action ids assigned to the trigger on the watchdog hook's
// "run" event.
$aids = trigger_get_assigned_actions('watchdog');
// We can pass in any applicable information in $context. There isn't much in
// this case, but we'll pass in the hook name as the bare minimum.
$context = array(
'hook' => 'watchdog',
);
// Fire the actions on the associated object ($log_entry) and the context
// variable.
actions_do(array_keys($aids), $log_entry, $context);
}
/**
* Implements hook_init().
*/
function actions_loop_test_init() {
if (!empty($_GET['trigger_actions_on_watchdog'])) {
watchdog_skip_semaphore('actions_loop_test', 'Triggering action loop');
}
}
/**
* Implements hook_action_info().
*/
function actions_loop_test_action_info() {
return array(
'actions_loop_test_log' => array(
'label' => t('Write a message to the log.'),
'type' => 'system',
'configurable' => FALSE,
'triggers' => array('any'),
),
);
}
/**
* Write a message to the log.
*/
function actions_loop_test_log() {
$count = &drupal_static(__FUNCTION__, 0);
$count++;
watchdog_skip_semaphore('actions_loop_test', "Test log #$count");
}
/**
* Replacement of the watchdog() function that eliminates the use of semaphores
* so that we can test the abortion of an action loop.
*/
function watchdog_skip_semaphore($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
global $user, $base_root;
// Prepare the fields to be logged
$log_entry = array(
'type' => $type,
'message' => $message,
'variables' => $variables,
'severity' => $severity,
'link' => $link,
'user' => $user,
'uid' => isset($user->uid) ? $user->uid : 0,
'request_uri' => $base_root . request_uri(),
'referer' => $_SERVER['HTTP_REFERER'],
'ip' => ip_address(),
'timestamp' => REQUEST_TIME,
);
// Call the logging hooks to log/process the message
foreach (module_implements('watchdog') as $module) {
module_invoke($module, 'watchdog', $log_entry);
}
}

View file

@ -0,0 +1,617 @@
<?php
class AJAXTestCase extends DrupalWebTestCase {
function setUp() {
$modules = func_get_args();
if (isset($modules[0]) && is_array($modules[0])) {
$modules = $modules[0];
}
parent::setUp(array_unique(array_merge(array('ajax_test', 'ajax_forms_test'), $modules)));
}
/**
* Assert that a command with the required properties exists within the array of Ajax commands returned by the server.
*
* The Ajax framework, via the ajax_deliver() and ajax_render() functions,
* returns an array of commands. This array sometimes includes commands
* automatically provided by the framework in addition to commands returned by
* a particular page callback. During testing, we're usually interested that a
* particular command is present, and don't care whether other commands
* precede or follow the one we're interested in. Additionally, the command
* we're interested in may include additional data that we're not interested
* in. Therefore, this function simply asserts that one of the commands in
* $haystack contains all of the keys and values in $needle. Furthermore, if
* $needle contains a 'settings' key with an array value, we simply assert
* that all keys and values within that array are present in the command we're
* checking, and do not consider it a failure if the actual command contains
* additional settings that aren't part of $needle.
*
* @param $haystack
* An array of Ajax commands returned by the server.
* @param $needle
* Array of info we're expecting in one of those commands.
* @param $message
* An assertion message.
*/
protected function assertCommand($haystack, $needle, $message) {
$found = FALSE;
foreach ($haystack as $command) {
// If the command has additional settings that we're not testing for, do
// not consider that a failure.
if (isset($command['settings']) && is_array($command['settings']) && isset($needle['settings']) && is_array($needle['settings'])) {
$command['settings'] = array_intersect_key($command['settings'], $needle['settings']);
}
// If the command has additional data that we're not testing for, do not
// consider that a failure. Also, == instead of ===, because we don't
// require the key/value pairs to be in any particular order
// (http://www.php.net/manual/en/language.operators.array.php).
if (array_intersect_key($command, $needle) == $needle) {
$found = TRUE;
break;
}
}
$this->assertTrue($found, $message);
}
}
/**
* Tests primary Ajax framework functions.
*/
class AJAXFrameworkTestCase extends AJAXTestCase {
protected $profile = 'testing';
public static function getInfo() {
return array(
'name' => 'AJAX framework',
'description' => 'Performs tests on AJAX framework functions.',
'group' => 'AJAX',
);
}
/**
* Test that ajax_render() returns JavaScript settings generated during the page request.
*
* @todo Add tests to ensure that ajax_render() returns commands for new CSS
* and JavaScript files to be loaded by the page. See
* http://drupal.org/node/561858.
*/
function testAJAXRender() {
$commands = $this->drupalGetAJAX('ajax-test/render');
// Verify that there is a command to load settings added with
// drupal_add_js().
$expected = array(
'command' => 'settings',
'settings' => array('basePath' => base_path(), 'ajax' => 'test'),
);
$this->assertCommand($commands, $expected, t('ajax_render() loads settings added with drupal_add_js().'));
// Verify that Ajax settings are loaded for #type 'link'.
$this->drupalGet('ajax-test/link');
$settings = $this->drupalGetSettings();
$this->assertEqual($settings['ajax']['ajax-link']['url'], url('filter/tips'));
$this->assertEqual($settings['ajax']['ajax-link']['wrapper'], 'block-system-main');
}
/**
* Test behavior of ajax_render_error().
*/
function testAJAXRenderError() {
// Verify default error message.
$commands = $this->drupalGetAJAX('ajax-test/render-error');
$expected = array(
'command' => 'alert',
'text' => t('An error occurred while handling the request: The server received invalid input.'),
);
$this->assertCommand($commands, $expected, t('ajax_render_error() invokes alert command.'));
// Verify custom error message.
$edit = array(
'message' => 'Custom error message.',
);
$commands = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit));
$expected = array(
'command' => 'alert',
'text' => $edit['message'],
);
$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',
);
// @todo D8: Add a drupal_css_defaults() helper function.
$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);
$expected_js_html = drupal_get_js('header', array($expected['js'] => drupal_js_defaults($expected['js'])), TRUE);
// 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 requests.
$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 without triggering files getting added.
$commands = $this->drupalPostAJAX(NULL, array('add_files' => FALSE), array('op' => t('Submit')));
$new_settings = $this->drupalGetSettings();
// Verify the setting was not added when not expected.
$this->assertTrue(!isset($new_settings['setting_name']), t('Page still lacks the %setting, as expected.', array('%setting' => $expected['setting_name'])));
// Verify a settings command does not add CSS or scripts to Drupal.settings
// and no command inserts the corresponding tags on the page.
$found_settings_command = FALSE;
$found_markup_command = FALSE;
foreach ($commands as $command) {
if ($command['command'] == 'settings' && (array_key_exists('css', $command['settings']['ajaxPageState']) || array_key_exists('js', $command['settings']['ajaxPageState']))) {
$found_settings_command = TRUE;
}
if (isset($command['data']) && ($command['data'] == $expected_js_html || $command['data'] == $expected_css_html)) {
$found_markup_command = TRUE;
}
}
$this->assertFalse($found_settings_command, t('Page state still lacks the %css and %js files, as expected.', array('%css' => $expected['css'], '%js' => $expected['js'])));
$this->assertFalse($found_markup_command, t('Page still lacks the %css and %js files, as expected.', array('%css' => $expected['css'], '%js' => $expected['js'])));
// Submit the AJAX request and trigger adding files.
$commands = $this->drupalPostAJAX(NULL, array('add_files' => TRUE), 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.
$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.
$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'])));
}
/**
* Tests that overridden CSS files are not added during lazy load.
*/
function testLazyLoadOverriddenCSS() {
// The test theme overrides system.base.css without an implementation,
// thereby removing it.
theme_enable(array('test_theme'));
variable_set('theme_default', 'test_theme');
// This gets the form, and emulates an Ajax submission on it, including
// adding markup to the HEAD and BODY for any lazy loaded JS/CSS files.
$this->drupalPostAJAX('ajax_forms_test_lazy_load_form', array('add_files' => TRUE), array('op' => t('Submit')));
// Verify that the resulting HTML does not load the overridden CSS file.
// We add a "?" to the assertion, because Drupal.settings may include
// information about the file; we only really care about whether it appears
// in a LINK or STYLE tag, for which Drupal always adds a query string for
// cache control.
$this->assertNoText('system.base.css?', 'Ajax lazy loading does not add overridden CSS files.');
}
}
/**
* Tests Ajax framework commands.
*/
class AJAXCommandsTestCase extends AJAXTestCase {
public static function getInfo() {
return array(
'name' => 'AJAX commands',
'description' => 'Performs tests on AJAX framework commands.',
'group' => 'AJAX',
);
}
/**
* Test the various Ajax Commands.
*/
function testAJAXCommands() {
$form_path = 'ajax_forms_test_ajax_commands_form';
$web_user = $this->drupalCreateUser(array('access content'));
$this->drupalLogin($web_user);
$edit = array();
// Tests the 'after' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div")));
$expected = array(
'command' => 'insert',
'method' => 'after',
'data' => 'This will be placed after',
);
$this->assertCommand($commands, $expected, "'after' AJAX command issued with correct data");
// Tests the 'alert' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert")));
$expected = array(
'command' => 'alert',
'text' => 'Alert',
);
$this->assertCommand($commands, $expected, "'alert' AJAX Command issued with correct text");
// Tests the 'append' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Append': Click to append something")));
$expected = array(
'command' => 'insert',
'method' => 'append',
'data' => 'Appended text',
);
$this->assertCommand($commands, $expected, "'append' AJAX command issued with correct data");
// Tests the 'before' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'before': Click to put something before the div")));
$expected = array(
'command' => 'insert',
'method' => 'before',
'data' => 'Before text',
);
$this->assertCommand($commands, $expected, "'before' AJAX command issued with correct data");
// Tests the 'changed' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed.")));
$expected = array(
'command' => 'changed',
'selector' => '#changed_div',
);
$this->assertCommand($commands, $expected, "'changed' AJAX command issued with correct selector");
// Tests the 'changed' command using the second argument.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed with asterisk.")));
$expected = array(
'command' => 'changed',
'selector' => '#changed_div',
'asterisk' => '#changed_div_mark_this',
);
$this->assertCommand($commands, $expected, "'changed' AJAX command (with asterisk) issued with correct selector");
// Tests the 'css' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the '#box' div to be blue.")));
$expected = array(
'command' => 'css',
'selector' => '#css_div',
'argument' => array('background-color' => 'blue'),
);
$this->assertCommand($commands, $expected, "'css' AJAX command issued with correct selector");
// Tests the 'data' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX data command: Issue command.")));
$expected = array(
'command' => 'data',
'name' => 'testkey',
'value' => 'testvalue',
);
$this->assertCommand($commands, $expected, "'data' AJAX command issued with correct key and value");
// Tests the 'invoke' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX invoke command: Invoke addClass() method.")));
$expected = array(
'command' => 'invoke',
'method' => 'addClass',
'arguments' => array('error'),
);
$this->assertCommand($commands, $expected, "'invoke' AJAX command issued with correct method and argument");
// Tests the 'html' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX html: Replace the HTML in a selector.")));
$expected = array(
'command' => 'insert',
'method' => 'html',
'data' => 'replacement text',
);
$this->assertCommand($commands, $expected, "'html' AJAX command issued with correct data");
// Tests the 'insert' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX insert: Let client insert based on #ajax['method'].")));
$expected = array(
'command' => 'insert',
'data' => 'insert replacement text',
);
$this->assertCommand($commands, $expected, "'insert' AJAX command issued with correct data");
// Tests the 'prepend' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'prepend': Click to prepend something")));
$expected = array(
'command' => 'insert',
'method' => 'prepend',
'data' => 'prepended text',
);
$this->assertCommand($commands, $expected, "'prepend' AJAX command issued with correct data");
// Tests the 'remove' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'remove': Click to remove text")));
$expected = array(
'command' => 'remove',
'selector' => '#remove_text',
);
$this->assertCommand($commands, $expected, "'remove' AJAX command issued with correct command and selector");
// Tests the 'restripe' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command")));
$expected = array(
'command' => 'restripe',
'selector' => '#restripe_table',
);
$this->assertCommand($commands, $expected, "'restripe' AJAX command issued with correct selector");
// Tests the 'settings' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'settings' command")));
$expected = array(
'command' => 'settings',
'settings' => array('ajax_forms_test' => array('foo' => 42)),
);
$this->assertCommand($commands, $expected, "'settings' AJAX command issued with correct data");
// Tests the 'add_css' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'add_css' command")));
$expected = array(
'command' => 'add_css',
'data' => 'my/file.css',
);
$this->assertCommand($commands, $expected, "'add_css' AJAX command issued with correct data");
}
}
/**
* Test that $form_state['values'] is properly delivered to $ajax['callback'].
*/
class AJAXFormValuesTestCase extends AJAXTestCase {
public static function getInfo() {
return array(
'name' => 'AJAX command form values',
'description' => 'Tests that form values are properly delivered to AJAX callbacks.',
'group' => 'AJAX',
);
}
function setUp() {
parent::setUp();
$this->web_user = $this->drupalCreateUser(array('access content'));
$this->drupalLogin($this->web_user);
}
/**
* Create a simple form, then POST to system/ajax to change to it.
*/
function testSimpleAJAXFormValue() {
// Verify form values of a select element.
foreach (array('red', 'green', 'blue') as $item) {
$edit = array(
'select' => $item,
);
$commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select');
$expected = array(
'command' => 'data',
'value' => $item,
);
$this->assertCommand($commands, $expected, "verification of AJAX form values from a selectbox issued with a correct value");
}
// Verify form values of a checkbox element.
foreach (array(FALSE, TRUE) as $item) {
$edit = array(
'checkbox' => $item,
);
$commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox');
$expected = array(
'command' => 'data',
'value' => (int) $item,
);
$this->assertCommand($commands, $expected, "verification of AJAX form values from a checkbox issued with a correct value");
}
}
}
/**
* Tests that Ajax-enabled forms work when multiple instances of the same form are on a page.
*/
class AJAXMultiFormTestCase extends AJAXTestCase {
public static function getInfo() {
return array(
'name' => 'AJAX multi form',
'description' => 'Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.',
'group' => 'AJAX',
);
}
function setUp() {
parent::setUp(array('form_test'));
// Create a multi-valued field for 'page' nodes to use for Ajax testing.
$field_name = 'field_ajax_test';
$field = array(
'field_name' => $field_name,
'type' => 'text',
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
);
field_create_field($field);
$instance = array(
'field_name' => $field_name,
'entity_type' => 'node',
'bundle' => 'page',
);
field_create_instance($instance);
// Login a user who can create 'page' nodes.
$this->web_user = $this->drupalCreateUser(array('create page content'));
$this->drupalLogin($this->web_user);
}
/**
* Test that a page with the 'page_node_form' included twice works correctly.
*/
function testMultiForm() {
// HTML IDs for elements within the field are potentially modified with
// each Ajax submission, but these variables are stable and help target the
// desired elements.
$field_name = 'field_ajax_test';
$field_xpaths = array(
'page-node-form' => '//form[@id="page-node-form"]//div[contains(@class, "field-name-field-ajax-test")]',
'page-node-form--2' => '//form[@id="page-node-form--2"]//div[contains(@class, "field-name-field-ajax-test")]',
);
$button_name = $field_name . '_add_more';
$button_value = t('Add another item');
$button_xpath_suffix = '//input[@name="' . $button_name . '"]';
$field_items_xpath_suffix = '//input[@type="text"]';
// Ensure the initial page contains both node forms and the correct number
// of field items and "add more" button for the multi-valued field within
// each form.
$this->drupalGet('form-test/two-instances-of-same-form');
foreach ($field_xpaths as $form_html_id => $field_xpath) {
$this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == 1, t('Found the correct number of field items on the initial page.'));
$this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button on the initial page.'));
}
$this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other');
// Submit the "add more" button of each form twice. After each corresponding
// page update, ensure the same as above.
foreach ($field_xpaths as $form_html_id => $field_xpath) {
for ($i = 0; $i < 2; $i++) {
$this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_html_id);
$this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == $i+2, t('Found the correct number of field items after an AJAX submission.'));
$this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button after an AJAX submission.'));
$this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other');
}
}
}
}
/**
* Test Ajax forms when page caching for anonymous users is turned on.
*/
class AJAXFormPageCacheTestCase extends AJAXTestCase {
protected $profile = 'testing';
public static function getInfo() {
return array(
'name' => 'AJAX forms on cached pages',
'description' => 'Tests that AJAX forms work properly for anonymous users on cached pages.',
'group' => 'AJAX',
);
}
public function setUp() {
parent::setUp();
variable_set('cache', TRUE);
}
/**
* Return the build id of the current form.
*/
protected function getFormBuildId() {
$build_id_fields = $this->xpath('//input[@name="form_build_id"]');
$this->assertEqual(count($build_id_fields), 1, 'One form build id field on the page');
return (string) $build_id_fields[0]['value'];
}
/**
* Create a simple form, then POST to system/ajax to change to it.
*/
public function testSimpleAJAXFormValue() {
$this->drupalGet('ajax_forms_test_get_form');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$build_id_initial = $this->getFormBuildId();
$edit = array('select' => 'green');
$commands = $this->drupalPostAJAX(NULL, $edit, 'select');
$build_id_first_ajax = $this->getFormBuildId();
$this->assertNotEqual($build_id_initial, $build_id_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
$expected = array(
'command' => 'updateBuildId',
'old' => $build_id_initial,
'new' => $build_id_first_ajax,
);
$this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');
$edit = array('select' => 'red');
$commands = $this->drupalPostAJAX(NULL, $edit, 'select');
$build_id_second_ajax = $this->getFormBuildId();
$this->assertEqual($build_id_first_ajax, $build_id_second_ajax, 'Build id remains the same on subsequent AJAX submissions');
// Repeat the test sequence but this time with a page loaded from the cache.
$this->drupalGet('ajax_forms_test_get_form');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$build_id_from_cache_initial = $this->getFormBuildId();
$this->assertEqual($build_id_initial, $build_id_from_cache_initial, 'Build id is the same as on the first request');
$edit = array('select' => 'green');
$commands = $this->drupalPostAJAX(NULL, $edit, 'select');
$build_id_from_cache_first_ajax = $this->getFormBuildId();
$this->assertNotEqual($build_id_from_cache_initial, $build_id_from_cache_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
$this->assertNotEqual($build_id_first_ajax, $build_id_from_cache_first_ajax, 'Build id from first user is not reused');
$expected = array(
'command' => 'updateBuildId',
'old' => $build_id_from_cache_initial,
'new' => $build_id_from_cache_first_ajax,
);
$this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');
$edit = array('select' => 'red');
$commands = $this->drupalPostAJAX(NULL, $edit, 'select');
$build_id_from_cache_second_ajax = $this->getFormBuildId();
$this->assertEqual($build_id_from_cache_first_ajax, $build_id_from_cache_second_ajax, 'Build id remains the same on subsequent AJAX submissions');
}
}
/**
* Miscellaneous Ajax tests using ajax_test module.
*/
class AJAXElementValidation extends AJAXTestCase {
public static function getInfo() {
return array(
'name' => 'Miscellaneous AJAX tests',
'description' => 'Various tests of AJAX behavior',
'group' => 'AJAX',
);
}
/**
* Try to post an Ajax change to a form that has a validated element.
*
* The drivertext field is Ajax-enabled. An additional field is not, but
* is set to be a required field. In this test the required field is not
* filled in, and we want to see if the activation of the "drivertext"
* Ajax-enabled field fails due to the required field being empty.
*/
function testAJAXElementValidation() {
$web_user = $this->drupalCreateUser();
$edit = array('drivertext' => t('some dumb text'));
// Post with 'drivertext' as the triggering element.
$post_result = $this->drupalPostAJAX('ajax_validation_test', $edit, 'drivertext');
// Look for a validation failure in the resultant JSON.
$this->assertNoText(t('Error message'), "No error message in resultant JSON");
$this->assertText('ajax_forms_test_validation_form_callback invoked', 'The correct callback was invoked');
}
}

View file

@ -0,0 +1,12 @@
name = "AJAX form test mock module"
description = "Test for AJAX form calls."
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,520 @@
<?php
/**
* @file
* Simpletest mock module for Ajax forms testing.
*/
/**
* Implements hook_menu().
*/
function ajax_forms_test_menu() {
$items = array();
$items['ajax_forms_test_get_form'] = array(
'title' => 'AJAX forms simple form test',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_forms_test_simple_form'),
'access callback' => TRUE,
);
$items['ajax_forms_test_ajax_commands_form'] = array(
'title' => 'AJAX forms AJAX commands test',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_forms_test_ajax_commands_form'),
'access callback' => TRUE,
);
$items['ajax_validation_test'] = array(
'title' => 'AJAX Validation Test',
'page callback' => 'drupal_get_form',
'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;
}
/**
* A basic form used to test form_state['values'] during callback.
*/
function ajax_forms_test_simple_form($form, &$form_state) {
$form = array();
$form['select'] = array(
'#type' => 'select',
'#options' => array(
'red' => 'red',
'green' => 'green',
'blue' => 'blue'),
'#ajax' => array(
'callback' => 'ajax_forms_test_simple_form_select_callback',
),
'#suffix' => '<div id="ajax_selected_color">No color yet selected</div>',
);
$form['checkbox'] = array(
'#type' => 'checkbox',
'#title' => t('Test checkbox'),
'#ajax' => array(
'callback' => 'ajax_forms_test_simple_form_checkbox_callback',
),
'#suffix' => '<div id="ajax_checkbox_value">No action yet</div>',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('submit'),
);
return $form;
}
/**
* Ajax callback triggered by select.
*/
function ajax_forms_test_simple_form_select_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_html('#ajax_selected_color', $form_state['values']['select']);
$commands[] = ajax_command_data('#ajax_selected_color', 'form_state_value_select', $form_state['values']['select']);
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback triggered by checkbox.
*/
function ajax_forms_test_simple_form_checkbox_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_html('#ajax_checkbox_value', (int) $form_state['values']['checkbox']);
$commands[] = ajax_command_data('#ajax_checkbox_value', 'form_state_value_select', (int) $form_state['values']['checkbox']);
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Form to display the Ajax Commands.
*/
function ajax_forms_test_ajax_commands_form($form, &$form_state) {
$form = array();
// Shows the 'after' command with a callback generating commands.
$form['after_command_example'] = array(
'#value' => t("AJAX 'After': Click to put something after the div"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_after_callback',
),
'#suffix' => '<div id="after_div">Something can be inserted after this</div>',
);
// Shows the 'alert' command.
$form['alert_command_example'] = array(
'#value' => t("AJAX 'Alert': Click to alert"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_alert_callback',
),
);
// Shows the 'append' command.
$form['append_command_example'] = array(
'#value' => t("AJAX 'Append': Click to append something"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_append_callback',
),
'#suffix' => '<div id="append_div">Append inside this div</div>',
);
// Shows the 'before' command.
$form['before_command_example'] = array(
'#value' => t("AJAX 'before': Click to put something before the div"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_before_callback',
),
'#suffix' => '<div id="before_div">Insert something before this.</div>',
);
// Shows the 'changed' command without asterisk.
$form['changed_command_example'] = array(
'#value' => t("AJAX changed: Click to mark div changed."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_changed_callback',
),
'#suffix' => '<div id="changed_div"> <div id="changed_div_mark_this">This div can be marked as changed or not.</div></div>',
);
// Shows the 'changed' command adding the asterisk.
$form['changed_command_asterisk_example'] = array(
'#value' => t("AJAX changed: Click to mark div changed with asterisk."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_changed_asterisk_callback',
),
);
// Shows the Ajax 'css' command.
$form['css_command_example'] = array(
'#value' => t("Set the '#box' div to be blue."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_css_callback',
),
'#suffix' => '<div id="css_div" style="height: 50px; width: 50px; border: 1px solid black"> box</div>',
);
// Shows the Ajax 'data' command. But there is no use of this information,
// as this would require a javascript client to use the data.
$form['data_command_example'] = array(
'#value' => t("AJAX data command: Issue command."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_data_callback',
),
'#suffix' => '<div id="data_div">Data attached to this div.</div>',
);
// Shows the Ajax 'invoke' command.
$form['invoke_command_example'] = array(
'#value' => t("AJAX invoke command: Invoke addClass() method."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_invoke_callback',
),
'#suffix' => '<div id="invoke_div">Original contents</div>',
);
// Shows the Ajax 'html' command.
$form['html_command_example'] = array(
'#value' => t("AJAX html: Replace the HTML in a selector."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_html_callback',
),
'#suffix' => '<div id="html_div">Original contents</div>',
);
// Shows the Ajax 'insert' command.
$form['insert_command_example'] = array(
'#value' => t("AJAX insert: Let client insert based on #ajax['method']."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_insert_callback',
'method' => 'prepend',
),
'#suffix' => '<div id="insert_div">Original contents</div>',
);
// Shows the Ajax 'prepend' command.
$form['prepend_command_example'] = array(
'#value' => t("AJAX 'prepend': Click to prepend something"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_prepend_callback',
),
'#suffix' => '<div id="prepend_div">Something will be prepended to this div. </div>',
);
// Shows the Ajax 'remove' command.
$form['remove_command_example'] = array(
'#value' => t("AJAX 'remove': Click to remove text"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_remove_callback',
),
'#suffix' => '<div id="remove_div"><div id="remove_text">text to be removed</div></div>',
);
// Shows the Ajax 'restripe' command.
$form['restripe_command_example'] = array(
'#type' => 'submit',
'#value' => t("AJAX 'restripe' command"),
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_restripe_callback',
),
'#suffix' => '<div id="restripe_div">
<table id="restripe_table" style="border: 1px solid black" >
<tr id="table-first"><td>first row</td></tr>
<tr ><td>second row</td></tr>
</table>
</div>',
);
// Demonstrates the Ajax 'settings' command. The 'settings' command has
// nothing visual to "show", but it can be tested via SimpleTest and via
// Firebug.
$form['settings_command_example'] = array(
'#type' => 'submit',
'#value' => t("AJAX 'settings' command"),
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_settings_callback',
),
);
// Shows the Ajax 'add_css' command.
$form['add_css_command_example'] = array(
'#type' => 'submit',
'#value' => t("AJAX 'add_css' command"),
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_add_css_callback',
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Ajax callback for 'after'.
*/
function ajax_forms_test_advanced_commands_after_callback($form, $form_state) {
$selector = '#after_div';
$commands = array();
$commands[] = ajax_command_after($selector, "This will be placed after");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'alert'.
*/
function ajax_forms_test_advanced_commands_alert_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_alert("Alert");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'append'.
*/
function ajax_forms_test_advanced_commands_append_callback($form, $form_state) {
$selector = '#append_div';
$commands = array();
$commands[] = ajax_command_append($selector, "Appended text");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'before'.
*/
function ajax_forms_test_advanced_commands_before_callback($form, $form_state) {
$selector = '#before_div';
$commands = array();
$commands[] = ajax_command_before($selector, "Before text");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'changed'.
*/
function ajax_forms_test_advanced_commands_changed_callback($form, $form_state) {
$commands[] = ajax_command_changed('#changed_div');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'changed' with asterisk marking inner div.
*/
function ajax_forms_test_advanced_commands_changed_asterisk_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_changed('#changed_div', '#changed_div_mark_this');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'css'.
*/
function ajax_forms_test_advanced_commands_css_callback($form, $form_state) {
$selector = '#css_div';
$color = 'blue';
$commands = array();
$commands[] = ajax_command_css($selector, array('background-color' => $color));
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'data'.
*/
function ajax_forms_test_advanced_commands_data_callback($form, $form_state) {
$selector = '#data_div';
$commands = array();
$commands[] = ajax_command_data($selector, 'testkey', 'testvalue');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'invoke'.
*/
function ajax_forms_test_advanced_commands_invoke_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_invoke('#invoke_div', 'addClass', array('error'));
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'html'.
*/
function ajax_forms_test_advanced_commands_html_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_html('#html_div', 'replacement text');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'insert'.
*/
function ajax_forms_test_advanced_commands_insert_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_insert('#insert_div', 'insert replacement text');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'prepend'.
*/
function ajax_forms_test_advanced_commands_prepend_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_prepend('#prepend_div', "prepended text");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'remove'.
*/
function ajax_forms_test_advanced_commands_remove_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_remove('#remove_text');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'restripe'.
*/
function ajax_forms_test_advanced_commands_restripe_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_restripe('#restripe_table');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'settings'.
*/
function ajax_forms_test_advanced_commands_settings_callback($form, $form_state) {
$commands = array();
$setting['ajax_forms_test']['foo'] = 42;
$commands[] = ajax_command_settings($setting);
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'add_css'.
*/
function ajax_forms_test_advanced_commands_add_css_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_add_css('my/file.css');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* This form and its related submit and callback functions demonstrate
* not validating another form element when a single Ajax element is triggered.
*
* The "drivertext" element is an Ajax-enabled textfield, free-form.
* The "required_field" element is a textfield marked required.
*
* The correct behavior is that the Ajax-enabled drivertext element should
* be able to trigger without causing validation of the "required_field".
*/
function ajax_forms_test_validation_form($form, &$form_state) {
$form['drivertext'] = array(
'#title' => t('AJAX-enabled textfield.'),
'#description' => t("When this one AJAX-triggers and the spare required field is empty, you should not get an error."),
'#type' => 'textfield',
'#default_value' => !empty($form_state['values']['drivertext']) ? $form_state['values']['drivertext'] : "",
'#ajax' => array(
'callback' => 'ajax_forms_test_validation_form_callback',
'wrapper' => 'message_area',
'method' => 'replace',
),
'#suffix' => '<div id="message_area"></div>',
);
$form['spare_required_field'] = array(
'#title' => t("Spare Required Field"),
'#type' => 'textfield',
'#required' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Submit handler for the validation form.
*/
function ajax_forms_test_validation_form_submit($form, $form_state) {
drupal_set_message(t("Validation form submitted"));
}
/**
* Ajax callback for the 'drivertext' element of the validation form.
*/
function ajax_forms_test_validation_form_callback($form, $form_state) {
drupal_set_message("ajax_forms_test_validation_form_callback invoked");
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['add_files'] = array(
'#type' => 'checkbox',
'#default_value' => FALSE,
);
$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) {
if ($form_state['values']['add_files']) {
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;
}

View file

@ -0,0 +1,12 @@
name = AJAX Test
description = Support module for AJAX framework tests.
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,79 @@
<?php
/**
* @file
* Helper module for Ajax framework tests.
*/
/**
* Implements hook_menu().
*/
function ajax_test_menu() {
$items['ajax-test/render'] = array(
'title' => 'ajax_render',
'page callback' => 'ajax_test_render',
'delivery callback' => 'ajax_deliver',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['ajax-test/render-error'] = array(
'title' => 'ajax_render_error',
'page callback' => 'ajax_test_error',
'delivery callback' => 'ajax_deliver',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['ajax-test/link'] = array(
'title' => 'AJAX Link',
'page callback' => 'ajax_test_link',
'access callback' => TRUE,
);
return $items;
}
/**
* Implements hook_system_theme_info().
*/
function ajax_test_system_theme_info() {
$themes['test_theme'] = drupal_get_path('module', 'ajax_test') . '/themes/test_theme/test_theme.info';
return $themes;
}
/**
* Menu callback; Return an element suitable for use by ajax_deliver().
*
* Additionally ensures that ajax_render() incorporates JavaScript settings
* generated during the page request by invoking drupal_add_js() with a dummy
* setting.
*/
function ajax_test_render() {
drupal_add_js(array('ajax' => 'test'), 'setting');
return array('#type' => 'ajax', '#commands' => array());
}
/**
* Menu callback; Returns Ajax element with #error property set.
*/
function ajax_test_error() {
$message = '';
if (!empty($_GET['message'])) {
$message = $_GET['message'];
}
return array('#type' => 'ajax', '#error' => $message);
}
/**
* Menu callback; Renders a #type link with #ajax.
*/
function ajax_test_link() {
$build['link'] = array(
'#type' => 'link',
'#title' => 'Show help',
'#href' => 'filter/tips',
'#ajax' => array(
'wrapper' => 'block-system-main',
),
);
return $build;
}

View file

@ -0,0 +1,403 @@
<?php
/**
* @file
* Tests for the Batch API.
*/
/**
* Tests for the Batch API.
*/
class BatchProcessingTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Batch processing',
'description' => 'Test batch processing in form and non-form workflow.',
'group' => 'Batch API',
);
}
function setUp() {
parent::setUp('batch_test');
}
/**
* Test batches triggered outside of form submission.
*/
function testBatchNoForm() {
// Displaying the page triggers batch 1.
$this->drupalGet('batch-test/no-form');
$this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Test batches defined in a form submit handler.
*/
function testBatchForm() {
// Batch 0: no operation.
$edit = array('batch' => 'batch_0');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_0'), t('Batch with no operation performed successfully.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
// Batch 1: several simple operations.
$edit = array('batch' => 'batch_1');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch with simple operations performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
// Batch 2: one multistep operation.
$edit = array('batch' => 'batch_2');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch with multistep operation performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
// Batch 3: simple + multistep combined.
$edit = array('batch' => 'batch_3');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_3'), t('Batch with simple and multistep operations performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_3'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
// Batch 4: nested batch.
$edit = array('batch' => 'batch_4');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_4'), t('Nested batch performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_4'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Test batches defined in a multistep form.
*/
function testBatchFormMultistep() {
$this->drupalGet('batch-test/multistep');
$this->assertText('step 1', t('Form is displayed in step 1.'));
// First step triggers batch 1.
$this->drupalPost(NULL, array(), 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch for step 1 performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
$this->assertText('step 2', t('Form is displayed in step 2.'));
// Second step triggers batch 2.
$this->drupalPost(NULL, array(), 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch for step 2 performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Test batches defined in different submit handlers on the same form.
*/
function testBatchFormMultipleBatches() {
// Batches 1, 2 and 3 are triggered in sequence by different submit
// handlers. Each submit handler modify the submitted 'value'.
$value = rand(0, 255);
$edit = array('value' => $value);
$this->drupalPost('batch-test/chained', $edit, 'Submit');
// Check that result messages are present and in the correct order.
$this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
// The stack contains execution order of batch callbacks and submit
// hanlders and logging of corresponding $form_state[{values'].
$this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Test batches defined in a programmatically submitted form.
*
* Same as above, but the form is submitted through drupal_form_execute().
*/
function testBatchFormProgrammatic() {
// Batches 1, 2 and 3 are triggered in sequence by different submit
// handlers. Each submit handler modify the submitted 'value'.
$value = rand(0, 255);
$this->drupalGet('batch-test/programmatic/' . $value);
// Check that result messages are present and in the correct order.
$this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
// The stack contains execution order of batch callbacks and submit
// hanlders and logging of corresponding $form_state[{values'].
$this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
$this->assertText('Got out of a programmatic batched form.', t('Page execution continues normally.'));
}
/**
* Test that drupal_form_submit() can run within a batch operation.
*/
function testDrupalFormSubmitInBatch() {
// Displaying the page triggers a batch that programmatically submits a
// form.
$value = rand(0, 255);
$this->drupalGet('batch-test/nested-programmatic/' . $value);
$this->assertEqual(batch_test_stack(), array('mock form submitted with value = ' . $value), t('drupal_form_submit() ran successfully within a batch operation.'));
}
/**
* Test batches that return $context['finished'] > 1 do in fact complete.
* See http://drupal.org/node/600836
*/
function testBatchLargePercentage() {
// Displaying the page triggers batch 5.
$this->drupalGet('batch-test/large-percentage');
$this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_5'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Will trigger a pass if the texts were found in order in the raw content.
*
* @param $texts
* Array of raw strings to look for .
* @param $message
* Message to display.
* @return
* TRUE on pass, FALSE on fail.
*/
function assertBatchMessages($texts, $message) {
$pattern = '|' . implode('.*', $texts) .'|s';
return $this->assertPattern($pattern, $message);
}
/**
* Helper function: return expected execution stacks for the test batches.
*/
function _resultStack($id, $value = 0) {
$stack = array();
switch ($id) {
case 'batch_1':
for ($i = 1; $i <= 10; $i++) {
$stack[] = "op 1 id $i";
}
break;
case 'batch_2':
for ($i = 1; $i <= 10; $i++) {
$stack[] = "op 2 id $i";
}
break;
case 'batch_3':
for ($i = 1; $i <= 5; $i++) {
$stack[] = "op 1 id $i";
}
for ($i = 1; $i <= 5; $i++) {
$stack[] = "op 2 id $i";
}
for ($i = 6; $i <= 10; $i++) {
$stack[] = "op 1 id $i";
}
for ($i = 6; $i <= 10; $i++) {
$stack[] = "op 2 id $i";
}
break;
case 'batch_4':
for ($i = 1; $i <= 5; $i++) {
$stack[] = "op 1 id $i";
}
$stack[] = 'setting up batch 2';
for ($i = 6; $i <= 10; $i++) {
$stack[] = "op 1 id $i";
}
$stack = array_merge($stack, $this->_resultStack('batch_2'));
break;
case 'batch_5':
for ($i = 1; $i <= 10; $i++) {
$stack[] = "op 5 id $i";
}
break;
case 'chained':
$stack[] = 'submit handler 1';
$stack[] = 'value = ' . $value;
$stack = array_merge($stack, $this->_resultStack('batch_1'));
$stack[] = 'submit handler 2';
$stack[] = 'value = ' . ($value + 1);
$stack = array_merge($stack, $this->_resultStack('batch_2'));
$stack[] = 'submit handler 3';
$stack[] = 'value = ' . ($value + 2);
$stack[] = 'submit handler 4';
$stack[] = 'value = ' . ($value + 3);
$stack = array_merge($stack, $this->_resultStack('batch_3'));
break;
}
return $stack;
}
/**
* Helper function: return expected result messages for the test batches.
*/
function _resultMessages($id) {
$messages = array();
switch ($id) {
case 'batch_0':
$messages[] = 'results for batch 0<br />none';
break;
case 'batch_1':
$messages[] = 'results for batch 1<br />op 1: processed 10 elements';
break;
case 'batch_2':
$messages[] = 'results for batch 2<br />op 2: processed 10 elements';
break;
case 'batch_3':
$messages[] = 'results for batch 3<br />op 1: processed 10 elements<br />op 2: processed 10 elements';
break;
case 'batch_4':
$messages[] = 'results for batch 4<br />op 1: processed 10 elements';
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
break;
case 'batch_5':
$messages[] = 'results for batch 5<br />op 1: processed 10 elements. $context[\'finished\'] > 1 returned from batch process, with success.';
break;
case 'chained':
$messages = array_merge($messages, $this->_resultMessages('batch_1'));
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
$messages = array_merge($messages, $this->_resultMessages('batch_3'));
break;
}
return $messages;
}
}
/**
* Tests for the Batch API Progress page.
*/
class BatchPageTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Batch progress page',
'description' => 'Test the content of the progress page.',
'group' => 'Batch API',
);
}
function setUp() {
parent::setUp('batch_test');
}
/**
* Tests that the batch API progress page uses the correct theme.
*/
function testBatchProgressPageTheme() {
// Make sure that the page which starts the batch (an administrative page)
// is using a different theme than would normally be used by the batch API.
variable_set('theme_default', 'bartik');
variable_set('admin_theme', 'seven');
// Log in as an administrator who can see the administrative theme.
$admin_user = $this->drupalCreateUser(array('view the administration theme'));
$this->drupalLogin($admin_user);
// Visit an administrative page that runs a test batch, and check that the
// theme that was used during batch execution (which the batch callback
// function saved as a variable) matches the theme used on the
// administrative page.
$this->drupalGet('admin/batch-test/test-theme');
// The stack should contain the name of the theme used on the progress
// page.
$this->assertEqual(batch_test_stack(), array('seven'), t('A progressive batch correctly uses the theme of the page that started the batch.'));
}
}
/**
* Tests the function _batch_api_percentage() to make sure that the rounding
* works properly in all cases.
*/
class BatchPercentagesUnitTestCase extends DrupalUnitTestCase {
protected $testCases = array();
public static function getInfo() {
return array(
'name' => 'Batch percentages',
'description' => 'Unit tests of progress percentage rounding.',
'group' => 'Batch API',
);
}
function setUp() {
// Set up an array of test cases, where the expected values are the keys,
// and the values are arrays with the keys 'total' and 'current',
// corresponding with the function parameters of _batch_api_percentage().
$this->testCases = array(
// 1/2 is 50%.
'50' => array('total' => 2, 'current' => 1),
// Though we should never encounter a case where the current set is set
// 0, if we did, we should get 0%.
'0' => array('total' => 3, 'current' => 0),
// 1/3 is closer to 33% than to 34%.
'33' => array('total' => 3, 'current' => 1),
// 2/3 is closer to 67% than to 66%.
'67' => array('total' => 3, 'current' => 2),
// 1/199 should round up to 1%.
'1' => array('total' => 199, 'current' => 1),
// 198/199 should round down to 99%.
'99' => array('total' => 199, 'current' => 198),
// 199/200 would have rounded up to 100%, which would give the false
// impression of being finished, so we add another digit and should get
// 99.5%.
'99.5' => array('total' => 200, 'current' => 199),
// The same logic holds for 1/200: we should get 0.5%.
'0.5' => array('total' => 200, 'current' => 1),
// Numbers that come out evenly, such as 50/200, should be forced to have
// extra digits for consistancy.
'25.0' => array('total' => 200, 'current' => 50),
// Regardless of number of digits we're using, 100% should always just be
// 100%.
'100' => array('total' => 200, 'current' => 200),
// 1998/1999 should similarly round down to 99.9%.
'99.9' => array('total' => 1999, 'current' => 1998),
// 1999/2000 should add another digit and go to 99.95%.
'99.95' => array('total' => 2000, 'current' => 1999),
// 19999/20000 should add yet another digit and go to 99.995%.
'99.995' => array('total' => 20000, 'current' => 19999),
// The next five test cases simulate a batch with a single operation
// ('total' equals 1) that takes several steps to complete. Within the
// operation, we imagine that there are 501 items to process, and 100 are
// completed during each step. The percentages we get back should be
// rounded the usual way for the first few passes (i.e., 20%, 40%, etc.),
// but for the last pass through, when 500 out of 501 items have been
// processed, we do not want to round up to 100%, since that would
// erroneously indicate that the processing is complete.
'20' => array('total' => 1, 'current' => 100/501),
'40' => array('total' => 1, 'current' => 200/501),
'60' => array('total' => 1, 'current' => 300/501),
'80' => array('total' => 1, 'current' => 400/501),
'99.8' => array('total' => 1, 'current' => 500/501),
);
require_once DRUPAL_ROOT . '/includes/batch.inc';
parent::setUp();
}
/**
* Test the _batch_api_percentage() function.
*/
function testBatchPercentages() {
foreach ($this->testCases as $expected_result => $arguments) {
// PHP sometimes casts numeric strings that are array keys to integers,
// cast them back here.
$expected_result = (string) $expected_result;
$total = $arguments['total'];
$current = $arguments['current'];
$actual_result = _batch_api_percentage($total, $current);
if ($actual_result === $expected_result) {
$this->pass(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, and got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
}
else {
$this->fail(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, but got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
}
}
}
}

View file

@ -0,0 +1,163 @@
<?php
/**
* @file
* Batch callbacks for the Batch API tests.
*/
/**
* Implements callback_batch_operation().
*
* Simple batch operation.
*/
function _batch_test_callback_1($id, $sleep, &$context) {
// No-op, but ensure the batch take a couple iterations.
// Batch needs time to run for the test, so sleep a bit.
usleep($sleep);
// Track execution, and store some result for post-processing in the
// 'finished' callback.
batch_test_stack("op 1 id $id");
$context['results'][1][] = $id;
}
/**
* Implements callback_batch_operation().
*
* Multistep batch operation.
*/
function _batch_test_callback_2($start, $total, $sleep, &$context) {
// Initialize context with progress information.
if (!isset($context['sandbox']['current'])) {
$context['sandbox']['current'] = $start;
$context['sandbox']['count'] = 0;
}
// Process by groups of 5 (arbitrary value).
$limit = 5;
for ($i = 0; $i < $limit && $context['sandbox']['count'] < $total; $i++) {
// No-op, but ensure the batch take a couple iterations.
// Batch needs time to run for the test, so sleep a bit.
usleep($sleep);
// Track execution, and store some result for post-processing in the
// 'finished' callback.
$id = $context['sandbox']['current'] + $i;
batch_test_stack("op 2 id $id");
$context['results'][2][] = $id;
// Update progress information.
$context['sandbox']['count']++;
}
$context['sandbox']['current'] += $i;
// Inform batch engine about progress.
if ($context['sandbox']['count'] != $total) {
$context['finished'] = $context['sandbox']['count'] / $total;
}
}
/**
* Implements callback_batch_operation().
*
* Simple batch operation.
*/
function _batch_test_callback_5($id, $sleep, &$context) {
// No-op, but ensure the batch take a couple iterations.
// Batch needs time to run for the test, so sleep a bit.
usleep($sleep);
// Track execution, and store some result for post-processing in the
// 'finished' callback.
batch_test_stack("op 5 id $id");
$context['results'][5][] = $id;
// This test is to test finished > 1
$context['finished'] = 3.14;
}
/**
* Implements callback_batch_operation().
*
* Batch operation setting up its own batch.
*/
function _batch_test_nested_batch_callback() {
batch_test_stack('setting up batch 2');
batch_set(_batch_test_batch_2());
}
/**
* Implements callback_batch_finished().
*
* Common 'finished' callbacks for batches 1 to 4.
*/
function _batch_test_finished_helper($batch_id, $success, $results, $operations) {
$messages = array("results for batch $batch_id");
if ($results) {
foreach ($results as $op => $op_results) {
$messages[] = 'op '. $op . ': processed ' . count($op_results) . ' elements';
}
}
else {
$messages[] = 'none';
}
if (!$success) {
// A fatal error occurred during the processing.
$error_operation = reset($operations);
$messages[] = t('An error occurred while processing @op with arguments:<br/>@args', array('@op' => $error_operation[0], '@args' => print_r($error_operation[1], TRUE)));
}
drupal_set_message(implode('<br />', $messages));
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 0.
*/
function _batch_test_finished_0($success, $results, $operations) {
_batch_test_finished_helper(0, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 1.
*/
function _batch_test_finished_1($success, $results, $operations) {
_batch_test_finished_helper(1, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 2.
*/
function _batch_test_finished_2($success, $results, $operations) {
_batch_test_finished_helper(2, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 3.
*/
function _batch_test_finished_3($success, $results, $operations) {
_batch_test_finished_helper(3, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 4.
*/
function _batch_test_finished_4($success, $results, $operations) {
_batch_test_finished_helper(4, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 5.
*/
function _batch_test_finished_5($success, $results, $operations) {
_batch_test_finished_helper(5, $success, $results, $operations);
}

View file

@ -0,0 +1,12 @@
name = "Batch API test"
description = "Support module for Batch API tests."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,513 @@
<?php
/**
* @file
* Helper module for the Batch API tests.
*/
/**
* Implement hook_menu().
*/
function batch_test_menu() {
$items = array();
$items['batch-test'] = array(
'title' => 'Batch test',
'page callback' => 'drupal_get_form',
'page arguments' => array('batch_test_simple_form'),
'access callback' => TRUE,
);
// Simple form: one submit handler, setting a batch.
$items['batch-test/simple'] = array(
'title' => 'Simple',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => 0,
);
// Multistep form: two steps, each setting a batch.
$items['batch-test/multistep'] = array(
'title' => 'Multistep',
'page callback' => 'drupal_get_form',
'page arguments' => array('batch_test_multistep_form'),
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
// Chained form: four submit handlers, several of which set a batch.
$items['batch-test/chained'] = array(
'title' => 'Chained',
'page callback' => 'drupal_get_form',
'page arguments' => array('batch_test_chained_form'),
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 2,
);
// Programmatic form: the page submits the 'Chained' form through
// drupal_form_submit().
$items['batch-test/programmatic'] = array(
'title' => 'Programmatic',
'page callback' => 'batch_test_programmatic',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 3,
);
// No form: fire a batch simply by accessing a page.
$items['batch-test/no-form'] = array(
'title' => 'Simple page',
'page callback' => 'batch_test_no_form',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 4,
);
// No form: fire a batch; return > 100% complete
$items['batch-test/large-percentage'] = array(
'title' => 'Simple page with batch over 100% complete',
'page callback' => 'batch_test_large_percentage',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 5,
);
// Tests programmatic form submission within a batch operation.
$items['batch-test/nested-programmatic'] = array(
'title' => 'Nested programmatic',
'page callback' => 'batch_test_nested_drupal_form_submit',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 6,
);
// Landing page to test redirects.
$items['batch-test/redirect'] = array(
'title' => 'Redirect',
'page callback' => 'batch_test_redirect_page',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 7,
);
// This item lives under 'admin' so that the page uses the admin theme.
$items['admin/batch-test/test-theme'] = array(
'page callback' => 'batch_test_theme_batch',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Simple form.
*/
function batch_test_simple_form() {
$form['batch'] = array(
'#type' => 'select',
'#title' => 'Choose batch',
'#options' => array(
'batch_0' => 'batch 0',
'batch_1' => 'batch 1',
'batch_2' => 'batch 2',
'batch_3' => 'batch 3',
'batch_4' => 'batch 4',
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Submit handler for the simple form.
*/
function batch_test_simple_form_submit($form, &$form_state) {
batch_test_stack(NULL, TRUE);
$function = '_batch_test_' . $form_state['values']['batch'];
batch_set($function());
$form_state['redirect'] = 'batch-test/redirect';
}
/**
* Multistep form.
*/
function batch_test_multistep_form($form, &$form_state) {
if (empty($form_state['storage']['step'])) {
$form_state['storage']['step'] = 1;
}
$form['step_display'] = array(
'#markup' => 'step ' . $form_state['storage']['step'] . '<br/>',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Submit handler for the multistep form.
*/
function batch_test_multistep_form_submit($form, &$form_state) {
batch_test_stack(NULL, TRUE);
switch ($form_state['storage']['step']) {
case 1:
batch_set(_batch_test_batch_1());
break;
case 2:
batch_set(_batch_test_batch_2());
break;
}
if ($form_state['storage']['step'] < 2) {
$form_state['storage']['step']++;
$form_state['rebuild'] = TRUE;
}
// This will only be effective on the last step.
$form_state['redirect'] = 'batch-test/redirect';
}
/**
* Form with chained submit callbacks.
*/
function batch_test_chained_form() {
// This value is used to test that $form_state persists through batched
// submit handlers.
$form['value'] = array(
'#type' => 'textfield',
'#title' => 'Value',
'#default_value' => 1,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
$form['#submit'] = array(
'batch_test_chained_form_submit_1',
'batch_test_chained_form_submit_2',
'batch_test_chained_form_submit_3',
'batch_test_chained_form_submit_4',
);
return $form;
}
/**
* Submit handler #1 for the chained form.
*/
function batch_test_chained_form_submit_1($form, &$form_state) {
batch_test_stack(NULL, TRUE);
batch_test_stack('submit handler 1');
batch_test_stack('value = ' . $form_state['values']['value']);
$form_state['values']['value']++;
batch_set(_batch_test_batch_1());
// This redirect should not be taken into account.
$form_state['redirect'] = 'should/be/discarded';
}
/**
* Submit handler #2 for the chained form.
*/
function batch_test_chained_form_submit_2($form, &$form_state) {
batch_test_stack('submit handler 2');
batch_test_stack('value = ' . $form_state['values']['value']);
$form_state['values']['value']++;
batch_set(_batch_test_batch_2());
// This redirect should not be taken into account.
$form_state['redirect'] = 'should/be/discarded';
}
/**
* Submit handler #3 for the chained form.
*/
function batch_test_chained_form_submit_3($form, &$form_state) {
batch_test_stack('submit handler 3');
batch_test_stack('value = ' . $form_state['values']['value']);
$form_state['values']['value']++;
// This redirect should not be taken into account.
$form_state['redirect'] = 'should/be/discarded';
}
/**
* Submit handler #4 for the chained form.
*/
function batch_test_chained_form_submit_4($form, &$form_state) {
batch_test_stack('submit handler 4');
batch_test_stack('value = ' . $form_state['values']['value']);
$form_state['values']['value']++;
batch_set(_batch_test_batch_3());
// This is the redirect that should prevail.
$form_state['redirect'] = 'batch-test/redirect';
}
/**
* Menu callback: programmatically submits the 'Chained' form.
*/
function batch_test_programmatic($value = 1) {
$form_state = array(
'values' => array('value' => $value)
);
drupal_form_submit('batch_test_chained_form', $form_state);
return 'Got out of a programmatic batched form.';
}
/**
* Menu callback: programmatically submits a form within a batch.
*/
function batch_test_nested_drupal_form_submit($value = 1) {
// Set the batch and process it.
$batch['operations'] = array(
array('_batch_test_nested_drupal_form_submit_callback', array($value)),
);
batch_set($batch);
batch_process('batch-test/redirect');
}
/**
* Batch operation: submits form_test_mock_form using drupal_form_submit().
*/
function _batch_test_nested_drupal_form_submit_callback($value) {
$state['values']['test_value'] = $value;
drupal_form_submit('batch_test_mock_form', $state);
}
/**
* A simple form with a textfield and submit button.
*/
function batch_test_mock_form($form, $form_state) {
$form['test_value'] = array(
'#type' => 'textfield',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Submit handler for the batch_test_mock form.
*/
function batch_test_mock_form_submit($form, &$form_state) {
batch_test_stack('mock form submitted with value = ' . $form_state['values']['test_value']);
}
/**
* Menu callback: fire a batch process without a form submission.
*/
function batch_test_no_form() {
batch_test_stack(NULL, TRUE);
batch_set(_batch_test_batch_1());
batch_process('batch-test/redirect');
}
/**
* Menu callback: fire a batch process without a form submission.
*/
function batch_test_large_percentage() {
batch_test_stack(NULL, TRUE);
batch_set(_batch_test_batch_5());
batch_process('batch-test/redirect');
}
/**
* Menu callback: successful redirection.
*/
function batch_test_redirect_page() {
return 'Redirection successful.';
}
/**
* Batch 0: no operation.
*/
function _batch_test_batch_0() {
$batch = array(
'operations' => array(),
'finished' => '_batch_test_finished_0',
'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 1: repeats a simple operation.
*
* Operations: op 1 from 1 to 10.
*/
function _batch_test_batch_1() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array();
for ($i = 1; $i <= $total; $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_1',
'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 2: single multistep operation.
*
* Operations: op 2 from 1 to 10.
*/
function _batch_test_batch_2() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array(
array('_batch_test_callback_2', array(1, $total, $sleep)),
);
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_2',
'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 3: both single and multistep operations.
*
* Operations:
* - op 1 from 1 to 5,
* - op 2 from 1 to 5,
* - op 1 from 6 to 10,
* - op 2 from 6 to 10.
*/
function _batch_test_batch_3() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array();
for ($i = 1; $i <= round($total / 2); $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$operations[] = array('_batch_test_callback_2', array(1, $total / 2, $sleep));
for ($i = round($total / 2) + 1; $i <= $total; $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$operations[] = array('_batch_test_callback_2', array(6, $total / 2, $sleep));
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_3',
'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 4: batch within a batch.
*
* Operations:
* - op 1 from 1 to 5,
* - set batch 2 (op 2 from 1 to 10, should run at the end)
* - op 1 from 6 to 10,
*/
function _batch_test_batch_4() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array();
for ($i = 1; $i <= round($total / 2); $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$operations[] = array('_batch_test_nested_batch_callback', array());
for ($i = round($total / 2) + 1; $i <= $total; $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_4',
'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 5: repeats a simple operation.
*
* Operations: op 1 from 1 to 10.
*/
function _batch_test_batch_5() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array();
for ($i = 1; $i <= $total; $i++) {
$operations[] = array('_batch_test_callback_5', array($i, $sleep));
}
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_5',
'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Menu callback: run a batch for testing theme used on the progress page.
*/
function batch_test_theme_batch() {
batch_test_stack(NULL, TRUE);
$batch = array(
'operations' => array(
array('_batch_test_theme_callback', array()),
),
);
batch_set($batch);
batch_process('batch-test/redirect');
}
/**
* Batch callback function for testing the theme used on the progress page.
*/
function _batch_test_theme_callback() {
// Because drupalGet() steps through the full progressive batch before
// returning control to the test function, we cannot test that the correct
// theme is being used on the batch processing page by viewing that page
// directly. Instead, we save the theme being used in a variable here, so
// that it can be loaded and inspected in the thread running the test.
global $theme;
batch_test_stack($theme);
}
/**
* Helper function: store or retrieve traced execution data.
*/
function batch_test_stack($data = NULL, $reset = FALSE) {
if ($reset) {
variable_del('batch_test_stack');
}
if (!isset($data)) {
return variable_get('batch_test_stack', array());
}
$stack = variable_get('batch_test_stack', array());
$stack[] = $data;
variable_set('batch_test_stack', $stack);
}

View file

@ -0,0 +1,38 @@
<?php
/**
* Perform early bootstrap tests.
*/
class EarlyBootstrapTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Early bootstrap test',
'description' => 'Confirm that calling module_implements() during early bootstrap does not pollute the module_implements() cache.',
'group' => 'System',
);
}
function setUp() {
parent::setUp('boot_test_1', 'boot_test_2');
}
/**
* Test hook_boot() on both regular and "early exit" pages.
*/
public function testHookBoot() {
$paths = array('', 'early_exit');
foreach ($paths as $path) {
// Empty the module_implements() caches.
module_implements(NULL, FALSE, TRUE);
// Do a request to the front page, which will call module_implements()
// during hook_boot().
$this->drupalGet($path);
// Reset the static cache so we get implementation data from the persistent
// cache.
drupal_static_reset();
// Make sure we get a full list of all modules implementing hook_help().
$modules = module_implements('help');
$this->assertTrue(in_array('boot_test_2', $modules));
}
}
}

View file

@ -0,0 +1,12 @@
name = Early bootstrap tests
description = A support module for hook_boot testing.
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,21 @@
<?php
/**
* @file
* Tests calling module_implements() during hook_boot() invocation.
*/
/**
* Implements hook_boot().
*/
function boot_test_1_boot() {
// Calling module_implements during hook_boot() will return "vital" modules
// only, and this list of modules will be statically cached.
module_implements('help');
// Define a special path to test that the static cache isn't written away
// if we exit before having completed the bootstrap.
if ($_GET['q'] == 'early_exit') {
module_implements_write_cache();
exit();
}
}

View file

@ -0,0 +1,12 @@
name = Early bootstrap tests
description = A support module for hook_boot hook testing.
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,13 @@
<?php
/**
* @file
* Defines a hook_help() implementation in a non-"bootstrap" module.
*/
/**
* Implements hook_help().
*/
function boot_test_2_help($path, $arg) {
// Empty hook.
}

View file

@ -0,0 +1,878 @@
<?php
class BootstrapIPAddressTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'IP address and HTTP_HOST test',
'description' => 'Get the IP address from the current visitor from the server variables, check hostname validation.',
'group' => 'Bootstrap'
);
}
function setUp() {
$this->oldserver = $_SERVER;
$this->remote_ip = '127.0.0.1';
$this->proxy_ip = '127.0.0.2';
$this->proxy2_ip = '127.0.0.3';
$this->forwarded_ip = '127.0.0.4';
$this->cluster_ip = '127.0.0.5';
$this->untrusted_ip = '0.0.0.0';
drupal_static_reset('ip_address');
$_SERVER['REMOTE_ADDR'] = $this->remote_ip;
unset($_SERVER['HTTP_X_FORWARDED_FOR']);
unset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']);
parent::setUp();
}
function tearDown() {
$_SERVER = $this->oldserver;
drupal_static_reset('ip_address');
parent::tearDown();
}
/**
* test IP Address and hostname
*/
function testIPAddressHost() {
// Test the normal IP address.
$this->assertTrue(
ip_address() == $this->remote_ip,
'Got remote IP address.'
);
// Proxy forwarding on but no proxy addresses defined.
variable_set('reverse_proxy', 1);
$this->assertTrue(
ip_address() == $this->remote_ip,
'Proxy forwarding without trusted proxies got remote IP address.'
);
// Proxy forwarding on and proxy address not trusted.
variable_set('reverse_proxy_addresses', array($this->proxy_ip, $this->proxy2_ip));
drupal_static_reset('ip_address');
$_SERVER['REMOTE_ADDR'] = $this->untrusted_ip;
$this->assertTrue(
ip_address() == $this->untrusted_ip,
'Proxy forwarding with untrusted proxy got remote IP address.'
);
// Proxy forwarding on and proxy address trusted.
$_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
$_SERVER['HTTP_X_FORWARDED_FOR'] = $this->forwarded_ip;
drupal_static_reset('ip_address');
$this->assertTrue(
ip_address() == $this->forwarded_ip,
'Proxy forwarding with trusted proxy got forwarded IP address.'
);
// Proxy forwarding on and proxy address trusted and visiting from proxy.
$_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
$_SERVER['HTTP_X_FORWARDED_FOR'] = $this->proxy_ip;
drupal_static_reset('ip_address');
$this->assertTrue(
ip_address() == $this->proxy_ip,
'Visiting from trusted proxy got proxy IP address.'
);
// Multi-tier architecture with comma separated values in header.
$_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
$_SERVER['HTTP_X_FORWARDED_FOR'] = implode(', ', array($this->untrusted_ip, $this->forwarded_ip, $this->proxy2_ip));
drupal_static_reset('ip_address');
$this->assertTrue(
ip_address() == $this->forwarded_ip,
'Proxy forwarding with trusted 2-tier proxy got forwarded IP address.'
);
// Custom client-IP header.
variable_set('reverse_proxy_header', 'HTTP_X_CLUSTER_CLIENT_IP');
$_SERVER['HTTP_X_CLUSTER_CLIENT_IP'] = $this->cluster_ip;
drupal_static_reset('ip_address');
$this->assertTrue(
ip_address() == $this->cluster_ip,
'Cluster environment got cluster client IP.'
);
// Verifies that drupal_valid_http_host() prevents invalid characters.
$this->assertFalse(drupal_valid_http_host('security/.drupal.org:80'), 'HTTP_HOST with / is invalid');
$this->assertFalse(drupal_valid_http_host('security\\.drupal.org:80'), 'HTTP_HOST with \\ is invalid');
$this->assertFalse(drupal_valid_http_host('security<.drupal.org:80'), 'HTTP_HOST with &lt; is invalid');
$this->assertFalse(drupal_valid_http_host('security..drupal.org:80'), 'HTTP_HOST with .. is invalid');
// Verifies that host names are shorter than 1000 characters.
$this->assertFalse(drupal_valid_http_host(str_repeat('x', 1001)), 'HTTP_HOST with more than 1000 characters is invalid.');
$this->assertFalse(drupal_valid_http_host(str_repeat('.', 101)), 'HTTP_HOST with more than 100 subdomains is invalid.');
$this->assertFalse(drupal_valid_http_host(str_repeat(':', 101)), 'HTTP_HOST with more than 100 portseparators is invalid.');
// IPv6 loopback address
$this->assertTrue(drupal_valid_http_host('[::1]:80'), 'HTTP_HOST containing IPv6 loopback is valid');
}
}
class BootstrapPageCacheTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Page cache test',
'description' => 'Enable the page cache and test it with various HTTP requests.',
'group' => 'Bootstrap'
);
}
function setUp() {
parent::setUp('system_test');
}
/**
* Test support for requests containing If-Modified-Since and If-None-Match headers.
*/
function testConditionalRequests() {
variable_set('cache', 1);
// Fill the cache.
$this->drupalGet('');
$this->drupalHead('');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$etag = $this->drupalGetHeader('ETag');
$last_modified = $this->drupalGetHeader('Last-Modified');
$this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
$this->assertResponse(304, 'Conditional request returned 304 Not Modified.');
$this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC822, strtotime($last_modified)), 'If-None-Match: ' . $etag));
$this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
$this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC850, strtotime($last_modified)), 'If-None-Match: ' . $etag));
$this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
$this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified));
$this->assertResponse(200, 'Conditional request without If-None-Match returned 200 OK.');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC7231, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
$this->assertResponse(200, 'Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$user = $this->drupalCreateUser();
$this->drupalLogin($user);
$this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
$this->assertResponse(200, 'Conditional request returned 200 OK for authenticated user.');
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Absence of Page was not cached.');
$this->assertFalse($this->drupalGetHeader('ETag'), 'ETag HTTP headers are not present for logged in users.');
$this->assertFalse($this->drupalGetHeader('Last-Modified'), 'Last-Modified HTTP headers are not present for logged in users.');
}
/**
* Test cache headers.
*/
function testPageCache() {
variable_set('cache', 1);
// Fill the cache.
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', 'Vary header was sent.');
$this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
// Check cache.
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', 'Vary: Cookie header was sent.');
$this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
// Check replacing default headers.
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT')));
$this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', 'Default header was replaced.');
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent')));
$this->assertEqual($this->drupalGetHeader('Vary'), 'User-Agent,Accept-Encoding', 'Default header was replaced.');
// Check that authenticated users bypass the cache.
$user = $this->drupalCreateUser();
$this->drupalLogin($user);
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Caching was bypassed.');
$this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.');
$this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
}
/**
* Test page compression.
*
* The test should pass even if zlib.output_compression is enabled in php.ini,
* .htaccess or similar, or if compression is done outside PHP, e.g. by the
* mod_deflate Apache module.
*/
function testPageCompression() {
variable_set('cache', 1);
// Fill the cache and verify that output is compressed.
$this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
$this->assertRaw('</html>', 'Page was gzip compressed.');
// Verify that cached output is compressed.
$this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', 'A Content-Encoding header was sent.');
$this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
$this->assertRaw('</html>', 'Page was gzip compressed.');
// Verify that a client without compression support gets an uncompressed page.
$this->drupalGet('');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$this->assertFalse($this->drupalGetHeader('Content-Encoding'), 'A Content-Encoding header was not sent.');
$this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), 'Site title matches.');
$this->assertRaw('</html>', 'Page was not compressed.');
// Disable compression mode.
variable_set('page_compression', FALSE);
// Verify if cached page is still available for a client with compression support.
$this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
$this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
$this->assertRaw('</html>', 'Page was delivered after compression mode is changed (compression support enabled).');
// Verify if cached page is still available for a client without compression support.
$this->drupalGet('');
$this->assertRaw('</html>', 'Page was delivered after compression mode is changed (compression support disabled).');
}
}
class BootstrapVariableTestCase extends DrupalWebTestCase {
function setUp() {
parent::setUp('system_test');
}
public static function getInfo() {
return array(
'name' => 'Variable test',
'description' => 'Make sure the variable system functions correctly.',
'group' => 'Bootstrap'
);
}
/**
* testVariable
*/
function testVariable() {
// Setting and retrieving values.
$variable = $this->randomName();
variable_set('simpletest_bootstrap_variable_test', $variable);
$this->assertIdentical($variable, variable_get('simpletest_bootstrap_variable_test'), 'Setting and retrieving values');
// Make sure the variable persists across multiple requests.
$this->drupalGet('system-test/variable-get');
$this->assertText($variable, 'Variable persists across multiple requests');
// Deleting variables.
$default_value = $this->randomName();
variable_del('simpletest_bootstrap_variable_test');
$variable = variable_get('simpletest_bootstrap_variable_test', $default_value);
$this->assertIdentical($variable, $default_value, 'Deleting variables');
}
/**
* Makes sure that the default variable parameter is passed through okay.
*/
function testVariableDefaults() {
// Tests passing nothing through to the default.
$this->assertIdentical(NULL, variable_get('simpletest_bootstrap_variable_test'), 'Variables are correctly defaulting to NULL.');
// Tests passing 5 to the default parameter.
$this->assertIdentical(5, variable_get('simpletest_bootstrap_variable_test', 5), 'The default variable parameter is passed through correctly.');
}
}
/**
* Tests the auto-loading behavior of the code registry.
*/
class BootstrapAutoloadTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Code registry',
'description' => 'Test that the code registry functions correctly.',
'group' => 'Bootstrap',
);
}
function setUp() {
parent::setUp('drupal_autoload_test');
}
/**
* Tests that autoloader name matching is not case sensitive.
*/
function testAutoloadCase() {
// Test interface autoloader.
$this->assertTrue(drupal_autoload_interface('drupalautoloadtestinterface'), 'drupal_autoload_interface() recognizes <em>DrupalAutoloadTestInterface</em> in lower case.');
// Test class autoloader.
$this->assertTrue(drupal_autoload_class('drupalautoloadtestclass'), 'drupal_autoload_class() recognizes <em>DrupalAutoloadTestClass</em> in lower case.');
// Test trait autoloader.
if (version_compare(PHP_VERSION, '5.4') >= 0) {
$this->assertTrue(drupal_autoload_trait('drupalautoloadtesttrait'), 'drupal_autoload_trait() recognizes <em>DrupalAutoloadTestTrait</em> in lower case.');
}
}
}
/**
* Test hook_boot() and hook_exit().
*/
class HookBootExitTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Boot and exit hook invocation',
'description' => 'Test that hook_boot() and hook_exit() are called correctly.',
'group' => 'Bootstrap',
);
}
function setUp() {
parent::setUp('system_test', 'dblog');
}
/**
* Test calling of hook_boot() and hook_exit().
*/
function testHookBootExit() {
// Test with cache disabled. Boot and exit should always fire.
variable_set('cache', 0);
$this->drupalGet('');
$calls = 1;
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with disabled cache.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with disabled cache.'));
// Test with normal cache. Boot and exit should be called.
variable_set('cache', 1);
$this->drupalGet('');
$calls++;
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with normal cache.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with normal cache.'));
// Boot and exit should not fire since the page is cached.
variable_set('page_cache_invoke_hooks', FALSE);
$this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.'));
$this->drupalGet('');
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot not called with aggressive cache and a cached page.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit not called with aggressive cache and a cached page.'));
// Test with page cache cleared, boot and exit should be called.
$this->assertTrue(db_delete('cache_page')->execute(), t('Page cache cleared.'));
$this->drupalGet('');
$calls++;
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with aggressive cache and no cached page.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with aggressive cache and no cached page.'));
}
}
/**
* Test drupal_get_filename()'s availability.
*/
class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Get filename test (without the system table)',
'description' => 'Test that drupal_get_filename() works correctly when the database is not available.',
'group' => 'Bootstrap',
);
}
/**
* The last file-related error message triggered by the filename test.
*
* Used by BootstrapGetFilenameTestCase::testDrupalGetFilename().
*/
protected $getFilenameTestTriggeredError;
/**
* Test that drupal_get_filename() works correctly when the file is not found in the database.
*/
function testDrupalGetFilename() {
// Reset the static cache so we can test the "db is not active" code of
// drupal_get_filename().
drupal_static_reset('drupal_get_filename');
// Retrieving the location of a module.
$this->assertIdentical(drupal_get_filename('module', 'php'), 'modules/php/php.module', t('Retrieve module location.'));
// Retrieving the location of a theme.
$this->assertIdentical(drupal_get_filename('theme', 'stark'), 'themes/stark/stark.info', t('Retrieve theme location.'));
// 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 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.'));
// When searching for a module that does not exist, drupal_get_filename()
// should return NULL and trigger an appropriate error message.
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$non_existing_module = $this->randomName();
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for an item that does not exist triggers the correct error.');
restore_error_handler();
// Check that the result is stored in the file system scan cache.
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files static variable.');
// Performing the search again in the same request still should not find
// the file, but the error message should not be repeated (therefore we do
// not override the error handler here).
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL during the second search.');
}
/**
* Skips handling of "file not found" errors.
*/
public function fileNotFoundErrorHandler($error_level, $message, $filename, $line, $context) {
// Skip error handling if this is a "file not found" error.
if (strpos($message, 'is missing from the file system:') !== FALSE || strpos($message, 'has moved within the file system:') !== FALSE) {
$this->getFilenameTestTriggeredError = $message;
return;
}
_drupal_error_handler($error_level, $message, $filename, $line, $context);
}
}
/**
* Test drupal_get_filename() in the context of a full Drupal installation.
*/
class BootstrapGetFilenameWebTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Get filename test (full installation)',
'description' => 'Test that drupal_get_filename() works correctly in the context of a full Drupal installation.',
'group' => 'Bootstrap',
);
}
function setUp() {
parent::setUp('system_test');
}
/**
* The last file-related error message triggered by the filename test.
*
* Used by BootstrapGetFilenameWebTestCase::testDrupalGetFilename().
*/
protected $getFilenameTestTriggeredError;
/**
* Test that drupal_get_filename() works correctly with a full Drupal site.
*/
function testDrupalGetFilename() {
// Search for a module that exists in the file system and the {system}
// table and make sure that it is found.
$this->assertIdentical(drupal_get_filename('module', 'node'), 'modules/node/node.module', 'Module found at expected location.');
// Search for a module that does not exist in either the file system or the
// {system} table. Make sure that an appropriate error is triggered and
// that the module winds up in the static and persistent cache.
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$non_existing_module = $this->randomName();
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for a module that does not exist triggers the correct error.');
restore_error_handler();
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files static variable.');
drupal_file_scan_write_cache();
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
$this->assertIdentical($cache->data['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files persistent cache.');
// Simulate moving a module to a location that does not match the location
// in the {system} table and perform similar tests as above.
db_update('system')
->fields(array('filename' => 'modules/simpletest/tests/fake_location/module_test.module'))
->condition('name', 'module_test')
->condition('type', 'module')
->execute();
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$this->assertIdentical(drupal_get_filename('module', 'module_test'), 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved finds the module at its new location.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module has moved within the file system: %name', array('%name' => 'module_test'))) === 0, 'Searching for a module that has moved triggers the correct error.');
restore_error_handler();
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module']['module_test'], 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved creates a record in the missing and moved files static variable.');
drupal_file_scan_write_cache();
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
$this->assertIdentical($cache->data['module']['module_test'], 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved creates a record in the missing and moved files persistent cache.');
// Simulate a module that exists in the {system} table but does not exist
// in the file system and perform similar tests as above.
$non_existing_module = $this->randomName();
db_update('system')
->fields(array('name' => $non_existing_module))
->condition('name', 'module_test')
->condition('type', 'module')
->execute();
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that exists in the system table but not in the file system returns NULL.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for a module that exists in the system table but not in the file system triggers the correct error.');
restore_error_handler();
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that exists in the system table but not in the file system creates a record in the missing and moved files static variable.');
drupal_file_scan_write_cache();
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
$this->assertIdentical($cache->data['module'][$non_existing_module], FALSE, 'Searching for a module that exists in the system table but not in the file system creates a record in the missing and moved files persistent cache.');
// Simulate a module that exists in the file system but not in the {system}
// table and perform similar tests as above.
db_delete('system')
->condition('name', 'common_test')
->condition('type', 'module')
->execute();
system_list_reset();
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$this->assertIdentical(drupal_get_filename('module', 'common_test'), 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table finds the module at its actual location.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module has moved within the file system: %name', array('%name' => 'common_test'))) === 0, 'Searching for a module that does not exist in the system table triggers the correct error.');
restore_error_handler();
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module']['common_test'], 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table creates a record in the missing and moved files static variable.');
drupal_file_scan_write_cache();
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
$this->assertIdentical($cache->data['module']['common_test'], 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table creates a record in the missing and moved files persistent cache.');
}
/**
* Skips handling of "file not found" errors.
*/
public function fileNotFoundErrorHandler($error_level, $message, $filename, $line, $context) {
// Skip error handling if this is a "file not found" error.
if (strpos($message, 'is missing from the file system:') !== FALSE || strpos($message, 'has moved within the file system:') !== FALSE) {
$this->getFilenameTestTriggeredError = $message;
return;
}
_drupal_error_handler($error_level, $message, $filename, $line, $context);
}
/**
* Test that watchdog messages about missing files are correctly recorded.
*/
public function testWatchdog() {
// Search for a module that does not exist in either the file system or the
// {system} table. Make sure that an appropriate warning is recorded in the
// logs.
$non_existing_module = $this->randomName();
$query_parameters = array(
':type' => 'php',
':severity' => WATCHDOG_WARNING,
);
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND severity = :severity', $query_parameters)->fetchField(), 0, 'No warning message appears in the logs before searching for a module that does not exist.');
// Trigger the drupal_get_filename() call. This must be done via a request
// to a separate URL since the watchdog() will happen in a shutdown
// function, and so that SimpleTest can be told to ignore (and not fail as
// a result of) the expected PHP warnings generated during this process.
variable_set('system_test_drupal_get_filename_test_module_name', $non_existing_module);
$this->drupalGet('system-test/drupal-get-filename');
$message_variables = db_query('SELECT variables FROM {watchdog} WHERE type = :type AND severity = :severity', $query_parameters)->fetchCol();
$this->assertEqual(count($message_variables), 1, 'A single warning message appears in the logs after searching for a module that does not exist.');
$variables = reset($message_variables);
$variables = unserialize($variables);
$this->assertTrue(isset($variables['!message']) && strpos($variables['!message'], format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) !== FALSE, 'The warning message that appears in the logs after searching for a module that does not exist contains the expected text.');
}
/**
* Test that drupal_get_filename() does not break recursive rebuilds.
*/
public function testRecursiveRebuilds() {
// Ensure that the drupal_get_filename() call due to a missing module does
// not break the data returned by an attempted recursive rebuild. The code
// path which is tested is as follows:
// - Call drupal_get_schema().
// - Within a hook_schema() implementation, trigger a drupal_get_filename()
// search for a nonexistent module.
// - In the watchdog() call that results from that, trigger
// drupal_get_schema() again.
// Without some kind of recursion protection, this could cause the second
// drupal_get_schema() call to return incomplete results. This test ensures
// that does not happen.
$non_existing_module = $this->randomName();
variable_set('system_test_drupal_get_filename_test_module_name', $non_existing_module);
$this->drupalGet('system-test/drupal-get-filename-with-schema-rebuild');
$original_drupal_get_schema_tables = variable_get('system_test_drupal_get_filename_with_schema_rebuild_original_tables');
$final_drupal_get_schema_tables = variable_get('system_test_drupal_get_filename_with_schema_rebuild_final_tables');
$this->assertTrue(!empty($original_drupal_get_schema_tables));
$this->assertTrue(!empty($final_drupal_get_schema_tables));
$this->assertEqual($original_drupal_get_schema_tables, $final_drupal_get_schema_tables);
}
}
class BootstrapTimerTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Timer test',
'description' => 'Test that timer_read() works both when a timer is running and when a timer is stopped.',
'group' => 'Bootstrap',
);
}
/**
* Test timer_read() to ensure it properly accumulates time when the timer
* started and stopped multiple times.
* @return
*/
function testTimer() {
timer_start('test');
sleep(1);
$this->assertTrue(timer_read('test') >= 1000, 'Timer measured 1 second of sleeping while running.');
sleep(1);
timer_stop('test');
$this->assertTrue(timer_read('test') >= 2000, 'Timer measured 2 seconds of sleeping after being stopped.');
timer_start('test');
sleep(1);
$this->assertTrue(timer_read('test') >= 3000, 'Timer measured 3 seconds of sleeping after being restarted.');
sleep(1);
$timer = timer_stop('test');
$this->assertTrue(timer_read('test') >= 4000, 'Timer measured 4 seconds of sleeping after being stopped for a second time.');
$this->assertEqual($timer['count'], 2, 'Timer counted 2 instances of being started.');
}
}
/**
* Test that resetting static variables works.
*/
class BootstrapResettableStaticTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Resettable static variables test',
'description' => 'Test that drupal_static() and drupal_static_reset() work.',
'group' => 'Bootstrap',
);
}
/**
* Test that a variable reference returned by drupal_static() gets reset when
* drupal_static_reset() is called.
*/
function testDrupalStatic() {
$name = __CLASS__ . '_' . __METHOD__;
$var = &drupal_static($name, 'foo');
$this->assertEqual($var, 'foo', 'Variable returned by drupal_static() was set to its default.');
// Call the specific reset and the global reset each twice to ensure that
// multiple resets can be issued without odd side effects.
$var = 'bar';
drupal_static_reset($name);
$this->assertEqual($var, 'foo', 'Variable was reset after first invocation of name-specific reset.');
$var = 'bar';
drupal_static_reset($name);
$this->assertEqual($var, 'foo', 'Variable was reset after second invocation of name-specific reset.');
$var = 'bar';
drupal_static_reset();
$this->assertEqual($var, 'foo', 'Variable was reset after first invocation of global reset.');
$var = 'bar';
drupal_static_reset();
$this->assertEqual($var, 'foo', 'Variable was reset after second invocation of global reset.');
}
}
/**
* Test miscellaneous functions in bootstrap.inc.
*/
class BootstrapMiscTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Miscellaneous bootstrap unit tests',
'description' => 'Test miscellaneous functions in bootstrap.inc.',
'group' => 'Bootstrap',
);
}
/**
* Test miscellaneous functions in bootstrap.inc.
*/
function testMisc() {
// Test drupal_array_merge_deep().
$link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => 'X', 'class' => array('a', 'b')), 'language' => 'en');
$link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('c', 'd')), 'html' => TRUE);
$expected = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('a', 'b', 'c', 'd')), 'language' => 'en', 'html' => TRUE);
$this->assertIdentical(drupal_array_merge_deep($link_options_1, $link_options_2), $expected, 'drupal_array_merge_deep() returned a properly merged array.');
}
/**
* Tests that the drupal_check_memory_limit() function works as expected.
*/
function testCheckMemoryLimit() {
$memory_limit = ini_get('memory_limit');
// Test that a very reasonable amount of memory is available.
$this->assertTrue(drupal_check_memory_limit('30MB'), '30MB of memory tested available.');
// Get the available memory and multiply it by two to make it unreasonably
// high.
$twice_avail_memory = ($memory_limit * 2) . 'MB';
// The function should always return true if the memory limit is set to -1.
$this->assertTrue(drupal_check_memory_limit($twice_avail_memory, -1), 'drupal_check_memory_limit() returns TRUE when a limit of -1 (none) is supplied');
// Test that even though we have 30MB of memory available - the function
// returns FALSE when given an upper limit for how much memory can be used.
$this->assertFalse(drupal_check_memory_limit('30MB', '16MB'), 'drupal_check_memory_limit() returns FALSE with a 16MB upper limit on a 30MB requirement.');
// Test that an equal amount of memory to the amount requested returns TRUE.
$this->assertTrue(drupal_check_memory_limit('30MB', '30MB'), 'drupal_check_memory_limit() returns TRUE when requesting 30MB on a 30MB requirement.');
}
}
/**
* Tests for overriding server variables via the API.
*/
class BootstrapOverrideServerVariablesTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Overriding server variables',
'description' => 'Test that drupal_override_server_variables() works correctly.',
'group' => 'Bootstrap',
);
}
/**
* Test providing a direct URL to to drupal_override_server_variables().
*/
function testDrupalOverrideServerVariablesProvidedURL() {
$tests = array(
'http://example.com' => array(
'HTTP_HOST' => 'example.com',
'SCRIPT_NAME' => isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : NULL,
),
'http://example.com/index.php' => array(
'HTTP_HOST' => 'example.com',
'SCRIPT_NAME' => '/index.php',
),
'http://example.com/subdirectory/index.php' => array(
'HTTP_HOST' => 'example.com',
'SCRIPT_NAME' => '/subdirectory/index.php',
),
);
foreach ($tests as $url => $expected_server_values) {
// Remember the original value of $_SERVER, since the function call below
// will modify it.
$original_server = $_SERVER;
// Call drupal_override_server_variables() and ensure that all expected
// $_SERVER variables were modified correctly.
drupal_override_server_variables(array('url' => $url));
foreach ($expected_server_values as $key => $value) {
$this->assertIdentical($_SERVER[$key], $value);
}
// Restore the original value of $_SERVER.
$_SERVER = $original_server;
}
}
}
/**
* Tests for $_GET['destination'] and $_REQUEST['destination'] validation.
*/
class BootstrapDestinationTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'URL destination validation',
'description' => 'Test that $_GET[\'destination\'] and $_REQUEST[\'destination\'] cannot contain external URLs.',
'group' => 'Bootstrap',
);
}
function setUp() {
parent::setUp('system_test');
}
/**
* Tests that $_GET/$_REQUEST['destination'] only contain internal URLs.
*
* @see _drupal_bootstrap_variables()
* @see system_test_get_destination()
* @see system_test_request_destination()
*/
public function testDestination() {
$test_cases = array(
array(
'input' => 'node',
'output' => 'node',
'message' => "Standard internal example node path is present in the 'destination' parameter.",
),
array(
'input' => '/example.com',
'output' => '/example.com',
'message' => 'Internal path with one leading slash is allowed.',
),
array(
'input' => '//example.com/test',
'output' => '',
'message' => 'External URL without scheme is not allowed.',
),
array(
'input' => 'example:test',
'output' => 'example:test',
'message' => 'Internal URL using a colon is allowed.',
),
array(
'input' => 'http://example.com',
'output' => '',
'message' => 'External URL is not allowed.',
),
array(
'input' => 'javascript:alert(0)',
'output' => 'javascript:alert(0)',
'message' => 'Javascript URL is allowed because it is treated as an internal URL.',
),
);
foreach ($test_cases as $test_case) {
// Test $_GET['destination'].
$this->drupalGet('system-test/get-destination', array('query' => array('destination' => $test_case['input'])));
$this->assertIdentical($test_case['output'], $this->drupalGetContent(), $test_case['message']);
// Test $_REQUEST['destination']. There's no form to submit to, so
// drupalPost() won't work here; this just tests a direct $_POST request
// instead.
$curl_parameters = array(
CURLOPT_URL => $this->getAbsoluteUrl('system-test/request-destination'),
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => 'destination=' . urlencode($test_case['input']),
CURLOPT_HTTPHEADER => array(),
);
$post_output = $this->curlExec($curl_parameters);
$this->assertIdentical($test_case['output'], $post_output, $test_case['message']);
}
// Make sure that 404 pages do not populate $_GET['destination'] with
// external URLs.
variable_set('site_404', 'system-test/get-destination');
$this->drupalGet('http://example.com', array('external' => FALSE));
$this->assertIdentical('', $this->drupalGetContent(), 'External URL is not allowed on 404 pages.');
}
}

View file

@ -0,0 +1,437 @@
<?php
class CacheTestCase extends DrupalWebTestCase {
protected $default_bin = 'cache';
protected $default_cid = 'test_temporary';
protected $default_value = 'CacheTest';
/**
* Check whether or not a cache entry exists.
*
* @param $cid
* The cache id.
* @param $var
* The variable the cache should contain.
* @param $bin
* The bin the cache item was stored in.
* @return
* TRUE on pass, FALSE on fail.
*/
protected function checkCacheExists($cid, $var, $bin = NULL) {
if ($bin == NULL) {
$bin = $this->default_bin;
}
$cache = cache_get($cid, $bin);
return isset($cache->data) && $cache->data == $var;
}
/**
* Assert or a cache entry exists.
*
* @param $message
* Message to display.
* @param $var
* The variable the cache should contain.
* @param $cid
* The cache id.
* @param $bin
* The bin the cache item was stored in.
*/
protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
if ($bin == NULL) {
$bin = $this->default_bin;
}
if ($cid == NULL) {
$cid = $this->default_cid;
}
if ($var == NULL) {
$var = $this->default_value;
}
$this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
}
/**
* Assert or a cache entry has been removed.
*
* @param $message
* Message to display.
* @param $cid
* The cache id.
* @param $bin
* The bin the cache item was stored in.
*/
function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
if ($bin == NULL) {
$bin = $this->default_bin;
}
if ($cid == NULL) {
$cid = $this->default_cid;
}
$cache = cache_get($cid, $bin);
$this->assertFalse($cache, $message);
}
/**
* Perform the general wipe.
* @param $bin
* The bin to perform the wipe on.
*/
protected function generalWipe($bin = NULL) {
if ($bin == NULL) {
$bin = $this->default_bin;
}
cache_clear_all(NULL, $bin);
}
/**
* Setup the lifetime settings for caching.
*
* @param $time
* The time in seconds the cache should minimal live.
*/
protected function setupLifetime($time) {
variable_set('cache_lifetime', $time);
variable_set('cache_flush', 0);
}
}
class CacheSavingCase extends CacheTestCase {
public static function getInfo() {
return array(
'name' => 'Cache saving test',
'description' => 'Check our variables are saved and restored the right way.',
'group' => 'Cache'
);
}
/**
* Test the saving and restoring of a string.
*/
function testString() {
$this->checkVariable($this->randomName(100));
}
/**
* Test the saving and restoring of an integer.
*/
function testInteger() {
$this->checkVariable(100);
}
/**
* Test the saving and restoring of a double.
*/
function testDouble() {
$this->checkVariable(1.29);
}
/**
* Test the saving and restoring of an array.
*/
function testArray() {
$this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')));
}
/**
* Test the saving and restoring of an object.
*/
function testObject() {
$test_object = new stdClass();
$test_object->test1 = $this->randomName(100);
$test_object->test2 = 100;
$test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
cache_set('test_object', $test_object, 'cache');
$cache = cache_get('test_object', 'cache');
$this->assertTrue(isset($cache->data) && $cache->data == $test_object, 'Object is saved and restored properly.');
}
/**
* Check or a variable is stored and restored properly.
*/
function checkVariable($var) {
cache_set('test_var', $var, 'cache');
$cache = cache_get('test_var', 'cache');
$this->assertTrue(isset($cache->data) && $cache->data === $var, format_string('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
}
/**
* Test no empty cids are written in cache table.
*/
function testNoEmptyCids() {
$this->drupalGet('user/register');
$this->assertFalse(cache_get(''), 'No cache entry is written with an empty cid.');
}
}
/**
* Test cache_get_multiple().
*/
class CacheGetMultipleUnitTest extends CacheTestCase {
public static function getInfo() {
return array(
'name' => 'Fetching multiple cache items',
'description' => 'Confirm that multiple records are fetched correctly.',
'group' => 'Cache',
);
}
function setUp() {
$this->default_bin = 'cache_page';
parent::setUp();
}
/**
* Test cache_get_multiple().
*/
function testCacheMultiple() {
$item1 = $this->randomName(10);
$item2 = $this->randomName(10);
cache_set('item1', $item1, $this->default_bin);
cache_set('item2', $item2, $this->default_bin);
$this->assertTrue($this->checkCacheExists('item1', $item1), 'Item 1 is cached.');
$this->assertTrue($this->checkCacheExists('item2', $item2), 'Item 2 is cached.');
// Fetch both records from the database with cache_get_multiple().
$item_ids = array('item1', 'item2');
$items = cache_get_multiple($item_ids, $this->default_bin);
$this->assertEqual($items['item1']->data, $item1, 'Item was returned from cache successfully.');
$this->assertEqual($items['item2']->data, $item2, 'Item was returned from cache successfully.');
// Remove one item from the cache.
cache_clear_all('item2', $this->default_bin);
// Confirm that only one item is returned by cache_get_multiple().
$item_ids = array('item1', 'item2');
$items = cache_get_multiple($item_ids, $this->default_bin);
$this->assertEqual($items['item1']->data, $item1, 'Item was returned from cache successfully.');
$this->assertFalse(isset($items['item2']), 'Item was not returned from the cache.');
$this->assertTrue(count($items) == 1, 'Only valid cache entries returned.');
}
}
/**
* Test cache clearing methods.
*/
class CacheClearCase extends CacheTestCase {
public static function getInfo() {
return array(
'name' => 'Cache clear test',
'description' => 'Check our clearing is done the proper way.',
'group' => 'Cache'
);
}
function setUp() {
$this->default_bin = 'cache_page';
$this->default_value = $this->randomName(10);
parent::setUp();
}
/**
* Test clearing using a cid.
*/
function testClearCid() {
cache_set('test_cid_clear', $this->default_value, $this->default_bin);
$this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
cache_clear_all('test_cid_clear', $this->default_bin);
$this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches were created for checking cid "*" with wildcard false.');
cache_clear_all('*', $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches still exists after clearing cid "*" with wildcard false.');
}
/**
* Test clearing using wildcard.
*/
function testClearWildcard() {
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches were created for checking cid "*" with wildcard true.');
cache_clear_all('*', $this->default_bin, TRUE);
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches removed after clearing cid "*" with wildcard true.');
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches were created for checking cid substring with wildcard true.');
cache_clear_all('test_', $this->default_bin, TRUE);
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches removed after clearing cid substring with wildcard true.');
}
/**
* Test clearing using an array.
*/
function testClearArray() {
// Create three cache entries.
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value)
&& $this->checkCacheExists('test_cid_clear3', $this->default_value),
'Three cache entries were created.');
// Clear two entries using an array.
cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two cache entries removed after clearing with an array.');
$this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value),
'Entry was not cleared from the cache');
// Set the cache clear threshold to 2 to confirm that the full bin is cleared
// when the threshold is exceeded.
variable_set('cache_clear_threshold', 2);
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two cache entries were created.');
cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value)
|| $this->checkCacheExists('test_cid_clear3', $this->default_value),
'All cache entries removed when the array exceeded the cache clear threshold.');
}
/**
* Test drupal_flush_all_caches().
*/
function testFlushAllCaches() {
// Create cache entries for each flushed cache bin.
$bins = array('cache', 'cache_filter', 'cache_page', 'cache_boostrap', 'cache_path');
$bins = array_merge(module_invoke_all('flush_caches'), $bins);
foreach ($bins as $id => $bin) {
$id = 'test_cid_clear' . $id;
cache_set($id, $this->default_value, $bin);
}
// Remove all caches then make sure that they are cleared.
drupal_flush_all_caches();
foreach ($bins as $id => $bin) {
$id = 'test_cid_clear' . $id;
$this->assertFalse($this->checkCacheExists($id, $this->default_value, $bin), format_string('All cache entries removed from @bin.', array('@bin' => $bin)));
}
}
/**
* Test DrupalDatabaseCache::isValidBin().
*/
function testIsValidBin() {
// Retrieve existing cache bins.
$valid_bins = array('cache', 'cache_filter', 'cache_page', 'cache_boostrap', 'cache_path');
$valid_bins = array_merge(module_invoke_all('flush_caches'), $valid_bins);
foreach ($valid_bins as $id => $bin) {
$cache = _cache_get_object($bin);
if ($cache instanceof DrupalDatabaseCache) {
$this->assertTrue($cache->isValidBin(), format_string('Cache bin @bin is valid.', array('@bin' => $bin)));
}
}
// Check for non-cache tables and invalid bins.
$invalid_bins = array('block', 'filter', 'missing_table', $this->randomName());
foreach ($invalid_bins as $id => $bin) {
$cache = _cache_get_object($bin);
if ($cache instanceof DrupalDatabaseCache) {
$this->assertFalse($cache->isValidBin(), format_string('Cache bin @bin is not valid.', array('@bin' => $bin)));
}
}
}
/**
* Test minimum cache lifetime.
*/
function testMinimumCacheLifetime() {
// Set a minimum/maximum cache lifetime.
$this->setupLifetime(300);
// Login as a newly-created user.
$account = $this->drupalCreateUser(array());
$this->drupalLogin($account);
// Set two cache objects in different bins.
$data = $this->randomName(100);
cache_set($data, $data, 'cache', CACHE_TEMPORARY);
$cached = cache_get($data);
$this->assertTrue(isset($cached->data) && $cached->data === $data, 'Cached item retrieved.');
cache_set($data, $data, 'cache_page', CACHE_TEMPORARY);
// Expire temporary items in the 'page' bin.
cache_clear_all(NULL, 'cache_page');
// Since the database cache uses REQUEST_TIME, set the $_SESSION variable
// manually to force it to the current time.
$_SESSION['cache_expiration']['cache_page'] = time();
// Items in the default cache bin should not be expired.
$cached = cache_get($data);
$this->assertTrue(isset($cached->data) && $cached->data == $data, 'Cached item retrieved');
// Despite the minimum cache lifetime, the item in the 'page' bin should
// be invalidated for the current user.
$cached = cache_get($data, 'cache_page');
$this->assertFalse($cached, 'Cached item was invalidated');
}
}
/**
* Test cache_is_empty() function.
*/
class CacheIsEmptyCase extends CacheTestCase {
public static function getInfo() {
return array(
'name' => 'Cache emptiness test',
'description' => 'Check if a cache bin is empty after performing clear operations.',
'group' => 'Cache'
);
}
function setUp() {
$this->default_bin = 'cache_page';
$this->default_value = $this->randomName(10);
parent::setUp();
}
/**
* Test clearing using a cid.
*/
function testIsEmpty() {
// Clear the cache bin.
cache_clear_all('*', $this->default_bin);
$this->assertTrue(cache_is_empty($this->default_bin), 'The cache bin is empty');
// Add some data to the cache bin.
cache_set($this->default_cid, $this->default_value, $this->default_bin);
$this->assertCacheExists(t('Cache was set.'), $this->default_value, $this->default_cid);
$this->assertFalse(cache_is_empty($this->default_bin), 'The cache bin is not empty');
// Remove the cached data.
cache_clear_all($this->default_cid, $this->default_bin);
$this->assertCacheRemoved(t('Cache was removed.'), $this->default_cid);
$this->assertTrue(cache_is_empty($this->default_bin), 'The cache bin is empty');
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
/* This file is for testing CSS file inclusion, no contents are necessary. */

View file

@ -0,0 +1,14 @@
name = "Common Test"
description = "Support module for Common tests."
package = Testing
version = VERSION
core = 7.x
stylesheets[all][] = common_test.css
stylesheets[print][] = common_test.print.css
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,285 @@
<?php
/**
* @file
* Helper module for the Common tests.
*/
/**
* Implements hook_menu().
*/
function common_test_menu() {
$items['common-test/drupal_goto'] = array(
'title' => 'Drupal Goto',
'page callback' => 'common_test_drupal_goto_land',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/drupal_goto/fail'] = array(
'title' => 'Drupal Goto',
'page callback' => 'common_test_drupal_goto_land_fail',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/drupal_goto/redirect'] = array(
'title' => 'Drupal Goto',
'page callback' => 'common_test_drupal_goto_redirect',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/drupal_goto/redirect_advanced'] = array(
'title' => 'Drupal Goto',
'page callback' => 'common_test_drupal_goto_redirect_advanced',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/drupal_goto/redirect_fail'] = array(
'title' => 'Drupal Goto Failure',
'page callback' => 'drupal_goto',
'page arguments' => array('common-test/drupal_goto/fail'),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/destination'] = array(
'title' => 'Drupal Get Destination',
'page callback' => 'common_test_destination',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/query-string'] = array(
'title' => 'Test querystring',
'page callback' => 'common_test_js_and_css_querystring',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Redirect using drupal_goto().
*/
function common_test_drupal_goto_redirect() {
drupal_goto('common-test/drupal_goto');
}
/**
* Redirect using drupal_goto().
*/
function common_test_drupal_goto_redirect_advanced() {
drupal_goto('common-test/drupal_goto', array('query' => array('foo' => '123')), 301);
}
/**
* Landing page for drupal_goto().
*/
function common_test_drupal_goto_land() {
print "drupal_goto";
}
/**
* Fail landing page for drupal_goto().
*/
function common_test_drupal_goto_land_fail() {
print "drupal_goto_fail";
}
/**
* Implements hook_drupal_goto_alter().
*/
function common_test_drupal_goto_alter(&$path, &$options, &$http_response_code) {
if ($path == 'common-test/drupal_goto/fail') {
$path = 'common-test/drupal_goto/redirect';
}
}
/**
* Implements hook_init().
*/
function common_test_init() {
if (variable_get('common_test_redirect_current_path', FALSE)) {
drupal_goto(current_path());
}
}
/**
* Print destination query parameter.
*/
function common_test_destination() {
$destination = drupal_get_destination();
print "The destination: " . check_plain($destination['destination']);
}
/**
* Applies #printed to an element to help test #pre_render.
*/
function common_test_drupal_render_printing_pre_render($elements) {
$elements['#printed'] = TRUE;
return $elements;
}
/**
* Implements hook_TYPE_alter().
*/
function common_test_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
// Alter first argument.
if (is_array($data)) {
$data['foo'] = 'Drupal';
}
elseif (is_object($data)) {
$data->foo = 'Drupal';
}
// Alter second argument, if present.
if (isset($arg2)) {
if (is_array($arg2)) {
$arg2['foo'] = 'Drupal';
}
elseif (is_object($arg2)) {
$arg2->foo = 'Drupal';
}
}
// Try to alter third argument, if present.
if (isset($arg3)) {
if (is_array($arg3)) {
$arg3['foo'] = 'Drupal';
}
elseif (is_object($arg3)) {
$arg3->foo = 'Drupal';
}
}
}
/**
* Implements hook_TYPE_alter() on behalf of Bartik theme.
*
* Same as common_test_drupal_alter_alter(), but here, we verify that themes
* can also alter and come last.
*/
function bartik_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
// Alter first argument.
if (is_array($data)) {
$data['foo'] .= ' theme';
}
elseif (is_object($data)) {
$data->foo .= ' theme';
}
// Alter second argument, if present.
if (isset($arg2)) {
if (is_array($arg2)) {
$arg2['foo'] .= ' theme';
}
elseif (is_object($arg2)) {
$arg2->foo .= ' theme';
}
}
// Try to alter third argument, if present.
if (isset($arg3)) {
if (is_array($arg3)) {
$arg3['foo'] .= ' theme';
}
elseif (is_object($arg3)) {
$arg3->foo .= ' theme';
}
}
}
/**
* 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() {
return array(
'common_test_foo' => array(
'variables' => array('foo' => 'foo', 'bar' => 'bar'),
),
);
}
/**
* Theme function for testing drupal_render() theming.
*/
function theme_common_test_foo($variables) {
return $variables['foo'] . $variables['bar'];
}
/**
* Implements hook_library_alter().
*/
function common_test_library_alter(&$libraries, $module) {
if ($module == 'system' && isset($libraries['farbtastic'])) {
// Change the title of Farbtastic to "Farbtastic: Altered Library".
$libraries['farbtastic']['title'] = 'Farbtastic: Altered Library';
// Make Farbtastic depend on jQuery Form to test library dependencies.
$libraries['farbtastic']['dependencies'][] = array('system', 'form');
}
}
/**
* Implements hook_library().
*
* Adds Farbtastic in a different version.
*/
function common_test_library() {
$libraries['farbtastic'] = array(
'title' => 'Custom Farbtastic Library',
'website' => 'http://code.google.com/p/farbtastic/',
'version' => '5.3',
'js' => array(
'misc/farbtastic/farbtastic.js' => array(),
),
'css' => array(
'misc/farbtastic/farbtastic.css' => array(),
),
);
return $libraries;
}
/**
* Adds a JavaScript file and a CSS file with a query string appended.
*/
function common_test_js_and_css_querystring() {
drupal_add_js(drupal_get_path('module', 'node') . '/node.js');
drupal_add_css(drupal_get_path('module', 'node') . '/node.css');
// A relative URI may have a query string.
drupal_add_css('/' . drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&arg2=value2');
return '';
}
/**
* Implements hook_cron().
*
* System module should handle if a module does not catch an exception and keep
* cron going.
*
* @see common_test_cron_helper()
*
*/
function common_test_cron() {
throw new Exception(t('Uncaught exception'));
}

View file

@ -0,0 +1,2 @@
/* This file is for testing CSS file inclusion, no contents are necessary. */

View file

@ -0,0 +1,12 @@
name = "Common Test Cron Helper"
description = "Helper module for CronRunTestCase::testCronExceptions()."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,17 @@
<?php
/**
* @file
* Helper module for the testCronExceptions in addition to common_test module.
*/
/**
* Implements hook_cron().
*
* common_test_cron() throws an exception, but the execution should reach this
* function as well.
*
* @see common_test_cron()
*/
function common_test_cron_helper_cron() {
variable_set('common_test_cron', 'success');
}

View file

@ -0,0 +1,9 @@
; Test parsing with a simple string.
simple_string = A simple string
; Test that constants can be used as values.
simple_constant = WATCHDOG_INFO
; After parsing the .info file, 'double_colon' should hold the literal value.
; Parsing should not throw a fatal error or try to access a class constant.
double_colon = dummyClassName::

View file

@ -0,0 +1,12 @@
name = "Database Test"
description = "Support module for Database layer tests."
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,221 @@
<?php
/**
* @file
* Install, update and uninstall functions for the database_test module.
*/
/**
* Implements hook_schema().
*
* The database tests use the database API which depends on schema
* information for certain operations on certain databases.
* Therefore, the schema must actually be declared in a normal module
* like any other, not directly in the test file.
*/
function database_test_schema() {
$schema['test'] = array(
'description' => 'Basic test table for the database unit tests.',
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'name' => array(
'description' => "A person's name",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'binary' => TRUE,
),
'age' => array(
'description' => "The person's age",
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'job' => array(
'description' => "The person's job",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => 'Undefined',
),
),
'primary key' => array('id'),
'unique keys' => array(
'name' => array('name')
),
'indexes' => array(
'ages' => array('age'),
),
);
// This is an alternate version of the same table that is structured the same
// but has a non-serial Primary Key.
$schema['test_people'] = array(
'description' => 'A duplicate version of the test table, used for additional tests.',
'fields' => array(
'name' => array(
'description' => "A person's name",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'age' => array(
'description' => "The person's age",
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'job' => array(
'description' => "The person's job",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
),
'primary key' => array('job'),
'indexes' => array(
'ages' => array('age'),
),
);
$schema['test_people_copy'] = $schema['test_people'];
$schema['test_people_copy']['description'] = 'A duplicate version of the test_people table, used for additional tests.';
$schema['test_one_blob'] = array(
'description' => 'A simple table including a BLOB field for testing BLOB behavior.',
'fields' => array(
'id' => array(
'description' => 'Simple unique ID.',
'type' => 'serial',
'not null' => TRUE,
),
'blob1' => array(
'description' => 'A BLOB field.',
'type' => 'blob',
),
),
'primary key' => array('id'),
);
$schema['test_two_blobs'] = array(
'description' => 'A simple test table with two BLOB fields.',
'fields' => array(
'id' => array(
'description' => 'Simple unique ID.',
'type' => 'serial',
'not null' => TRUE,
),
'blob1' => array(
'description' => 'A dummy BLOB field.',
'type' => 'blob',
),
'blob2' => array(
'description' => 'A second BLOB field.',
'type' => 'blob'
),
),
'primary key' => array('id'),
);
$schema['test_task'] = array(
'description' => 'A task list for people in the test table.',
'fields' => array(
'tid' => array(
'description' => 'Task ID, primary key.',
'type' => 'serial',
'not null' => TRUE,
),
'pid' => array(
'description' => 'The {test_people}.pid, foreign key for the test table.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'task' => array(
'description' => 'The task to be completed.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'priority' => array(
'description' => 'The priority of the task.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
),
'primary key' => array('tid'),
);
$schema['test_null'] = array(
'description' => 'Basic test table for NULL value handling.',
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'name' => array(
'description' => "A person's name.",
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'default' => '',
),
'age' => array(
'description' => "The person's age.",
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
'default' => 0),
),
'primary key' => array('id'),
'unique keys' => array(
'name' => array('name')
),
'indexes' => array(
'ages' => array('age'),
),
);
$schema['test_serialized'] = array(
'description' => 'Basic test table for NULL value handling.',
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'name' => array(
'description' => "A person's name.",
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'default' => '',
),
'info' => array(
'description' => "The person's data in serialized form.",
'type' => 'blob',
'serialize' => TRUE,
),
),
'primary key' => array('id'),
'unique keys' => array(
'name' => array('name')
),
);
return $schema;
}

View file

@ -0,0 +1,241 @@
<?php
/**
* Implements hook_query_alter().
*/
function database_test_query_alter(QueryAlterableInterface $query) {
if ($query->hasTag('database_test_alter_add_range')) {
$query->range(0, 2);
}
if ($query->hasTag('database_test_alter_add_join')) {
$people_alias = $query->join('test', 'people', "test_task.pid = %alias.id");
$name_field = $query->addField($people_alias, 'name', 'name');
$query->condition($people_alias . '.id', 2);
}
if ($query->hasTag('database_test_alter_change_conditional')) {
$conditions =& $query->conditions();
$conditions[0]['value'] = 2;
}
if ($query->hasTag('database_test_alter_change_fields')) {
$fields =& $query->getFields();
unset($fields['age']);
}
if ($query->hasTag('database_test_alter_change_expressions')) {
$expressions =& $query->getExpressions();
$expressions['double_age']['expression'] = 'age*3';
}
}
/**
* Implements hook_query_TAG_alter().
*
* Called by DatabaseTestCase::testAlterRemoveRange.
*/
function database_test_query_database_test_alter_remove_range_alter(QueryAlterableInterface $query) {
$query->range();
}
/**
* Implements hook_menu().
*/
function database_test_menu() {
$items['database_test/db_query_temporary'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_db_query_temporary',
);
$items['database_test/pager_query_even'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_even_pager_query',
);
$items['database_test/pager_query_odd'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_odd_pager_query',
);
$items['database_test/tablesort'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_tablesort',
);
$items['database_test/tablesort_first'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_tablesort_first',
);
$items['database_test/tablesort_default_sort'] = array(
'access callback' => TRUE,
'page callback' => 'drupal_get_form',
'page arguments' => array('database_test_theme_tablesort'),
);
return $items;
}
/**
* Run a db_query_temporary and output the table name and its number of rows.
*
* We need to test that the table created is temporary, so we run it here, in a
* separate menu callback request; After this request is done, the temporary
* table should automatically dropped.
*/
function database_test_db_query_temporary() {
$table_name = db_query_temporary('SELECT status FROM {system}', array());
drupal_json_output(array(
'table_name' => $table_name,
'row_count' => db_select($table_name)->countQuery()->execute()->fetchField(),
));
exit;
}
/**
* Run a pager query and return the results.
*
* This function does care about the page GET parameter, as set by the
* simpletest HTTP call.
*/
function database_test_even_pager_query($limit) {
$query = db_select('test', 't');
$query
->fields('t', array('name'))
->orderBy('age');
// This should result in 2 pages of results.
$query = $query->extend('PagerDefault')->limit($limit);
$names = $query->execute()->fetchCol();
drupal_json_output(array(
'names' => $names,
));
exit;
}
/**
* Run a pager query and return the results.
*
* This function does care about the page GET parameter, as set by the
* simpletest HTTP call.
*/
function database_test_odd_pager_query($limit) {
$query = db_select('test_task', 't');
$query
->fields('t', array('task'))
->orderBy('pid');
// This should result in 4 pages of results.
$query = $query->extend('PagerDefault')->limit($limit);
$names = $query->execute()->fetchCol();
drupal_json_output(array(
'names' => $names,
));
exit;
}
/**
* Run a tablesort query and return the results.
*
* This function does care about the page GET parameter, as set by the
* simpletest HTTP call.
*/
function database_test_tablesort() {
$header = array(
'tid' => array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc'),
'pid' => array('data' => t('Person ID'), 'field' => 'pid'),
'task' => array('data' => t('Task'), 'field' => 'task'),
'priority' => array('data' => t('Priority'), 'field' => 'priority', ),
);
$query = db_select('test_task', 't');
$query
->fields('t', array('tid', 'pid', 'task', 'priority'));
$query = $query->extend('TableSort')->orderByHeader($header);
// We need all the results at once to check the sort.
$tasks = $query->execute()->fetchAll();
drupal_json_output(array(
'tasks' => $tasks,
));
exit;
}
/**
* Run a tablesort query with a second order_by after and return the results.
*
* This function does care about the page GET parameter, as set by the
* simpletest HTTP call.
*/
function database_test_tablesort_first() {
$header = array(
'tid' => array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc'),
'pid' => array('data' => t('Person ID'), 'field' => 'pid'),
'task' => array('data' => t('Task'), 'field' => 'task'),
'priority' => array('data' => t('Priority'), 'field' => 'priority', ),
);
$query = db_select('test_task', 't');
$query
->fields('t', array('tid', 'pid', 'task', 'priority'));
$query = $query->extend('TableSort')->orderByHeader($header)->orderBy('priority');
// We need all the results at once to check the sort.
$tasks = $query->execute()->fetchAll();
drupal_json_output(array(
'tasks' => $tasks,
));
exit;
}
/**
* Output a form without setting a header sort.
*/
function database_test_theme_tablesort($form, &$form_state) {
$header = array(
'username' => array('data' => t('Username'), 'field' => 'u.name'),
'status' => array('data' => t('Status'), 'field' => 'u.status'),
);
$query = db_select('users', 'u');
$query->condition('u.uid', 0, '<>');
user_build_filter_query($query);
$count_query = clone $query;
$count_query->addExpression('COUNT(u.uid)');
$query = $query->extend('PagerDefault')->extend('TableSort');
$query
->fields('u', array('uid', 'name', 'status', 'created', 'access'))
->limit(50)
->orderByHeader($header)
->setCountQuery($count_query);
$result = $query->execute();
$options = array();
$status = array(t('blocked'), t('active'));
$accounts = array();
foreach ($result as $account) {
$options[$account->uid] = array(
'username' => check_plain($account->name),
'status' => $status[$account->status],
);
}
$form['accounts'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#empty' => t('No people available.'),
);
return $form;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
name = "Drupal code registry test"
description = "Support module for testing the code registry."
files[] = drupal_autoload_test_interface.inc
files[] = drupal_autoload_test_class.inc
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,22 @@
<?php
/**
* @file
* Test module to check code registry.
*/
/**
* Implements hook_registry_files_alter().
*/
function drupal_autoload_test_registry_files_alter(&$files, $modules) {
foreach ($modules as $module) {
// Add the drupal_autoload_test_trait.sh file to the registry when PHP 5.4+
// is being used.
if ($module->name == 'drupal_autoload_test' && version_compare(PHP_VERSION, '5.4') >= 0) {
$files["$module->dir/drupal_autoload_test_trait.sh"] = array(
'module' => $module->name,
'weight' => $module->weight,
);
}
}
}

View file

@ -0,0 +1,11 @@
<?php
/**
* @file
* Test classes for code registry testing.
*/
/**
* This class is empty because we only care if Drupal can find it.
*/
class DrupalAutoloadTestClass implements DrupalAutoloadTestInterface {}

View file

@ -0,0 +1,11 @@
<?php
/**
* @file
* Test interfaces for code registry testing.
*/
/**
* This interface is empty because we only care if Drupal can find it.
*/
interface DrupalAutoloadTestInterface {}

View file

@ -0,0 +1,16 @@
<?php
/**
* @file
* Test traits for code registry testing.
*
* This file has a non-standard extension to prevent PHP < 5.4 testbots from
* trying to run a syntax check on it.
* @todo Use a standard extension once the testbots allow it. See
* https://www.drupal.org/node/2589649.
*/
/**
* This trait is empty because we only care if Drupal can find it.
*/
trait DrupalAutoloadTestTrait {}

View file

@ -0,0 +1,12 @@
name = "Drupal system listing compatible test"
description = "Support module for testing the drupal_system_listing function."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,12 @@
name = "Drupal system listing incompatible test"
description = "Support module for testing the drupal_system_listing function."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,13 @@
name = "Entity cache test"
description = "Support module for testing entity cache."
package = Testing
version = VERSION
core = 7.x
dependencies[] = entity_cache_test_dependency
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,27 @@
<?php
/**
* @file
* Helper module for entity cache tests.
*/
/**
* Implements hook_watchdog().
*
* This hook is called during module_enable() and since this hook
* implementation is invoked, we have to expect that this module and dependent
* modules have been properly installed already. So we expect to be able to
* retrieve the entity information that has been registered by the required
* dependency module.
*
* @see EnableDisableTestCase::testEntityCache()
* @see entity_cache_test_dependency_entity_info()
*/
function entity_cache_test_watchdog($log_entry) {
if ($log_entry['type'] == 'system' && $log_entry['message'] == '%module module installed.') {
$info = entity_get_info('entity_cache_test');
// Store the information in a system variable to analyze it later in the
// test case.
variable_set('entity_cache_test', $info);
}
}

View file

@ -0,0 +1,12 @@
name = "Entity cache test dependency"
description = "Support dependency module for testing entity cache."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,17 @@
<?php
/**
* @file
* Helper module for entity cache tests.
*/
/**
* Implements hook_entity_info().
*/
function entity_cache_test_dependency_entity_info() {
return array(
'entity_cache_test' => array(
'label' => variable_get('entity_cache_test_label', 'Entity Cache Test'),
),
);
}

View file

@ -0,0 +1,49 @@
<?php
/**
* @file
* Tests for the Entity CRUD API.
*/
/**
* Tests the entity_load() function.
*/
class EntityLoadTestCase extends DrupalWebTestCase {
protected $profile = 'testing';
public static function getInfo() {
return array(
'name' => 'Entity loading',
'description' => 'Tests the entity_load() function.',
'group' => 'Entity API',
);
}
/**
* Tests the functionality for loading entities matching certain conditions.
*/
public function testEntityLoadConditions() {
// Create a few nodes. One of them is given an edge-case title of "Array",
// because loading entities by an array of conditions is subject to PHP
// array-to-string conversion issues and we want to test those.
$node_1 = $this->drupalCreateNode(array('title' => 'Array'));
$node_2 = $this->drupalCreateNode(array('title' => 'Node 2'));
$node_3 = $this->drupalCreateNode(array('title' => 'Node 3'));
// Load all entities so that they are statically cached.
$all_nodes = entity_load('node', FALSE);
// Check that the first node can be loaded by title.
$nodes_loaded = entity_load('node', FALSE, array('title' => 'Array'));
$this->assertEqual(array_keys($nodes_loaded), array($node_1->nid));
// Check that the second and third nodes can be loaded by title using an
// array of conditions, and that the first node is not loaded from the
// cache along with them.
$nodes_loaded = entity_load('node', FALSE, array('title' => array('Node 2', 'Node 3')));
ksort($nodes_loaded);
$this->assertEqual(array_keys($nodes_loaded), array($node_2->nid, $node_3->nid));
$this->assertIdentical($nodes_loaded[$node_2->nid], $all_nodes[$node_2->nid], 'Loaded node 2 is identical to cached node.');
$this->assertIdentical($nodes_loaded[$node_3->nid], $all_nodes[$node_3->nid], 'Loaded node 3 is identical to cached node.');
}
}

View file

@ -0,0 +1,12 @@
name = "Entity CRUD Hooks Test"
description = "Support module for CRUD hook tests."
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,251 @@
<?php
/**
* @file
* Test module for the Entity CRUD API.
*/
/**
* Implements hook_entity_presave().
*/
function entity_crud_hook_test_entity_presave($entity, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_presave().
*/
function entity_crud_hook_test_comment_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_presave().
*/
function entity_crud_hook_test_file_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_presave().
*/
function entity_crud_hook_test_node_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_presave().
*/
function entity_crud_hook_test_taxonomy_term_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_presave().
*/
function entity_crud_hook_test_taxonomy_vocabulary_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_presave().
*/
function entity_crud_hook_test_user_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_entity_insert().
*/
function entity_crud_hook_test_entity_insert($entity, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_insert().
*/
function entity_crud_hook_test_comment_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_insert().
*/
function entity_crud_hook_test_file_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_insert().
*/
function entity_crud_hook_test_node_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_insert().
*/
function entity_crud_hook_test_taxonomy_term_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_insert().
*/
function entity_crud_hook_test_taxonomy_vocabulary_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_insert().
*/
function entity_crud_hook_test_user_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_entity_load().
*/
function entity_crud_hook_test_entity_load(array $entities, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_load().
*/
function entity_crud_hook_test_comment_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_load().
*/
function entity_crud_hook_test_file_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_load().
*/
function entity_crud_hook_test_node_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_load().
*/
function entity_crud_hook_test_taxonomy_term_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_load().
*/
function entity_crud_hook_test_taxonomy_vocabulary_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_load().
*/
function entity_crud_hook_test_user_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_entity_update().
*/
function entity_crud_hook_test_entity_update($entity, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_update().
*/
function entity_crud_hook_test_comment_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_update().
*/
function entity_crud_hook_test_file_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_update().
*/
function entity_crud_hook_test_node_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_update().
*/
function entity_crud_hook_test_taxonomy_term_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_update().
*/
function entity_crud_hook_test_taxonomy_vocabulary_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_update().
*/
function entity_crud_hook_test_user_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_entity_delete().
*/
function entity_crud_hook_test_entity_delete($entity, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_delete().
*/
function entity_crud_hook_test_comment_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_delete().
*/
function entity_crud_hook_test_file_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_delete().
*/
function entity_crud_hook_test_node_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_delete().
*/
function entity_crud_hook_test_taxonomy_term_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_delete().
*/
function entity_crud_hook_test_taxonomy_vocabulary_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_delete().
*/
function entity_crud_hook_test_user_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}

View file

@ -0,0 +1,338 @@
<?php
/**
* @file
* CRUD hook tests for the Entity CRUD API.
*/
/**
* Tests invocation of hooks when performing an action.
*
* Tested hooks are:
* - hook_entity_insert()
* - hook_entity_load()
* - hook_entity_update()
* - hook_entity_delete()
* As well as all type-specific hooks, like hook_node_insert(),
* hook_comment_update(), etc.
*/
class EntityCrudHookTestCase extends DrupalWebTestCase {
protected $ids = array();
public static function getInfo() {
return array(
'name' => 'Entity CRUD hooks',
'description' => 'Tests the invocation of hooks when inserting, loading, updating or deleting an entity.',
'group' => 'Entity API',
);
}
public function setUp() {
parent::setUp('entity_crud_hook_test', 'taxonomy', 'comment');
}
/**
* Pass if the message $text was set by one of the CRUD hooks in
* entity_crud_hook_test.module, i.e., if the $text is an element of
* $_SESSION['entity_crud_hook_test'].
*
* @param $text
* Plain text to look for.
* @param $message
* Message to display.
* @param $group
* The group this message belongs to, defaults to 'Other'.
* @return
* TRUE on pass, FALSE on fail.
*/
protected function assertHookMessage($text, $message = NULL, $group = 'Other') {
if (!isset($message)) {
$message = $text;
}
return $this->assertTrue(array_search($text, $_SESSION['entity_crud_hook_test']) !== FALSE, $message, $group);
}
/**
* Tests hook invocations for CRUD operations on comments.
*/
public function testCommentHooks() {
$node = (object) array(
'uid' => 1,
'type' => 'article',
'title' => 'Test node',
'status' => 1,
'comment' => 2,
'promote' => 0,
'sticky' => 0,
'language' => LANGUAGE_NONE,
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
);
node_save($node);
$nid = $node->nid;
$comment = (object) array(
'cid' => NULL,
'pid' => 0,
'nid' => $nid,
'uid' => 1,
'subject' => 'Test comment',
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
'status' => 1,
'language' => LANGUAGE_NONE,
);
$_SESSION['entity_crud_hook_test'] = array();
comment_save($comment);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$comment = comment_load($comment->cid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_load called');
$_SESSION['entity_crud_hook_test'] = array();
$comment->subject = 'New subject';
comment_save($comment);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_update called');
$_SESSION['entity_crud_hook_test'] = array();
comment_delete($comment->cid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_delete called');
}
/**
* Tests hook invocations for CRUD operations on files.
*/
public function testFileHooks() {
$url = 'public://entity_crud_hook_test.file';
file_put_contents($url, 'Test test test');
$file = (object) array(
'fid' => NULL,
'uid' => 1,
'filename' => 'entity_crud_hook_test.file',
'uri' => $url,
'filemime' => 'text/plain',
'filesize' => filesize($url),
'status' => 1,
'timestamp' => REQUEST_TIME,
);
$_SESSION['entity_crud_hook_test'] = array();
file_save($file);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$file = file_load($file->fid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_load called');
$_SESSION['entity_crud_hook_test'] = array();
$file->filename = 'new.entity_crud_hook_test.file';
file_save($file);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_update called');
$_SESSION['entity_crud_hook_test'] = array();
file_delete($file);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_delete called');
}
/**
* Tests hook invocations for CRUD operations on nodes.
*/
public function testNodeHooks() {
$node = (object) array(
'uid' => 1,
'type' => 'article',
'title' => 'Test node',
'status' => 1,
'comment' => 2,
'promote' => 0,
'sticky' => 0,
'language' => LANGUAGE_NONE,
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
);
$_SESSION['entity_crud_hook_test'] = array();
node_save($node);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$node = node_load($node->nid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_load called');
$_SESSION['entity_crud_hook_test'] = array();
$node->title = 'New title';
node_save($node);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_update called');
$_SESSION['entity_crud_hook_test'] = array();
node_delete($node->nid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_delete called');
}
/**
* Tests hook invocations for CRUD operations on taxonomy terms.
*/
public function testTaxonomyTermHooks() {
$vocabulary = (object) array(
'name' => 'Test vocabulary',
'machine_name' => 'test',
'description' => NULL,
'module' => 'entity_crud_hook_test',
);
taxonomy_vocabulary_save($vocabulary);
$term = (object) array(
'vid' => $vocabulary->vid,
'name' => 'Test term',
'description' => NULL,
'format' => 1,
);
$_SESSION['entity_crud_hook_test'] = array();
taxonomy_term_save($term);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$term = taxonomy_term_load($term->tid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_load called');
$_SESSION['entity_crud_hook_test'] = array();
$term->name = 'New name';
taxonomy_term_save($term);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_update called');
$_SESSION['entity_crud_hook_test'] = array();
taxonomy_term_delete($term->tid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_delete called');
}
/**
* Tests hook invocations for CRUD operations on taxonomy vocabularies.
*/
public function testTaxonomyVocabularyHooks() {
$vocabulary = (object) array(
'name' => 'Test vocabulary',
'machine_name' => 'test',
'description' => NULL,
'module' => 'entity_crud_hook_test',
);
$_SESSION['entity_crud_hook_test'] = array();
taxonomy_vocabulary_save($vocabulary);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$vocabulary = taxonomy_vocabulary_load($vocabulary->vid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_load called');
$_SESSION['entity_crud_hook_test'] = array();
$vocabulary->name = 'New name';
taxonomy_vocabulary_save($vocabulary);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_update called');
$_SESSION['entity_crud_hook_test'] = array();
taxonomy_vocabulary_delete($vocabulary->vid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_delete called');
}
/**
* Tests hook invocations for CRUD operations on users.
*/
public function testUserHooks() {
$edit = array(
'name' => 'Test user',
'mail' => 'test@example.com',
'created' => REQUEST_TIME,
'status' => 1,
'language' => 'en',
);
$account = (object) $edit;
$_SESSION['entity_crud_hook_test'] = array();
$account = user_save($account, $edit);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$account = user_load($account->uid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_load called');
$_SESSION['entity_crud_hook_test'] = array();
$edit['name'] = 'New name';
$account = user_save($account, $edit);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_update called');
$_SESSION['entity_crud_hook_test'] = array();
user_delete($account->uid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_delete called');
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
name = "Entity query access test"
description = "Support module for checking entity query results."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,54 @@
<?php
/**
* @file
* Helper module for testing EntityFieldQuery access on any type of entity.
*/
/**
* Implements hook_menu().
*/
function entity_query_access_test_menu() {
$items['entity-query-access/test/%'] = array(
'title' => "Retrieve a sample of entity query access data",
'page callback' => 'entity_query_access_test_sample_query',
'page arguments' => array(2),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Returns the results from an example EntityFieldQuery.
*/
function entity_query_access_test_sample_query($field_name) {
global $user;
// Simulate user does not have access to view all nodes.
$access = &drupal_static('node_access_view_all_nodes');
$access[$user->uid] = FALSE;
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'test_entity_bundle_key')
->fieldCondition($field_name, 'value', 0, '>')
->entityOrderBy('entity_id', 'ASC');
$results = array(
'items' => array(),
'title' => t('EntityFieldQuery results'),
);
foreach ($query->execute() as $entity_type => $entity_ids) {
foreach ($entity_ids as $entity_id => $entity_stub) {
$results['items'][] = format_string('Found entity of type @entity_type with id @entity_id', array('@entity_type' => $entity_type, '@entity_id' => $entity_id));
}
}
if (count($results['items']) > 0) {
$output = theme('item_list', $results);
}
else {
$output = 'No results found with EntityFieldQuery.';
}
return $output;
}

View file

@ -0,0 +1,116 @@
<?php
/**
* Tests Drupal error and exception handlers.
*/
class DrupalErrorHandlerTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Drupal error handlers',
'description' => 'Performs tests on the Drupal error and exception handler.',
'group' => 'System',
);
}
function setUp() {
parent::setUp('error_test');
}
/**
* Test the error handler.
*/
function testErrorHandler() {
$error_notice = array(
'%type' => 'Notice',
'!message' => 'Undefined variable: bananas',
'%function' => 'error_test_generate_warnings()',
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
$error_warning = array(
'%type' => 'Warning',
'!message' => 'Division by zero',
'%function' => 'error_test_generate_warnings()',
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
$error_user_notice = array(
'%type' => 'User warning',
'!message' => 'Drupal is awesome',
'%function' => 'error_test_generate_warnings()',
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
// Set error reporting to collect notices.
variable_set('error_level', ERROR_REPORTING_DISPLAY_ALL);
$this->drupalGet('error-test/generate-warnings');
$this->assertResponse(200, 'Received expected HTTP status code.');
$this->assertErrorMessage($error_notice);
$this->assertErrorMessage($error_warning);
$this->assertErrorMessage($error_user_notice);
// Set error reporting to not collect notices.
variable_set('error_level', ERROR_REPORTING_DISPLAY_SOME);
$this->drupalGet('error-test/generate-warnings');
$this->assertResponse(200, 'Received expected HTTP status code.');
$this->assertNoErrorMessage($error_notice);
$this->assertErrorMessage($error_warning);
$this->assertErrorMessage($error_user_notice);
// Set error reporting to not show any errors.
variable_set('error_level', ERROR_REPORTING_HIDE);
$this->drupalGet('error-test/generate-warnings');
$this->assertResponse(200, 'Received expected HTTP status code.');
$this->assertNoErrorMessage($error_notice);
$this->assertNoErrorMessage($error_warning);
$this->assertNoErrorMessage($error_user_notice);
}
/**
* Test the exception handler.
*/
function testExceptionHandler() {
$error_exception = array(
'%type' => 'Exception',
'!message' => 'Drupal is awesome',
'%function' => 'error_test_trigger_exception()',
'%line' => 57,
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
$error_pdo_exception = array(
'%type' => 'PDOException',
'!message' => 'SELECT * FROM bananas_are_awesome',
'%function' => 'error_test_trigger_pdo_exception()',
'%line' => 65,
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
$this->drupalGet('error-test/trigger-exception');
$this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.');
$this->assertErrorMessage($error_exception);
$this->drupalGet('error-test/trigger-pdo-exception');
$this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.');
// We cannot use assertErrorMessage() since the extact error reported
// varies from database to database. Check that the SQL string is displayed.
$this->assertText($error_pdo_exception['%type'], format_string('Found %type in error page.', $error_pdo_exception));
$this->assertText($error_pdo_exception['!message'], format_string('Found !message in error page.', $error_pdo_exception));
$error_details = format_string('in %function (line ', $error_pdo_exception);
$this->assertRaw($error_details, format_string("Found '!message' in error page.", array('!message' => $error_details)));
}
/**
* Helper function: assert that the error message is found.
*/
function assertErrorMessage(array $error) {
$message = t('%type: !message in %function (line ', $error);
$this->assertRaw($message, format_string('Found error message: !message.', array('!message' => $message)));
}
/**
* Helper function: assert that the error message is not found.
*/
function assertNoErrorMessage(array $error) {
$message = t('%type: !message in %function (line ', $error);
$this->assertNoRaw($message, format_string('Did not find error message: !message.', array('!message' => $message)));
}
}

View file

@ -0,0 +1,12 @@
name = "Error test"
description = "Support module for error and exception testing."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,65 @@
<?php
/**
* Implements hook_menu().
*/
function error_test_menu() {
$items['error-test/generate-warnings'] = array(
'title' => 'Generate warnings',
'page callback' => 'error_test_generate_warnings',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['error-test/generate-warnings-with-report'] = array(
'title' => 'Generate warnings with Simpletest reporting',
'page callback' => 'error_test_generate_warnings',
'page arguments' => array(TRUE),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['error-test/trigger-exception'] = array(
'title' => 'Trigger an exception',
'page callback' => 'error_test_trigger_exception',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['error-test/trigger-pdo-exception'] = array(
'title' => 'Trigger a PDO exception',
'page callback' => 'error_test_trigger_pdo_exception',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Menu callback; generate warnings to test the error handler.
*/
function error_test_generate_warnings($collect_errors = FALSE) {
// Tell Drupal error reporter to send errors to Simpletest or not.
define('SIMPLETEST_COLLECT_ERRORS', $collect_errors);
// This will generate a notice.
$monkey_love = $bananas;
// This will generate a warning.
$awesomely_big = 1/0;
// This will generate a user error.
trigger_error("Drupal is awesome", E_USER_WARNING);
return "";
}
/**
* Menu callback; trigger an exception to test the exception handler.
*/
function error_test_trigger_exception() {
define('SIMPLETEST_COLLECT_ERRORS', FALSE);
throw new Exception("Drupal is awesome");
}
/**
* Menu callback; trigger an exception to test the exception handler.
*/
function error_test_trigger_pdo_exception() {
define('SIMPLETEST_COLLECT_ERRORS', FALSE);
db_query('SELECT * FROM bananas_are_awesome');
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
name = "File test"
description = "Support module for file handling tests."
package = Testing
version = VERSION
core = 7.x
files[] = file_test.module
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,461 @@
<?php
/**
* @file
* Helper module for the file tests.
*
* The caller is must call file_test_reset() to initializing this module before
* calling file_test_get_calls() or file_test_set_return().
*/
define('FILE_URL_TEST_CDN_1', 'http://cdn1.example.com');
define('FILE_URL_TEST_CDN_2', 'http://cdn2.example.com');
/**
* Implements hook_menu().
*/
function file_test_menu() {
$items['file-test/upload'] = array(
'title' => 'Upload test',
'page callback' => 'drupal_get_form',
'page arguments' => array('_file_test_form'),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_stream_wrappers().
*/
function file_test_stream_wrappers() {
return array(
'dummy' => array(
'name' => t('Dummy files'),
'class' => 'DrupalDummyStreamWrapper',
'description' => t('Dummy wrapper for simpletest.'),
),
'dummy-remote' => array(
'name' => t('Dummy files (remote)'),
'class' => 'DrupalDummyRemoteStreamWrapper',
'description' => t('Dummy wrapper for simpletest (remote).'),
),
);
}
/**
* Form to test file uploads.
*/
function _file_test_form($form, &$form_state) {
$form['file_test_upload'] = array(
'#type' => 'file',
'#title' => t('Upload a file'),
);
$form['file_test_replace'] = array(
'#type' => 'select',
'#title' => t('Replace existing image'),
'#options' => array(
FILE_EXISTS_RENAME => t('Appends number until name is unique'),
FILE_EXISTS_REPLACE => t('Replace the existing file'),
FILE_EXISTS_ERROR => t('Fail with an error'),
),
'#default_value' => FILE_EXISTS_RENAME,
);
$form['file_subdir'] = array(
'#type' => 'textfield',
'#title' => t('Subdirectory for test file'),
'#default_value' => '',
);
$form['extensions'] = array(
'#type' => 'textfield',
'#title' => t('Allowed extensions.'),
'#default_value' => '',
);
$form['allow_all_extensions'] = array(
'#type' => 'checkbox',
'#title' => t('Allow all extensions?'),
'#default_value' => FALSE,
);
$form['is_image_file'] = array(
'#type' => 'checkbox',
'#title' => t('Is this an image file?'),
'#default_value' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Process the upload.
*/
function _file_test_form_submit(&$form, &$form_state) {
// Process the upload and perform validation. Note: we're using the
// form value for the $replace parameter.
if (!empty($form_state['values']['file_subdir'])) {
$destination = 'temporary://' . $form_state['values']['file_subdir'];
file_prepare_directory($destination, FILE_CREATE_DIRECTORY);
}
else {
$destination = FALSE;
}
// Setup validators.
$validators = array();
if ($form_state['values']['is_image_file']) {
$validators['file_validate_is_image'] = array();
}
if ($form_state['values']['allow_all_extensions']) {
$validators['file_validate_extensions'] = array();
}
elseif (!empty($form_state['values']['extensions'])) {
$validators['file_validate_extensions'] = array($form_state['values']['extensions']);
}
$file = file_save_upload('file_test_upload', $validators, $destination, $form_state['values']['file_test_replace']);
if ($file) {
$form_state['values']['file_test_upload'] = $file;
drupal_set_message(t('File @filepath was uploaded.', array('@filepath' => $file->uri)));
drupal_set_message(t('File name is @filename.', array('@filename' => $file->filename)));
drupal_set_message(t('File MIME type is @mimetype.', array('@mimetype' => $file->filemime)));
drupal_set_message(t('You WIN!'));
}
elseif ($file === FALSE) {
drupal_set_message(t('Epic upload FAIL!'), 'error');
}
}
/**
* Reset/initialize the history of calls to the file_* hooks.
*
* @see file_test_get_calls()
* @see file_test_reset()
*/
function file_test_reset() {
// Keep track of calls to these hooks
$results = array(
'load' => array(),
'validate' => array(),
'download' => array(),
'insert' => array(),
'update' => array(),
'copy' => array(),
'move' => array(),
'delete' => array(),
);
variable_set('file_test_results', $results);
// These hooks will return these values, see file_test_set_return().
$return = array(
'validate' => array(),
'download' => NULL,
);
variable_set('file_test_return', $return);
}
/**
* Get the arguments passed to invocation of a given hook since
* file_test_reset() was last called.
*
* @param $op
* One of the hook_file_* operations: 'load', 'validate', 'download',
* 'insert', 'update', 'copy', 'move', 'delete'.
*
* @return
* Array of the parameters passed to each call.
*
* @see _file_test_log_call()
* @see file_test_reset()
*/
function file_test_get_calls($op) {
$results = variable_get('file_test_results', array());
return $results[$op];
}
/**
* Get an array with the calls for all hooks.
*
* @return
* An array keyed by hook name ('load', 'validate', 'download', 'insert',
* 'update', 'copy', 'move', 'delete') with values being arrays of parameters
* passed to each call.
*/
function file_test_get_all_calls() {
return variable_get('file_test_results', array());
}
/**
* Store the values passed to a hook invocation.
*
* @param $op
* One of the hook_file_* operations: 'load', 'validate', 'download',
* 'insert', 'update', 'copy', 'move', 'delete'.
* @param $args
* Values passed to hook.
*
* @see file_test_get_calls()
* @see file_test_reset()
*/
function _file_test_log_call($op, $args) {
$results = variable_get('file_test_results', array());
$results[$op][] = $args;
variable_set('file_test_results', $results);
}
/**
* Load the appropriate return value.
*
* @param $op
* One of the hook_file_[validate,download] operations.
*
* @return
* Value set by file_test_set_return().
*
* @see file_test_set_return()
* @see file_test_reset()
*/
function _file_test_get_return($op) {
$return = variable_get('file_test_return', array($op => NULL));
return $return[$op];
}
/**
* Assign a return value for a given operation.
*
* @param $op
* One of the hook_file_[validate,download] operations.
* @param $value
* Value for the hook to return.
*
* @see _file_test_get_return()
* @see file_test_reset()
*/
function file_test_set_return($op, $value) {
$return = variable_get('file_test_return', array());
$return[$op] = $value;
variable_set('file_test_return', $return);
}
/**
* Implements hook_file_load().
*/
function file_test_file_load($files) {
foreach ($files as $file) {
_file_test_log_call('load', array($file));
// Assign a value on the object so that we can test that the $file is passed
// by reference.
$file->file_test['loaded'] = TRUE;
}
}
/**
* Implements hook_file_validate().
*/
function file_test_file_validate($file) {
_file_test_log_call('validate', array($file));
return _file_test_get_return('validate');
}
/**
* Implements hook_file_download().
*/
function file_test_file_download($uri) {
_file_test_log_call('download', array($uri));
return _file_test_get_return('download');
}
/**
* Implements hook_file_insert().
*/
function file_test_file_insert($file) {
_file_test_log_call('insert', array($file));
}
/**
* Implements hook_file_update().
*/
function file_test_file_update($file) {
_file_test_log_call('update', array($file));
}
/**
* Implements hook_file_copy().
*/
function file_test_file_copy($file, $source) {
_file_test_log_call('copy', array($file, $source));
}
/**
* Implements hook_file_move().
*/
function file_test_file_move($file, $source) {
_file_test_log_call('move', array($file, $source));
}
/**
* Implements hook_file_delete().
*/
function file_test_file_delete($file) {
_file_test_log_call('delete', array($file));
}
/**
* Implements hook_file_url_alter().
*/
function file_test_file_url_alter(&$uri) {
// Only run this hook when this variable is set. Otherwise, we'd have to add
// another hidden test module just for this hook.
$alter_mode = variable_get('file_test_hook_file_url_alter', FALSE);
if (!$alter_mode) {
return;
}
// Test alteration of file URLs to use a CDN.
elseif ($alter_mode == 'cdn') {
$cdn_extensions = array('css', 'js', 'gif', 'jpg', 'jpeg', 'png');
// Most CDNs don't support private file transfers without a lot of hassle,
// so don't support this in the common case.
$schemes = array('public');
$scheme = file_uri_scheme($uri);
// Only serve shipped files and public created files from the CDN.
if (!$scheme || in_array($scheme, $schemes)) {
// Shipped files.
if (!$scheme) {
$path = $uri;
}
// Public created files.
else {
$wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
$path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
}
// Clean up Windows paths.
$path = str_replace('\\', '/', $path);
// Serve files with one of the CDN extensions from CDN 1, all others from
// CDN 2.
$pathinfo = pathinfo($path);
if (array_key_exists('extension', $pathinfo) && in_array($pathinfo['extension'], $cdn_extensions)) {
$uri = FILE_URL_TEST_CDN_1 . '/' . $path;
}
else {
$uri = FILE_URL_TEST_CDN_2 . '/' . $path;
}
}
}
// Test alteration of file URLs to use root-relative URLs.
elseif ($alter_mode == 'root-relative') {
// Only serve shipped files and public created files with root-relative
// URLs.
$scheme = file_uri_scheme($uri);
if (!$scheme || $scheme == 'public') {
// Shipped files.
if (!$scheme) {
$path = $uri;
}
// Public created files.
else {
$wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
$path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
}
// Clean up Windows paths.
$path = str_replace('\\', '/', $path);
// Generate a root-relative URL.
$uri = base_path() . '/' . $path;
}
}
// Test alteration of file URLs to use protocol-relative URLs.
elseif ($alter_mode == 'protocol-relative') {
// Only serve shipped files and public created files with protocol-relative
// URLs.
$scheme = file_uri_scheme($uri);
if (!$scheme || $scheme == 'public') {
// Shipped files.
if (!$scheme) {
$path = $uri;
}
// Public created files.
else {
$wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
$path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
}
// Clean up Windows paths.
$path = str_replace('\\', '/', $path);
// Generate a protocol-relative URL.
$uri = '/' . base_path() . '/' . $path;
}
}
}
/**
* Implements hook_file_mimetype_mapping_alter().
*/
function file_test_file_mimetype_mapping_alter(&$mapping) {
// Add new mappings.
$mapping['mimetypes']['file_test_mimetype_1'] = 'madeup/file_test_1';
$mapping['mimetypes']['file_test_mimetype_2'] = 'madeup/file_test_2';
$mapping['mimetypes']['file_test_mimetype_3'] = 'madeup/doc';
$mapping['extensions']['file_test_1'] = 'file_test_mimetype_1';
$mapping['extensions']['file_test_2'] = 'file_test_mimetype_2';
$mapping['extensions']['file_test_3'] = 'file_test_mimetype_2';
// Override existing mapping.
$mapping['extensions']['doc'] = 'file_test_mimetype_3';
}
/**
* Helper class for testing the stream wrapper registry.
*
* Dummy stream wrapper implementation (dummy://).
*/
class DrupalDummyStreamWrapper extends DrupalLocalStreamWrapper {
function getDirectoryPath() {
return variable_get('stream_public_path', 'sites/default/files');
}
/**
* Override getInternalUri().
*
* Return a dummy path for testing.
*/
function getInternalUri() {
return '/dummy/example.txt';
}
/**
* Override getExternalUrl().
*
* Return the HTML URI of a public file.
*/
function getExternalUrl() {
return '/dummy/example.txt';
}
}
/**
* Helper class for testing the stream wrapper registry.
*
* Dummy remote stream wrapper implementation (dummy-remote://).
*
* Basically just the public scheme but not returning a local file for realpath.
*/
class DrupalDummyRemoteStreamWrapper extends DrupalPublicStreamWrapper {
function realpath() {
return FALSE;
}
}

View file

@ -0,0 +1,168 @@
<?php
class FileTranferTest extends DrupalWebTestCase {
protected $hostname = 'localhost';
protected $username = 'drupal';
protected $password = 'password';
protected $port = '42';
public static function getInfo() {
return array(
'name' => 'FileTransfer unit tests',
'description' => 'Test that the jail is respected and that protocols using recursive file move operations work.',
'group' => 'System'
);
}
function setUp() {
parent::setUp();
$this->testConnection = TestFileTransfer::factory(DRUPAL_ROOT, array('hostname' => $this->hostname, 'username' => $this->username, 'password' => $this->password, 'port' => $this->port));
}
function _getFakeModuleFiles() {
$files = array(
'fake.module',
'fake.info',
'theme' => array(
'fake.tpl.php'
),
'inc' => array(
'fake.inc'
)
);
return $files;
}
function _buildFakeModule() {
$location = 'temporary://fake';
if (is_dir($location)) {
$ret = 0;
$output = array();
exec('rm -Rf ' . escapeshellarg($location), $output, $ret);
if ($ret != 0) {
throw new Exception('Error removing fake module directory.');
}
}
$files = $this->_getFakeModuleFiles();
$this->_writeDirectory($location, $files);
return $location;
}
function _writeDirectory($base, $files = array()) {
mkdir($base);
foreach ($files as $key => $file) {
if (is_array($file)) {
$this->_writeDirectory($base . DIRECTORY_SEPARATOR . $key, $file);
}
else {
//just write the filename into the file
file_put_contents($base . DIRECTORY_SEPARATOR . $file, $file);
}
}
}
function testJail() {
$source = $this->_buildFakeModule();
// This convoluted piece of code is here because our testing framework does
// not support expecting exceptions.
$gotit = FALSE;
try {
$this->testConnection->copyDirectory($source, '/tmp');
}
catch (FileTransferException $e) {
$gotit = TRUE;
}
$this->assertTrue($gotit, 'Was not able to copy a directory outside of the jailed area.');
$gotit = TRUE;
try {
$this->testConnection->copyDirectory($source, DRUPAL_ROOT . '/'. variable_get('file_public_path', conf_path() . '/files'));
}
catch (FileTransferException $e) {
$gotit = FALSE;
}
$this->assertTrue($gotit, 'Was able to copy a directory inside of the jailed area');
}
}
/**
* Mock FileTransfer object for test case.
*/
class TestFileTransfer extends FileTransfer {
protected $host = NULL;
protected $username = NULL;
protected $password = NULL;
protected $port = NULL;
/**
* This is for testing the CopyRecursive logic.
*/
public $shouldIsDirectoryReturnTrue = FALSE;
function __construct($jail, $username, $password, $hostname = 'localhost', $port = 9999) {
parent::__construct($jail, $username, $password, $hostname, $port);
}
static function factory($jail, $settings) {
return new TestFileTransfer($jail, $settings['username'], $settings['password'], $settings['hostname'], $settings['port']);
}
function connect() {
$parts = explode(':', $this->hostname);
$port = (count($parts) == 2) ? $parts[1] : $this->port;
$this->connection = new MockTestConnection();
$this->connection->connectionString = 'test://' . urlencode($this->username) . ':' . urlencode($this->password) . "@$this->host:$this->port/";
}
function copyFileJailed($source, $destination) {
$this->connection->run("copyFile $source $destination");
}
protected function removeDirectoryJailed($directory) {
$this->connection->run("rmdir $directory");
}
function createDirectoryJailed($directory) {
$this->connection->run("mkdir $directory");
}
function removeFileJailed($destination) {
if (!ftp_delete($this->connection, $item)) {
throw new FileTransferException('Unable to remove to file @file.', NULL, array('@file' => $item));
}
}
function isDirectory($path) {
return $this->shouldIsDirectoryReturnTrue;
}
function isFile($path) {
return FALSE;
}
function chmodJailed($path, $mode, $recursive) {
return;
}
}
/**
* Mock connection object for test case.
*/
class MockTestConnection {
var $commandsRun = array();
var $connectionString;
function run($cmd) {
$this->commandsRun[] = $cmd;
}
function flushCommands() {
$out = $this->commandsRun;
$this->commandsRun = array();
return $out;
}
}

View file

@ -0,0 +1,12 @@
name = Filter test module
description = Tests filter hooks and functions.
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,64 @@
<?php
/**
* @file
* Test module for Filter module hooks and functions not used in core.
*/
/**
* Implements hook_filter_format_insert().
*/
function filter_test_filter_format_insert($format) {
drupal_set_message('hook_filter_format_insert invoked.');
}
/**
* Implements hook_filter_format_update().
*/
function filter_test_filter_format_update($format) {
drupal_set_message('hook_filter_format_update invoked.');
}
/**
* Implements hook_filter_format_disable().
*/
function filter_test_filter_format_disable($format) {
drupal_set_message('hook_filter_format_disable invoked.');
}
/**
* Implements hook_filter_info().
*/
function filter_test_filter_info() {
$filters['filter_test_uncacheable'] = array(
'title' => 'Uncacheable filter',
'description' => 'Does nothing, but makes a text format uncacheable.',
'cache' => FALSE,
);
$filters['filter_test_replace'] = array(
'title' => 'Testing filter',
'description' => 'Replaces all content with filter and text format information.',
'process callback' => 'filter_test_replace',
);
return $filters;
}
/**
* Implements callback_filter_process().
*
* Process handler for filter_test_replace filter.
*
* Replaces all text with filter and text format information.
*/
function filter_test_replace($text, $filter, $format, $langcode, $cache, $cache_id) {
$text = array();
$text[] = 'Filter: ' . $filter->title . ' (' . $filter->name . ')';
$text[] = 'Format: ' . $format->name . ' (' . $format->format . ')';
$text[] = 'Language: ' . $langcode;
$text[] = 'Cache: ' . ($cache ? 'Enabled' : 'Disabled');
if ($cache_id) {
$text[] = 'Cache ID: ' . $cache_id;
}
return implode("<br />\n", $text);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
<?php
/**
* @file
* An include file to test loading it with the form API.
*/
/**
* Form constructor for testing FAPI file inclusion of the file specified in
* hook_menu().
*/
function form_test_load_include_menu($form, &$form_state) {
// Submit the form via Ajax. That way the FAPI has to care about including
// the file specified in hook_menu().
$ajax_wrapper_id = drupal_html_id('form-test-load-include-menu-ajax-wrapper');
$form['ajax_wrapper'] = array(
'#markup' => '<div id="' . $ajax_wrapper_id . '"></div>',
);
$form['button'] = array(
'#type' => 'submit',
'#value' => t('Save'),
'#submit' => array('form_test_load_include_submit'),
'#ajax' => array(
'wrapper' => $ajax_wrapper_id,
'method' => 'append',
'callback' => 'form_test_load_include_menu_ajax',
),
);
return $form;
}
/**
* Submit callback for the form API file inclusion test forms.
*/
function form_test_load_include_submit($form, $form_state) {
drupal_set_message('Submit callback called.');
}
/**
* Ajax callback for the file inclusion via menu test.
*/
function form_test_load_include_menu_ajax($form) {
// We don't need to return anything, since #ajax['method'] is 'append', which
// does not remove the original #ajax['wrapper'] element, and status messages
// are automatically added by the Ajax framework as long as there's a wrapper
// element to add them to.
return '';
}

View file

@ -0,0 +1,12 @@
name = "FormAPI Test"
description = "Support module for Form API tests."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,195 @@
<?php
/**
* @file
* Provides unit tests for graph.inc.
*/
/**
* Unit tests for the graph handling features.
*/
class GraphUnitTest extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Graph',
'description' => 'Graph handling unit tests.',
'group' => 'System',
);
}
function setUp() {
require_once DRUPAL_ROOT . '/includes/graph.inc';
parent::setUp();
}
/**
* Test depth-first-search features.
*/
function testDepthFirstSearch() {
// The sample graph used is:
// 1 --> 2 --> 3 5 ---> 6
// | ^ ^
// | | |
// | | |
// +---> 4 <-- 7 8 ---> 9
$graph = $this->normalizeGraph(array(
1 => array(2),
2 => array(3, 4),
3 => array(),
4 => array(3),
5 => array(6),
7 => array(4, 5),
8 => array(9),
9 => array(),
));
drupal_depth_first_search($graph);
$expected_paths = array(
1 => array(2, 3, 4),
2 => array(3, 4),
3 => array(),
4 => array(3),
5 => array(6),
7 => array(4, 3, 5, 6),
8 => array(9),
9 => array(),
);
$this->assertPaths($graph, $expected_paths);
$expected_reverse_paths = array(
1 => array(),
2 => array(1),
3 => array(2, 1, 4, 7),
4 => array(2, 1, 7),
5 => array(7),
7 => array(),
8 => array(),
9 => array(8),
);
$this->assertReversePaths($graph, $expected_reverse_paths);
// Assert that DFS didn't created "missing" vertexes automatically.
$this->assertFALSE(isset($graph[6]), 'Vertex 6 has not been created');
$expected_components = array(
array(1, 2, 3, 4, 5, 7),
array(8, 9),
);
$this->assertComponents($graph, $expected_components);
$expected_weights = array(
array(1, 2, 3),
array(2, 4, 3),
array(7, 4, 3),
array(7, 5),
array(8, 9),
);
$this->assertWeights($graph, $expected_weights);
}
/**
* Return a normalized version of a graph.
*/
function normalizeGraph($graph) {
$normalized_graph = array();
foreach ($graph as $vertex => $edges) {
// Create vertex even if it hasn't any edges.
$normalized_graph[$vertex] = array();
foreach ($edges as $edge) {
$normalized_graph[$vertex]['edges'][$edge] = TRUE;
}
}
return $normalized_graph;
}
/**
* Verify expected paths in a graph.
*
* @param $graph
* A graph array processed by drupal_depth_first_search().
* @param $expected_paths
* An associative array containing vertices with their expected paths.
*/
function assertPaths($graph, $expected_paths) {
foreach ($expected_paths as $vertex => $paths) {
// Build an array with keys = $paths and values = TRUE.
$expected = array_fill_keys($paths, TRUE);
$result = isset($graph[$vertex]['paths']) ? $graph[$vertex]['paths'] : array();
$this->assertEqual($expected, $result, format_string('Expected paths for vertex @vertex: @expected-paths, got @paths', array('@vertex' => $vertex, '@expected-paths' => $this->displayArray($expected, TRUE), '@paths' => $this->displayArray($result, TRUE))));
}
}
/**
* Verify expected reverse paths in a graph.
*
* @param $graph
* A graph array processed by drupal_depth_first_search().
* @param $expected_reverse_paths
* An associative array containing vertices with their expected reverse
* paths.
*/
function assertReversePaths($graph, $expected_reverse_paths) {
foreach ($expected_reverse_paths as $vertex => $paths) {
// Build an array with keys = $paths and values = TRUE.
$expected = array_fill_keys($paths, TRUE);
$result = isset($graph[$vertex]['reverse_paths']) ? $graph[$vertex]['reverse_paths'] : array();
$this->assertEqual($expected, $result, format_string('Expected reverse paths for vertex @vertex: @expected-paths, got @paths', array('@vertex' => $vertex, '@expected-paths' => $this->displayArray($expected, TRUE), '@paths' => $this->displayArray($result, TRUE))));
}
}
/**
* Verify expected components in a graph.
*
* @param $graph
* A graph array processed by drupal_depth_first_search().
* @param $expected_components
* An array containing of components defined as a list of their vertices.
*/
function assertComponents($graph, $expected_components) {
$unassigned_vertices = array_fill_keys(array_keys($graph), TRUE);
foreach ($expected_components as $component) {
$result_components = array();
foreach ($component as $vertex) {
$result_components[] = $graph[$vertex]['component'];
unset($unassigned_vertices[$vertex]);
}
$this->assertEqual(1, count(array_unique($result_components)), format_string('Expected one unique component for vertices @vertices, got @components', array('@vertices' => $this->displayArray($component), '@components' => $this->displayArray($result_components))));
}
$this->assertEqual(array(), $unassigned_vertices, format_string('Vertices not assigned to a component: @vertices', array('@vertices' => $this->displayArray($unassigned_vertices, TRUE))));
}
/**
* Verify expected order in a graph.
*
* @param $graph
* A graph array processed by drupal_depth_first_search().
* @param $expected_orders
* An array containing lists of vertices in their expected order.
*/
function assertWeights($graph, $expected_orders) {
foreach ($expected_orders as $order) {
$previous_vertex = array_shift($order);
foreach ($order as $vertex) {
$this->assertTrue($graph[$previous_vertex]['weight'] < $graph[$vertex]['weight'], format_string('Weights of @previous-vertex and @vertex are correct relative to each other', array('@previous-vertex' => $previous_vertex, '@vertex' => $vertex)));
}
}
}
/**
* Helper function to output vertices as comma-separated list.
*
* @param $paths
* An array containing a list of vertices.
* @param $keys
* (optional) Whether to output the keys of $paths instead of the values.
*/
function displayArray($paths, $keys = FALSE) {
if (!empty($paths)) {
return implode(', ', $keys ? array_keys($paths) : $paths);
}
else {
return '(empty)';
}
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* @file
* Fake an HTTP request, for use during testing.
*/
// Set a global variable to indicate a mock HTTP request.
$is_http_mock = !empty($_SERVER['HTTPS']);
// Change to HTTP.
$_SERVER['HTTPS'] = NULL;
ini_set('session.cookie_secure', FALSE);
foreach ($_SERVER as $key => $value) {
$_SERVER[$key] = str_replace('modules/simpletest/tests/http.php', 'index.php', $value);
$_SERVER[$key] = str_replace('https://', 'http://', $_SERVER[$key]);
}
// Change current directory to the Drupal root.
chdir('../../..');
define('DRUPAL_ROOT', getcwd());
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
// Make sure this file can only be used by simpletest.
drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
if (!drupal_valid_test_ua()) {
header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
exit;
}
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
menu_execute_active_handler();

View file

@ -0,0 +1,31 @@
<?php
/**
* @file
* Fake an HTTPS request, for use during testing.
*/
// Set a global variable to indicate a mock HTTPS request.
$is_https_mock = empty($_SERVER['HTTPS']);
// Change to HTTPS.
$_SERVER['HTTPS'] = 'on';
foreach ($_SERVER as $key => $value) {
$_SERVER[$key] = str_replace('modules/simpletest/tests/https.php', 'index.php', $value);
$_SERVER[$key] = str_replace('http://', 'https://', $_SERVER[$key]);
}
// Change current directory to the Drupal root.
chdir('../../..');
define('DRUPAL_ROOT', getcwd());
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
// Make sure this file can only be used by simpletest.
drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
if (!drupal_valid_test_ua()) {
header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
exit;
}
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
menu_execute_active_handler();

View file

@ -0,0 +1,578 @@
<?php
/**
* @file
* Tests for core image handling API.
*/
/**
* Base class for image manipulation testing.
*/
class ImageToolkitTestCase extends DrupalWebTestCase {
protected $toolkit;
protected $file;
protected $image;
function setUp() {
$modules = func_get_args();
if (isset($modules[0]) && is_array($modules[0])) {
$modules = $modules[0];
}
$modules[] = 'image_test';
parent::setUp($modules);
// Use the image_test.module's test toolkit.
$this->toolkit = 'test';
// Pick a file for testing.
$file = current($this->drupalGetTestFiles('image'));
$this->file = $file->uri;
// Setup a dummy image to work with, this replicate image_load() so we
// can avoid calling it.
$this->image = new stdClass();
$this->image->source = $this->file;
$this->image->info = image_get_info($this->file);
$this->image->toolkit = $this->toolkit;
// Clear out any hook calls.
image_test_reset();
}
/**
* Assert that all of the specified image toolkit operations were called
* exactly once once, other values result in failure.
*
* @param $expected
* Array with string containing with the operation name, e.g. 'load',
* 'save', 'crop', etc.
*/
function assertToolkitOperationsCalled(array $expected) {
// Determine which operations were called.
$actual = array_keys(array_filter(image_test_get_all_calls()));
// Determine if there were any expected that were not called.
$uncalled = array_diff($expected, $actual);
if (count($uncalled)) {
$this->assertTrue(FALSE, format_string('Expected operations %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled))));
}
else {
$this->assertTrue(TRUE, format_string('All the expected operations were called: %expected', array('%expected' => implode(', ', $expected))));
}
// Determine if there were any unexpected calls.
$unexpected = array_diff($actual, $expected);
if (count($unexpected)) {
$this->assertTrue(FALSE, format_string('Unexpected operations were called: %unexpected.', array('%unexpected' => implode(', ', $unexpected))));
}
else {
$this->assertTrue(TRUE, 'No unexpected operations were called.');
}
}
}
/**
* Test that the functions in image.inc correctly pass data to the toolkit.
*/
class ImageToolkitUnitTest extends ImageToolkitTestCase {
public static function getInfo() {
return array(
'name' => 'Image toolkit tests',
'description' => 'Check image toolkit functions.',
'group' => 'Image',
);
}
/**
* Check that hook_image_toolkits() is called and only available toolkits are
* returned.
*/
function testGetAvailableToolkits() {
$toolkits = image_get_available_toolkits();
$this->assertTrue(isset($toolkits['test']), 'The working toolkit was returned.');
$this->assertFalse(isset($toolkits['broken']), 'The toolkit marked unavailable was not returned');
$this->assertToolkitOperationsCalled(array());
}
/**
* Test the image_load() function.
*/
function testLoad() {
$image = image_load($this->file, $this->toolkit);
$this->assertTrue(is_object($image), 'Returned an object.');
$this->assertEqual($this->toolkit, $image->toolkit, 'Image had toolkit set.');
$this->assertToolkitOperationsCalled(array('load', 'get_info'));
}
/**
* Test the image_save() function.
*/
function testSave() {
$this->assertFalse(image_save($this->image), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('save'));
}
/**
* Test the image_resize() function.
*/
function testResize() {
$this->assertTrue(image_resize($this->image, 1, 2), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('resize'));
// Check the parameters.
$calls = image_test_get_all_calls();
$this->assertEqual($calls['resize'][0][1], 1, 'Width was passed correctly');
$this->assertEqual($calls['resize'][0][2], 2, 'Height was passed correctly');
}
/**
* Test the image_scale() function.
*/
function testScale() {
// TODO: need to test upscaling
$this->assertTrue(image_scale($this->image, 10, 10), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('resize'));
// Check the parameters.
$calls = image_test_get_all_calls();
$this->assertEqual($calls['resize'][0][1], 10, 'Width was passed correctly');
$this->assertEqual($calls['resize'][0][2], 5, 'Height was based off aspect ratio and passed correctly');
}
/**
* Test the image_scale_and_crop() function.
*/
function testScaleAndCrop() {
$this->assertTrue(image_scale_and_crop($this->image, 5, 10), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('resize', 'crop'));
// Check the parameters.
$calls = image_test_get_all_calls();
$this->assertEqual($calls['crop'][0][1], 7.5, 'X was computed and passed correctly');
$this->assertEqual($calls['crop'][0][2], 0, 'Y was computed and passed correctly');
$this->assertEqual($calls['crop'][0][3], 5, 'Width was computed and passed correctly');
$this->assertEqual($calls['crop'][0][4], 10, 'Height was computed and passed correctly');
}
/**
* Test the image_rotate() function.
*/
function testRotate() {
$this->assertTrue(image_rotate($this->image, 90, 1), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('rotate'));
// Check the parameters.
$calls = image_test_get_all_calls();
$this->assertEqual($calls['rotate'][0][1], 90, 'Degrees were passed correctly');
$this->assertEqual($calls['rotate'][0][2], 1, 'Background color was passed correctly');
}
/**
* Test the image_crop() function.
*/
function testCrop() {
$this->assertTrue(image_crop($this->image, 1, 2, 3, 4), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('crop'));
// Check the parameters.
$calls = image_test_get_all_calls();
$this->assertEqual($calls['crop'][0][1], 1, 'X was passed correctly');
$this->assertEqual($calls['crop'][0][2], 2, 'Y was passed correctly');
$this->assertEqual($calls['crop'][0][3], 3, 'Width was passed correctly');
$this->assertEqual($calls['crop'][0][4], 4, 'Height was passed correctly');
}
/**
* Test the image_desaturate() function.
*/
function testDesaturate() {
$this->assertTrue(image_desaturate($this->image), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('desaturate'));
// Check the parameters.
$calls = image_test_get_all_calls();
$this->assertEqual(count($calls['desaturate'][0]), 1, 'Only the image was passed.');
}
}
/**
* Test the core GD image manipulation functions.
*/
class ImageToolkitGdTestCase extends DrupalWebTestCase {
// Colors that are used in testing.
protected $black = array(0, 0, 0, 0);
protected $red = array(255, 0, 0, 0);
protected $green = array(0, 255, 0, 0);
protected $blue = array(0, 0, 255, 0);
protected $yellow = array(255, 255, 0, 0);
protected $white = array(255, 255, 255, 0);
protected $transparent = array(0, 0, 0, 127);
// Used as rotate background colors.
protected $fuchsia = array(255, 0, 255, 0);
protected $rotate_transparent = array(255, 255, 255, 127);
protected $width = 40;
protected $height = 20;
public static function getInfo() {
return array(
'name' => 'Image GD manipulation tests',
'description' => 'Check that core image manipulations work properly: scale, resize, rotate, crop, scale and crop, and desaturate.',
'group' => 'Image',
);
}
/**
* Function to compare two colors by RGBa.
*/
function colorsAreEqual($color_a, $color_b) {
// Fully transparent pixels are equal, regardless of RGB.
if ($color_a[3] == 127 && $color_b[3] == 127) {
return TRUE;
}
foreach ($color_a as $key => $value) {
if ($color_b[$key] != $value) {
return FALSE;
}
}
return TRUE;
}
/**
* Function for finding a pixel's RGBa values.
*/
function getPixelColor($image, $x, $y) {
$color_index = imagecolorat($image->resource, $x, $y);
$transparent_index = imagecolortransparent($image->resource);
if ($color_index == $transparent_index) {
return array(0, 0, 0, 127);
}
return array_values(imagecolorsforindex($image->resource, $color_index));
}
/**
* Since PHP can't visually check that our images have been manipulated
* properly, build a list of expected color values for each of the corners and
* the expected height and widths for the final images.
*/
function testManipulations() {
// If GD isn't available don't bother testing this.
module_load_include('inc', 'system', 'image.gd');
if (!function_exists('image_gd_check_settings') || !image_gd_check_settings()) {
$this->pass(t('Image manipulations for the GD toolkit were skipped because the GD toolkit is not available.'));
return;
}
// Typically the corner colors will be unchanged. These colors are in the
// order of top-left, top-right, bottom-right, bottom-left.
$default_corners = array($this->red, $this->green, $this->blue, $this->transparent);
// A list of files that will be tested.
$files = array(
'image-test.png',
'image-test.gif',
'image-test-no-transparency.gif',
'image-test.jpg',
);
// Setup a list of tests to perform on each type.
$operations = array(
'resize' => array(
'function' => 'resize',
'arguments' => array(20, 10),
'width' => 20,
'height' => 10,
'corners' => $default_corners,
),
'scale_x' => array(
'function' => 'scale',
'arguments' => array(20, NULL),
'width' => 20,
'height' => 10,
'corners' => $default_corners,
),
'scale_y' => array(
'function' => 'scale',
'arguments' => array(NULL, 10),
'width' => 20,
'height' => 10,
'corners' => $default_corners,
),
'upscale_x' => array(
'function' => 'scale',
'arguments' => array(80, NULL, TRUE),
'width' => 80,
'height' => 40,
'corners' => $default_corners,
),
'upscale_y' => array(
'function' => 'scale',
'arguments' => array(NULL, 40, TRUE),
'width' => 80,
'height' => 40,
'corners' => $default_corners,
),
'crop' => array(
'function' => 'crop',
'arguments' => array(12, 4, 16, 12),
'width' => 16,
'height' => 12,
'corners' => array_fill(0, 4, $this->white),
),
'scale_and_crop' => array(
'function' => 'scale_and_crop',
'arguments' => array(10, 8),
'width' => 10,
'height' => 8,
'corners' => array_fill(0, 4, $this->black),
),
);
// Systems using non-bundled GD2 don't have imagerotate. Test if available.
if (function_exists('imagerotate')) {
$operations += array(
'rotate_90' => array(
'function' => 'rotate',
'arguments' => array(90, 0xFF00FF), // Fuchsia background.
'width' => 20,
'height' => 40,
'corners' => array($this->fuchsia, $this->red, $this->green, $this->blue),
),
'rotate_transparent_90' => array(
'function' => 'rotate',
'arguments' => array(90),
'width' => 20,
'height' => 40,
'corners' => array($this->transparent, $this->red, $this->green, $this->blue),
),
);
// As of PHP version 5.5, GD uses a different algorithm to rotate images
// than version 5.4 and below, resulting in different dimensions.
// See https://bugs.php.net/bug.php?id=65148.
// For the 40x20 test images, the dimensions resulting from rotation will
// be 1 pixel smaller in both width and height in PHP 5.5 and above.
// @todo: If and when the PHP bug gets solved, add an upper limit
// version check.
if (version_compare(PHP_VERSION, '5.5', '>=')) {
$operations += array(
'rotate_5' => array(
'function' => 'rotate',
'arguments' => array(5, 0xFF00FF), // Fuchsia background.
'width' => 41,
'height' => 23,
'corners' => array_fill(0, 4, $this->fuchsia),
),
'rotate_transparent_5' => array(
'function' => 'rotate',
'arguments' => array(5),
'width' => 41,
'height' => 23,
'corners' => array_fill(0, 4, $this->rotate_transparent),
),
);
}
else {
$operations += array(
'rotate_5' => array(
'function' => 'rotate',
'arguments' => array(5, 0xFF00FF), // Fuchsia background.
'width' => 42,
'height' => 24,
'corners' => array_fill(0, 4, $this->fuchsia),
),
'rotate_transparent_5' => array(
'function' => 'rotate',
'arguments' => array(5),
'width' => 42,
'height' => 24,
'corners' => array_fill(0, 4, $this->rotate_transparent),
),
);
}
}
// Systems using non-bundled GD2 don't have imagefilter. Test if available.
if (function_exists('imagefilter')) {
$operations += array(
'desaturate' => array(
'function' => 'desaturate',
'arguments' => array(),
'height' => 20,
'width' => 40,
// Grayscale corners are a bit funky. Each of the corners are a shade of
// gray. The values of these were determined simply by looking at the
// final image to see what desaturated colors end up being.
'corners' => array(
array_fill(0, 3, 76) + array(3 => 0),
array_fill(0, 3, 149) + array(3 => 0),
array_fill(0, 3, 29) + array(3 => 0),
array_fill(0, 3, 225) + array(3 => 127)
),
),
);
}
foreach ($files as $file) {
foreach ($operations as $op => $values) {
// Load up a fresh image.
$image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, 'gd');
if (!$image) {
$this->fail(t('Could not load image %file.', array('%file' => $file)));
continue 2;
}
// All images should be converted to truecolor when loaded.
$image_truecolor = imageistruecolor($image->resource);
$this->assertTrue($image_truecolor, format_string('Image %file after load is a truecolor image.', array('%file' => $file)));
if ($image->info['extension'] == 'gif') {
if ($op == 'desaturate') {
// Transparent GIFs and the imagefilter function don't work together.
$values['corners'][3][3] = 0;
}
}
// Perform our operation.
$function = 'image_' . $values['function'];
$arguments = array();
$arguments[] = &$image;
$arguments = array_merge($arguments, $values['arguments']);
call_user_func_array($function, $arguments);
// To keep from flooding the test with assert values, make a general
// value for whether each group of values fail.
$correct_dimensions_real = TRUE;
$correct_dimensions_object = TRUE;
$correct_colors = TRUE;
// Check the real dimensions of the image first.
if (imagesy($image->resource) != $values['height'] || imagesx($image->resource) != $values['width']) {
$correct_dimensions_real = FALSE;
}
// Check that the image object has an accurate record of the dimensions.
if ($image->info['width'] != $values['width'] || $image->info['height'] != $values['height']) {
$correct_dimensions_object = FALSE;
}
// Now check each of the corners to ensure color correctness.
foreach ($values['corners'] as $key => $corner) {
// The test gif that does not have transparency has yellow where the
// others have transparent.
if ($file === 'image-test-no-transparency.gif' && $corner === $this->transparent) {
$corner = $this->yellow;
}
// Get the location of the corner.
switch ($key) {
case 0:
$x = 0;
$y = 0;
break;
case 1:
$x = $values['width'] - 1;
$y = 0;
break;
case 2:
$x = $values['width'] - 1;
$y = $values['height'] - 1;
break;
case 3:
$x = 0;
$y = $values['height'] - 1;
break;
}
$color = $this->getPixelColor($image, $x, $y);
$correct_colors = $this->colorsAreEqual($color, $corner);
}
$directory = file_default_scheme() . '://imagetests';
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
$file_path = $directory . '/' . $op . '.' . $image->info['extension'];
image_save($image, $file_path);
$this->assertTrue($correct_dimensions_real, format_string('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op)));
$this->assertTrue($correct_dimensions_object, format_string('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op)));
// JPEG colors will always be messed up due to compression.
if ($image->info['extension'] != 'jpg') {
$this->assertTrue($correct_colors, format_string('Image %file object after %action action has the correct color placement.', array('%file' => $file, '%action' => $op)));
}
}
// Check that saved image reloads without raising PHP errors.
$image_reloaded = image_load($file_path);
}
}
/**
* Tests loading an image whose transparent color index is out of range.
*/
function testTransparentColorOutOfRange() {
// This image was generated by taking an initial image with a palette size
// of 6 colors, and setting the transparent color index to 6 (one higher
// than the largest allowed index), as follows:
// @code
// $image = imagecreatefromgif('modules/simpletest/files/image-test.gif');
// imagecolortransparent($image, 6);
// imagegif($image, 'modules/simpletest/files/image-test-transparent-out-of-range.gif');
// @endcode
// This allows us to test that an image with an out-of-range color index
// can be loaded correctly.
$file = 'image-test-transparent-out-of-range.gif';
$image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file);
if (!$image) {
$this->fail(format_string('Could not load image %file.', array('%file' => $file)));
}
else {
// All images should be converted to truecolor when loaded.
$image_truecolor = imageistruecolor($image->resource);
$this->assertTrue($image_truecolor, format_string('Image %file after load is a truecolor image.', array('%file' => $file)));
}
}
}
/**
* Tests the file move function for managed files.
*/
class ImageFileMoveTest extends ImageToolkitTestCase {
public static function getInfo() {
return array(
'name' => 'Image moving',
'description' => 'Tests the file move function for managed files.',
'group' => 'Image',
);
}
/**
* Tests moving a randomly generated image.
*/
function testNormal() {
// Pick a file for testing.
$file = current($this->drupalGetTestFiles('image'));
// Create derivative image.
$style = image_style_load(key(image_styles()));
$derivative_uri = image_style_path($style['name'], $file->uri);
image_style_create_derivative($style, $file->uri, $derivative_uri);
// Check if derivative image exists.
$this->assertTrue(file_exists($derivative_uri), 'Make sure derivative image is generated successfully.');
// Clone the object so we don't have to worry about the function changing
// our reference copy.
$desired_filepath = 'public://' . $this->randomName();
$result = file_move(clone $file, $desired_filepath, FILE_EXISTS_ERROR);
// Check if image has been moved.
$this->assertTrue(file_exists($result->uri), 'Make sure image is moved successfully.');
// Check if derivative image has been flushed.
$this->assertFalse(file_exists($derivative_uri), 'Make sure derivative image has been flushed.');
}
}

View file

@ -0,0 +1,12 @@
name = "Image test"
description = "Support module for image toolkit tests."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,138 @@
<?php
/**
* @file
* Helper module for the image tests.
*/
/**
* Implements hook_image_toolkits().
*/
function image_test_image_toolkits() {
return array(
'test' => array(
'title' => t('A dummy toolkit that works'),
'available' => TRUE,
),
'broken' => array(
'title' => t('A dummy toolkit that is "broken"'),
'available' => FALSE,
),
);
}
/**
* Reset/initialize the history of calls to the toolkit functions.
*
* @see image_test_get_all_calls()
*/
function image_test_reset() {
// Keep track of calls to these operations
$results = array(
'load' => array(),
'save' => array(),
'settings' => array(),
'resize' => array(),
'rotate' => array(),
'crop' => array(),
'desaturate' => array(),
);
variable_set('image_test_results', $results);
}
/**
* Get an array with the all the calls to the toolkits since image_test_reset()
* was called.
*
* @return
* An array keyed by operation name ('load', 'save', 'settings', 'resize',
* 'rotate', 'crop', 'desaturate') with values being arrays of parameters
* passed to each call.
*/
function image_test_get_all_calls() {
return variable_get('image_test_results', array());
}
/**
* Store the values passed to a toolkit call.
*
* @param $op
* One of the image toolkit operations: 'get_info', 'load', 'save',
* 'settings', 'resize', 'rotate', 'crop', 'desaturate'.
* @param $args
* Values passed to hook.
*
* @see image_test_get_all_calls()
* @see image_test_reset()
*/
function _image_test_log_call($op, $args) {
$results = variable_get('image_test_results', array());
$results[$op][] = $args;
variable_set('image_test_results', $results);
}
/**
* Image tookit's settings operation.
*/
function image_test_settings() {
_image_test_log_call('settings', array());
return array();
}
/**
* Image toolkit's get_info operation.
*/
function image_test_get_info(stdClass $image) {
_image_test_log_call('get_info', array($image));
return array();
}
/**
* Image tookit's load operation.
*/
function image_test_load(stdClass $image) {
_image_test_log_call('load', array($image));
return $image;
}
/**
* Image tookit's save operation.
*/
function image_test_save(stdClass $image, $destination) {
_image_test_log_call('save', array($image, $destination));
// Return false so that image_save() doesn't try to chmod the destination
// file that we didn't bother to create.
return FALSE;
}
/**
* Image tookit's crop operation.
*/
function image_test_crop(stdClass $image, $x, $y, $width, $height) {
_image_test_log_call('crop', array($image, $x, $y, $width, $height));
return TRUE;
}
/**
* Image tookit's resize operation.
*/
function image_test_resize(stdClass $image, $width, $height) {
_image_test_log_call('resize', array($image, $width, $height));
return TRUE;
}
/**
* Image tookit's rotate operation.
*/
function image_test_rotate(stdClass $image, $degrees, $background = NULL) {
_image_test_log_call('rotate', array($image, $degrees, $background));
return TRUE;
}
/**
* Image tookit's desaturate operation.
*/
function image_test_desaturate(stdClass $image) {
_image_test_log_call('desaturate', array($image));
return TRUE;
}

View file

@ -0,0 +1,57 @@
<?php
/**
* Tests for the lock system.
*/
class LockFunctionalTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Locking framework tests',
'description' => 'Confirm locking works between two separate requests.',
'group' => 'System',
);
}
function setUp() {
parent::setUp('system_test');
}
/**
* Confirm that we can acquire and release locks in two parallel requests.
*/
function testLockAcquire() {
$lock_acquired = 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
$lock_not_acquired = 'FALSE: Lock not acquired in system_test_lock_acquire()';
$this->assertTrue(lock_acquire('system_test_lock_acquire'), 'Lock acquired by this request.', 'Lock');
$this->assertTrue(lock_acquire('system_test_lock_acquire'), 'Lock extended by this request.', 'Lock');
lock_release('system_test_lock_acquire');
// Cause another request to acquire the lock.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_acquired, 'Lock acquired by the other request.', 'Lock');
// The other request has finished, thus it should have released its lock.
$this->assertTrue(lock_acquire('system_test_lock_acquire'), 'Lock acquired by this request.', 'Lock');
// This request holds the lock, so the other request cannot acquire it.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_not_acquired, 'Lock not acquired by the other request.', 'Lock');
lock_release('system_test_lock_acquire');
// Try a very short timeout and lock breaking.
$this->assertTrue(lock_acquire('system_test_lock_acquire', 0.5), 'Lock acquired by this request.', 'Lock');
sleep(1);
// The other request should break our lock.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_acquired, 'Lock acquired by the other request, breaking our lock.', 'Lock');
// We cannot renew it, since the other thread took it.
$this->assertFalse(lock_acquire('system_test_lock_acquire'), 'Lock cannot be extended by this request.', 'Lock');
// Check the shut-down function.
$lock_acquired_exit = 'TRUE: Lock successfully acquired in system_test_lock_exit()';
$lock_not_acquired_exit = 'FALSE: Lock not acquired in system_test_lock_exit()';
$this->drupalGet('system-test/lock-exit');
$this->assertText($lock_acquired_exit, 'Lock acquired by the other request before exit.', 'Lock');
$this->assertTrue(lock_acquire('system_test_lock_exit'), 'Lock acquired by this request after the other request exits.', 'Lock');
}
}

View file

@ -0,0 +1,460 @@
<?php
/**
* @file
* Test the Drupal mailing system.
*/
class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
/**
* The most recent message that was sent through the test case.
*
* We take advantage here of the fact that static variables are shared among
* all instance of the same class.
*/
private static $sent_message;
public static function getInfo() {
return array(
'name' => 'Mail system',
'description' => 'Performs tests on the pluggable mailing framework.',
'group' => 'System',
);
}
function setUp() {
parent::setUp(array('simpletest'));
// Set MailTestCase (i.e. this class) as the SMTP library
variable_set('mail_system', array('default-system' => 'MailTestCase'));
}
/**
* Assert that the pluggable mail system is functional.
*/
function testPluggableFramework() {
global $language;
// Use MailTestCase for sending a message.
$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@example.com', '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.');
}
/**
* Concatenate and wrap the e-mail body for plain-text mails.
*
* @see DefaultMailSystem
*/
public function format(array $message) {
// Join the body array into one string.
$message['body'] = implode("\n\n", $message['body']);
// Convert any HTML to plain-text.
$message['body'] = drupal_html_to_text($message['body']);
// Wrap the mail body for sending.
$message['body'] = drupal_wrap_mail($message['body']);
return $message;
}
/**
* Send function that is called through the mail system.
*/
public function mail(array $message) {
self::$sent_message = $message;
}
}
/**
* Unit tests for drupal_html_to_text().
*/
class DrupalHtmlToTextTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'HTML to text conversion',
'description' => 'Tests drupal_html_to_text().',
'group' => 'Mail',
);
}
/**
* Converts a string to its PHP source equivalent for display in test messages.
*
* @param $text
* The text string to convert.
*
* @return
* An HTML representation of the text string that, when displayed in a
* browser, represents the PHP source code equivalent of $text.
*/
function stringToHtml($text) {
return '"' .
str_replace(
array("\n", ' '),
array('\n', '&nbsp;'),
check_plain($text)
) . '"';
}
/**
* Helper function for testing drupal_html_to_text().
*
* @param $html
* The source HTML string to be converted.
* @param $text
* The expected result of converting $html to text.
* @param $message
* A text message to display in the assertion message.
* @param $allowed_tags
* (optional) An array of allowed tags, or NULL to default to the full
* set of tags supported by drupal_html_to_text().
*/
function assertHtmlToText($html, $text, $message, $allowed_tags = NULL) {
preg_match_all('/<([a-z0-6]+)/', drupal_strtolower($html), $matches);
$tested_tags = implode(', ', array_unique($matches[1]));
$message .= ' (' . $tested_tags . ')';
$result = drupal_html_to_text($html, $allowed_tags);
$pass = $this->assertEqual($result, $text, check_plain($message));
$verbose = 'html = <pre>' . $this->stringToHtml($html)
. '</pre><br />' . 'result = <pre>' . $this->stringToHtml($result)
. '</pre><br />' . 'expected = <pre>' . $this->stringToHtml($text)
. '</pre>';
$this->verbose($verbose);
if (!$pass) {
$this->pass("Previous test verbose info:<br />$verbose");
}
}
/**
* Test all supported tags of drupal_html_to_text().
*/
function testTags() {
global $base_path, $base_url;
$tests = array(
// @todo Trailing linefeeds should be trimmed.
'<a href = "http://drupal.org">Drupal.org</a>' => "Drupal.org [1]\n\n[1] http://drupal.org\n",
// @todo Footer URLs should be absolute.
"<a href = \"$base_path\">Homepage</a>" => "Homepage [1]\n\n[1] $base_url/\n",
'<address>Drupal</address>' => "Drupal\n",
// @todo The <address> tag is currently not supported.
'<address>Drupal</address><address>Drupal</address>' => "DrupalDrupal\n",
'<b>Drupal</b>' => "*Drupal*\n",
// @todo There should be a space between the '>' and the text.
'<blockquote>Drupal</blockquote>' => ">Drupal\n",
'<blockquote>Drupal</blockquote><blockquote>Drupal</blockquote>' => ">Drupal\n>Drupal\n",
'<br />Drupal<br />Drupal<br /><br />Drupal' => "Drupal\nDrupal\nDrupal\n",
'<br/>Drupal<br/>Drupal<br/><br/>Drupal' => "Drupal\nDrupal\nDrupal\n",
// @todo There should be two line breaks before the paragraph.
'<br/>Drupal<br/>Drupal<br/><br/>Drupal<p>Drupal</p>' => "Drupal\nDrupal\nDrupal\nDrupal\n\n",
'<div>Drupal</div>' => "Drupal\n",
// @todo The <div> tag is currently not supported.
'<div>Drupal</div><div>Drupal</div>' => "DrupalDrupal\n",
'<em>Drupal</em>' => "/Drupal/\n",
'<h1>Drupal</h1>' => "======== DRUPAL ==============================================================\n\n",
'<h1>Drupal</h1><p>Drupal</p>' => "======== DRUPAL ==============================================================\n\nDrupal\n\n",
'<h2>Drupal</h2>' => "-------- DRUPAL --------------------------------------------------------------\n\n",
'<h2>Drupal</h2><p>Drupal</p>' => "-------- DRUPAL --------------------------------------------------------------\n\nDrupal\n\n",
'<h3>Drupal</h3>' => ".... Drupal\n\n",
'<h3>Drupal</h3><p>Drupal</p>' => ".... Drupal\n\nDrupal\n\n",
'<h4>Drupal</h4>' => ".. Drupal\n\n",
'<h4>Drupal</h4><p>Drupal</p>' => ".. Drupal\n\nDrupal\n\n",
'<h5>Drupal</h5>' => "Drupal\n\n",
'<h5>Drupal</h5><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
'<h6>Drupal</h6>' => "Drupal\n\n",
'<h6>Drupal</h6><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
'<hr />Drupal<hr />' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n",
'<hr/>Drupal<hr/>' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n",
'<hr/>Drupal<hr/><p>Drupal</p>' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n\n",
'<i>Drupal</i>' => "/Drupal/\n",
'<p>Drupal</p>' => "Drupal\n\n",
'<p>Drupal</p><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
'<strong>Drupal</strong>' => "*Drupal*\n",
// @todo Tables are currently not supported.
'<table><tr><td>Drupal</td><td>Drupal</td></tr><tr><td>Drupal</td><td>Drupal</td></tr></table>' => "DrupalDrupalDrupalDrupal\n",
'<table><tr><td>Drupal</td></tr></table><p>Drupal</p>' => "Drupal\nDrupal\n\n",
// @todo The <u> tag is currently not supported.
'<u>Drupal</u>' => "Drupal\n",
'<ul><li>Drupal</li></ul>' => " * Drupal\n\n",
'<ul><li>Drupal <em>Drupal</em> Drupal</li></ul>' => " * Drupal /Drupal/ Drupal\n\n",
// @todo Lines containing nothing but spaces should be trimmed.
'<ul><li>Drupal</li><li><ol><li>Drupal</li><li>Drupal</li></ol></li></ul>' => " * Drupal\n * 1) Drupal\n 2) Drupal\n \n\n",
'<ul><li>Drupal</li><li><ol><li>Drupal</li></ol></li><li>Drupal</li></ul>' => " * Drupal\n * 1) Drupal\n \n * Drupal\n\n",
'<ul><li>Drupal</li><li>Drupal</li></ul>' => " * Drupal\n * Drupal\n\n",
'<ul><li>Drupal</li></ul><p>Drupal</p>' => " * Drupal\n\nDrupal\n\n",
'<ol><li>Drupal</li></ol>' => " 1) Drupal\n\n",
'<ol><li>Drupal</li><li><ul><li>Drupal</li><li>Drupal</li></ul></li></ol>' => " 1) Drupal\n 2) * Drupal\n * Drupal\n \n\n",
'<ol><li>Drupal</li><li>Drupal</li></ol>' => " 1) Drupal\n 2) Drupal\n\n",
'<ol>Drupal</ol>' => "Drupal\n\n",
'<ol><li>Drupal</li></ol><p>Drupal</p>' => " 1) Drupal\n\nDrupal\n\n",
'<dl><dt>Drupal</dt></dl>' => "Drupal\n\n",
'<dl><dt>Drupal</dt><dd>Drupal</dd></dl>' => "Drupal\n Drupal\n\n",
'<dl><dt>Drupal</dt><dd>Drupal</dd><dt>Drupal</dt><dd>Drupal</dd></dl>' => "Drupal\n Drupal\nDrupal\n Drupal\n\n",
'<dl><dt>Drupal</dt><dd>Drupal</dd></dl><p>Drupal</p>' => "Drupal\n Drupal\n\nDrupal\n\n",
'<dl><dt>Drupal<dd>Drupal</dl>' => "Drupal\n Drupal\n\n",
'<dl><dt>Drupal</dt></dl><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
// @todo Again, lines containing only spaces should be trimmed.
'<ul><li>Drupal</li><li><dl><dt>Drupal</dt><dd>Drupal</dd><dt>Drupal</dt><dd>Drupal</dd></dl></li><li>Drupal</li></ul>' => " * Drupal\n * Drupal\n Drupal\n Drupal\n Drupal\n \n * Drupal\n\n",
// Tests malformed HTML tags.
'<br>Drupal<br>Drupal' => "Drupal\nDrupal\n",
'<hr>Drupal<hr>Drupal' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n",
'<ol><li>Drupal<li>Drupal</ol>' => " 1) Drupal\n 2) Drupal\n\n",
'<ul><li>Drupal <em>Drupal</em> Drupal</ul></ul>' => " * Drupal /Drupal/ Drupal\n\n",
'<ul><li>Drupal<li>Drupal</ol>' => " * Drupal\n * Drupal\n\n",
'<ul><li>Drupal<li>Drupal</ul>' => " * Drupal\n * Drupal\n\n",
'<ul>Drupal</ul>' => "Drupal\n\n",
'Drupal</ul></ol></dl><li>Drupal' => "Drupal\n * Drupal\n",
'<dl>Drupal</dl>' => "Drupal\n\n",
'<dl>Drupal</dl><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
'<dt>Drupal</dt>' => "Drupal\n",
// Tests some unsupported HTML tags.
'<html>Drupal</html>' => "Drupal\n",
// @todo Perhaps the contents of <script> tags should be dropped.
'<script type="text/javascript">Drupal</script>' => "Drupal\n",
);
foreach ($tests as $html => $text) {
$this->assertHtmlToText($html, $text, 'Supported tags');
}
}
/**
* Test $allowed_tags argument of drupal_html_to_text().
*/
function testDrupalHtmlToTextArgs() {
// The second parameter of drupal_html_to_text() overrules the allowed tags.
$this->assertHtmlToText(
'Drupal <b>Drupal</b> Drupal',
"Drupal *Drupal* Drupal\n",
'Allowed <b> tag found',
array('b')
);
$this->assertHtmlToText(
'Drupal <h1>Drupal</h1> Drupal',
"Drupal Drupal Drupal\n",
'Disallowed <h1> tag not found',
array('b')
);
$this->assertHtmlToText(
'Drupal <p><em><b>Drupal</b></em><p> Drupal',
"Drupal Drupal Drupal\n",
'Disallowed <p>, <em>, and <b> tags not found',
array('a', 'br', 'h1')
);
$this->assertHtmlToText(
'<html><body>Drupal</body></html>',
"Drupal\n",
'Unsupported <html> and <body> tags not found',
array('html', 'body')
);
}
/**
* Tests that drupal_wrap_mail() removes trailing whitespace before newlines.
*/
function testDrupalHtmltoTextRemoveTrailingWhitespace() {
$text = "Hi there! \nHerp Derp";
$mail_lines = explode("\n", drupal_wrap_mail($text));
$this->assertNotEqual(" ", substr($mail_lines[0], -1), 'Trailing whitespace removed.');
}
/**
* Tests drupal_wrap_mail() retains whitespace from Usenet style signatures.
*
* RFC 3676 says, "This is a special case; an (optionally quoted or quoted and
* stuffed) line consisting of DASH DASH SP is neither fixed nor flowed."
*/
function testDrupalHtmltoTextUsenetSignature() {
$text = "Hi there!\n-- \nHerp Derp";
$mail_lines = explode("\n", drupal_wrap_mail($text));
$this->assertEqual("-- ", $mail_lines[1], 'Trailing whitespace not removed for dash-dash-space signatures.');
$text = "Hi there!\n-- \nHerp Derp";
$mail_lines = explode("\n", drupal_wrap_mail($text));
$this->assertEqual("--", $mail_lines[1], 'Trailing whitespace removed for incorrect dash-dash-space signatures.');
}
/**
* Test that whitespace is collapsed.
*/
function testDrupalHtmltoTextCollapsesWhitespace() {
$input = "<p>Drupal Drupal\n\nDrupal<pre>Drupal Drupal\n\nDrupal</pre>Drupal Drupal\n\nDrupal</p>";
// @todo The whitespace should be collapsed.
$collapsed = "Drupal Drupal\n\nDrupalDrupal Drupal\n\nDrupalDrupal Drupal\n\nDrupal\n\n";
$this->assertHtmlToText(
$input,
$collapsed,
'Whitespace is collapsed',
array('p')
);
}
/**
* Test that text separated by block-level tags in HTML get separated by
* (at least) a newline in the plaintext version.
*/
function testDrupalHtmlToTextBlockTagToNewline() {
$input = '[text]'
. '<blockquote>[blockquote]</blockquote>'
. '<br />[br]'
. '<dl><dt>[dl-dt]</dt>'
. '<dt>[dt]</dt>'
. '<dd>[dd]</dd>'
. '<dd>[dd-dl]</dd></dl>'
. '<h1>[h1]</h1>'
. '<h2>[h2]</h2>'
. '<h3>[h3]</h3>'
. '<h4>[h4]</h4>'
. '<h5>[h5]</h5>'
. '<h6>[h6]</h6>'
. '<hr />[hr]'
. '<ol><li>[ol-li]</li>'
. '<li>[li]</li>'
. '<li>[li-ol]</li></ol>'
. '<p>[p]</p>'
. '<ul><li>[ul-li]</li>'
. '<li>[li-ul]</li></ul>'
. '[text]';
$output = drupal_html_to_text($input);
$pass = $this->assertFalse(
preg_match('/\][^\n]*\[/s', $output),
'Block-level HTML tags should force newlines'
);
if (!$pass) {
$this->verbose($this->stringToHtml($output));
}
$output_upper = drupal_strtoupper($output);
$upper_input = drupal_strtoupper($input);
$upper_output = drupal_html_to_text($upper_input);
$pass = $this->assertEqual(
$upper_output,
$output_upper,
'Tag recognition should be case-insensitive'
);
if (!$pass) {
$this->verbose(
$upper_output
. '<br />should be equal to <br />'
. $output_upper
);
}
}
/**
* Test that headers are properly separated from surrounding text.
*/
function testHeaderSeparation() {
$html = 'Drupal<h1>Drupal</h1>Drupal';
// @todo There should be more space above the header than below it.
$text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n";
$this->assertHtmlToText($html, $text,
'Text before and after <h1> tag');
$html = '<p>Drupal</p><h1>Drupal</h1>Drupal';
// @todo There should be more space above the header than below it.
$text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n";
$this->assertHtmlToText($html, $text,
'Paragraph before and text after <h1> tag');
$html = 'Drupal<h1>Drupal</h1><p>Drupal</p>';
// @todo There should be more space above the header than below it.
$text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n\n";
$this->assertHtmlToText($html, $text,
'Text before and paragraph after <h1> tag');
$html = '<p>Drupal</p><h1>Drupal</h1><p>Drupal</p>';
$text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n\n";
$this->assertHtmlToText($html, $text,
'Paragraph before and after <h1> tag');
}
/**
* Test that footnote references are properly generated.
*/
function testFootnoteReferences() {
global $base_path, $base_url;
$source = '<a href="http://www.example.com/node/1">Host and path</a>'
. '<br /><a href="http://www.example.com">Host, no path</a>'
. '<br /><a href="' . $base_path . 'node/1">Path, no host</a>'
. '<br /><a href="node/1">Relative path</a>';
// @todo Footnote URLs should be absolute.
$tt = "Host and path [1]"
. "\nHost, no path [2]"
// @todo The following two references should be combined.
. "\nPath, no host [3]"
. "\nRelative path [4]"
. "\n"
. "\n[1] http://www.example.com/node/1"
. "\n[2] http://www.example.com"
// @todo The following two references should be combined.
. "\n[3] $base_url/node/1"
. "\n[4] node/1\n";
$this->assertHtmlToText($source, $tt, 'Footnotes');
}
/**
* Test that combinations of paragraph breaks, line breaks, linefeeds,
* and spaces are properly handled.
*/
function testDrupalHtmlToTextParagraphs() {
$tests = array();
$tests[] = array(
'html' => "<p>line 1<br />\nline 2<br />line 3\n<br />line 4</p><p>paragraph</p>",
// @todo Trailing line breaks should be trimmed.
'text' => "line 1\nline 2\nline 3\nline 4\n\nparagraph\n\n",
);
$tests[] = array(
'html' => "<p>line 1<br /> line 2</p> <p>line 4<br /> line 5</p> <p>0</p>",
// @todo Trailing line breaks should be trimmed.
'text' => "line 1\nline 2\n\nline 4\nline 5\n\n0\n\n",
);
foreach ($tests as $test) {
$this->assertHtmlToText($test['html'], $test['text'], 'Paragraph breaks');
}
}
/**
* Tests that drupal_html_to_text() wraps before 1000 characters.
*
* RFC 3676 says, "The Text/Plain media type is the lowest common
* denominator of Internet email, with lines of no more than 998 characters."
*
* RFC 2046 says, "SMTP [RFC-821] allows a maximum of 998 octets before the
* next CRLF sequence."
*
* RFC 821 says, "The maximum total length of a text line including the
* <CRLF> is 1000 characters."
*/
function testVeryLongLineWrap() {
$input = 'Drupal<br /><p>' . str_repeat('x', 2100) . '</p><br />Drupal';
$output = drupal_html_to_text($input);
// This awkward construct comes from includes/mail.inc lines 8-13.
$eol = variable_get('mail_line_endings', MAIL_LINE_ENDINGS);
// We must use strlen() rather than drupal_strlen() in order to count
// octets rather than characters.
$line_length_limit = 1000 - drupal_strlen($eol);
$maximum_line_length = 0;
foreach (explode($eol, $output) as $line) {
// We must use strlen() rather than drupal_strlen() in order to count
// octets rather than characters.
$maximum_line_length = max($maximum_line_length, strlen($line . $eol));
}
$verbose = 'Maximum line length found was ' . $maximum_line_length . ' octets.';
$this->assertTrue($maximum_line_length <= 1000, $verbose);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
name = "Hook menu tests"
description = "Support module for menu hook testing."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,563 @@
<?php
/**
* @file
* Dummy module implementing hook menu.
*/
/**
* Implements hook_menu().
*/
function menu_test_menu() {
// The name of the menu changes during the course of the test. Using a $_GET.
$items['menu_name_test'] = array(
'title' => 'Test menu_name router item',
'page callback' => 'node_save',
'menu_name' => menu_test_menu_name(),
);
// This item is of type MENU_CALLBACK with no parents to test title.
$items['menu_callback_title'] = array(
'title' => 'Menu Callback Title',
'page callback' => 'menu_test_callback',
'type' => MENU_CALLBACK,
'access arguments' => array('access content'),
);
// Use FALSE as 'title callback' to bypass t().
$items['menu_no_title_callback'] = array(
'title' => 'A title with @placeholder',
'title callback' => FALSE,
'title arguments' => array('@placeholder' => 'some other text'),
'page callback' => 'menu_test_callback',
'access arguments' => array('access content'),
);
// Hidden link for menu_link_maintain tests
$items['menu_test_maintain/%'] = array(
'title' => 'Menu maintain test',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
);
// Hierarchical tests.
$items['menu-test/hierarchy/parent'] = array(
'title' => 'Parent menu router',
'page callback' => 'node_page_default',
);
$items['menu-test/hierarchy/parent/child'] = array(
'title' => 'Child menu router',
'page callback' => 'node_page_default',
);
$items['menu-test/hierarchy/parent/child2/child'] = array(
'title' => 'Unattached subchild router',
'page callback' => 'node_page_default',
);
// Theme callback tests.
$items['menu-test/theme-callback/%'] = array(
'title' => 'Page that displays different themes',
'page callback' => 'menu_test_theme_page_callback',
'access arguments' => array('access content'),
'theme callback' => 'menu_test_theme_callback',
'theme arguments' => array(2),
);
$items['menu-test/theme-callback/%/inheritance'] = array(
'title' => 'Page that tests theme callback inheritance.',
'page callback' => 'menu_test_theme_page_callback',
'page arguments' => array(TRUE),
'access arguments' => array('access content'),
);
$items['menu-test/no-theme-callback'] = array(
'title' => 'Page that displays different themes without using a theme callback.',
'page callback' => 'menu_test_theme_page_callback',
'access arguments' => array('access content'),
);
// Path containing "exotic" characters.
$path = "menu-test/ -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
"%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
"éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
$items[$path] = array(
'title' => '"Exotic" path',
'page callback' => 'menu_test_callback',
'access arguments' => array('access content'),
);
// Hidden tests; base parents.
// Same structure as in Menu and Block modules. Since those structures can
// change, we need to simulate our own in here.
$items['menu-test'] = array(
'title' => 'Menu test root',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
);
$items['menu-test/hidden'] = array(
'title' => 'Hidden test root',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
);
// Hidden tests; one dynamic argument.
$items['menu-test/hidden/menu'] = array(
'title' => 'Menus',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
);
$items['menu-test/hidden/menu/list'] = array(
'title' => 'List menus',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['menu-test/hidden/menu/add'] = array(
'title' => 'Add menu',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
'type' => MENU_LOCAL_ACTION,
);
$items['menu-test/hidden/menu/settings'] = array(
'title' => 'Settings',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
'type' => MENU_LOCAL_TASK,
'weight' => 5,
);
$items['menu-test/hidden/menu/manage/%menu'] = array(
'title' => 'Customize menu',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
);
$items['menu-test/hidden/menu/manage/%menu/list'] = array(
'title' => 'List links',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
);
$items['menu-test/hidden/menu/manage/%menu/add'] = array(
'title' => 'Add link',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
'type' => MENU_LOCAL_ACTION,
);
$items['menu-test/hidden/menu/manage/%menu/edit'] = array(
'title' => 'Edit menu',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
);
$items['menu-test/hidden/menu/manage/%menu/delete'] = array(
'title' => 'Delete menu',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
);
// Hidden tests; two dynamic arguments.
$items['menu-test/hidden/block'] = array(
'title' => 'Blocks',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
);
$items['menu-test/hidden/block/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['menu-test/hidden/block/add'] = array(
'title' => 'Add block',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
'type' => MENU_LOCAL_ACTION,
);
$items['menu-test/hidden/block/manage/%/%'] = array(
'title' => 'Configure block',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
);
$items['menu-test/hidden/block/manage/%/%/configure'] = array(
'title' => 'Configure block',
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
);
$items['menu-test/hidden/block/manage/%/%/delete'] = array(
'title' => 'Delete block',
'page callback' => 'node_page_default',
'access arguments' => array('access content'),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_NONE,
);
// Breadcrumbs tests.
// @see MenuBreadcrumbTestCase
$base = array(
'page callback' => 'menu_test_callback',
'access callback' => TRUE,
);
// Local tasks: Second level below default local task.
$items['menu-test/breadcrumb/tasks'] = array(
'title' => 'Breadcrumbs test: Local tasks',
) + $base;
$items['menu-test/breadcrumb/tasks/first'] = array(
'title' => 'First',
'type' => MENU_DEFAULT_LOCAL_TASK,
) + $base;
$items['menu-test/breadcrumb/tasks/second'] = array(
'title' => 'Second',
'type' => MENU_LOCAL_TASK,
) + $base;
$items['menu-test/breadcrumb/tasks/first/first'] = array(
'title' => 'First first',
'type' => MENU_DEFAULT_LOCAL_TASK,
) + $base;
$items['menu-test/breadcrumb/tasks/first/second'] = array(
'title' => 'First second',
'type' => MENU_LOCAL_TASK,
) + $base;
$items['menu-test/breadcrumb/tasks/second/first'] = array(
'title' => 'Second first',
'type' => MENU_DEFAULT_LOCAL_TASK,
) + $base;
$items['menu-test/breadcrumb/tasks/second/second'] = array(
'title' => 'Second second',
'type' => MENU_LOCAL_TASK,
) + $base;
// Menu trail tests.
// @see MenuTrailTestCase
$items['menu-test/menu-trail'] = array(
'title' => 'Menu trail - Case 1',
'page callback' => 'menu_test_menu_trail_callback',
'access arguments' => array('access content'),
);
$items['admin/config/development/menu-trail'] = array(
'title' => 'Menu trail - Case 2',
'description' => 'Tests menu_tree_set_path()',
'page callback' => 'menu_test_menu_trail_callback',
'access arguments' => array('access administration pages'),
);
$items['menu-test/custom-403-page'] = array(
'title' => 'Custom 403 page',
'page callback' => 'menu_test_custom_403_404_callback',
'access arguments' => array('access content'),
);
$items['menu-test/custom-404-page'] = array(
'title' => 'Custom 404 page',
'page callback' => 'menu_test_custom_403_404_callback',
'access arguments' => array('access content'),
);
// File inheritance tests. This menu item should inherit the page callback
// system_admin_menu_block_page() and therefore render its children as links
// on the page.
$items['admin/config/development/file-inheritance'] = array(
'title' => 'File inheritance',
'description' => 'Test file inheritance',
'access arguments' => array('access content'),
);
$items['admin/config/development/file-inheritance/inherit'] = array(
'title' => 'Inherit',
'description' => 'File inheritance test description',
'page callback' => 'menu_test_callback',
'access arguments' => array('access content'),
);
$items['menu_login_callback'] = array(
'title' => 'Used as a login path',
'page callback' => 'menu_login_callback',
'access callback' => TRUE,
);
$items['menu-title-test/case1'] = array(
'title' => 'Example title - Case 1',
'access callback' => TRUE,
'page callback' => 'menu_test_callback',
);
$items['menu-title-test/case2'] = array(
'title' => 'Example @sub1 - Case @op2',
// If '2' is not in quotes, the argument becomes arg(2).
'title arguments' => array('@sub1' => 'title', '@op2' => '2'),
'access callback' => TRUE,
'page callback' => 'menu_test_callback',
);
$items['menu-title-test/case3'] = array(
'title' => 'Example title',
'title callback' => 'menu_test_title_callback',
'access callback' => TRUE,
'page callback' => 'menu_test_callback',
);
$items['menu-title-test/case4'] = array(
// Title gets completely ignored. Good thing, too.
'title' => 'Bike sheds full of blue smurfs',
'title callback' => 'menu_test_title_callback',
// If '4' is not in quotes, the argument becomes arg(4).
'title arguments' => array('Example title', '4'),
'access callback' => TRUE,
'page callback' => 'menu_test_callback',
);
// Load arguments inheritance test.
$items['menu-test/arguments/%menu_test_argument/%'] = array(
'title' => 'Load arguments inheritance test',
'load arguments' => array(3),
'page callback' => 'menu_test_callback',
'access callback' => TRUE,
);
$items['menu-test/arguments/%menu_test_argument/%/default'] = array(
'title' => 'Default local task',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['menu-test/arguments/%menu_test_argument/%/task'] = array(
'title' => 'Local task',
'page callback' => 'menu_test_callback',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
);
// For this path, load arguments should be inherited for the first loader only.
$items['menu-test/arguments/%menu_test_argument/%menu_test_other_argument/common-loader'] = array(
'title' => 'Local task',
'page callback' => 'menu_test_callback',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
);
// For these paths, no load arguments should be inherited.
// Not on the same position.
$items['menu-test/arguments/%/%menu_test_argument/different-loaders-1'] = array(
'title' => 'An item not sharing the same loader',
'page callback' => 'menu_test_callback',
'access callback' => TRUE,
);
// Not the same loader.
$items['menu-test/arguments/%menu_test_other_argument/%/different-loaders-2'] = array(
'title' => 'An item not sharing the same loader',
'page callback' => 'menu_test_callback',
'access callback' => TRUE,
);
// Not the same loader.
$items['menu-test/arguments/%/%/different-loaders-3'] = array(
'title' => 'An item not sharing the same loader',
'page callback' => 'menu_test_callback',
'access callback' => TRUE,
);
// Explict load arguments should not be overriden (even if empty).
$items['menu-test/arguments/%menu_test_argument/%/explicit-arguments'] = array(
'title' => 'An item defining explicit load arguments',
'load arguments' => array(),
'page callback' => 'menu_test_callback',
'access callback' => TRUE,
);
return $items;
}
/**
* Dummy argument loader for hook_menu() to point to.
*/
function menu_test_argument_load($arg1) {
return FALSE;
}
/**
* Dummy argument loader for hook_menu() to point to.
*/
function menu_test_other_argument_load($arg1) {
return FALSE;
}
/**
* Dummy callback for hook_menu() to point to.
*
* @return
* A random string.
*/
function menu_test_callback() {
return 'This is menu_test_callback().';
}
/**
* Callback that test menu_test_menu_tree_set_path().
*/
function menu_test_menu_trail_callback() {
$menu_path = variable_get('menu_test_menu_tree_set_path', array());
if (!empty($menu_path)) {
menu_tree_set_path($menu_path['menu_name'], $menu_path['path']);
}
return 'This is menu_test_menu_trail_callback().';
}
/**
* Implements hook_init().
*/
function menu_test_init() {
// When requested by one of the MenuTrailTestCase tests, record the initial
// active trail during Drupal's bootstrap (before the user is redirected to a
// custom 403 or 404 page). See menu_test_custom_403_404_callback().
if (variable_get('menu_test_record_active_trail', FALSE)) {
variable_set('menu_test_active_trail_initial', menu_get_active_trail());
}
}
/**
* Callback for our custom 403 and 404 pages.
*/
function menu_test_custom_403_404_callback() {
// When requested by one of the MenuTrailTestCase tests, record the final
// active trail now that the user has been redirected to the custom 403 or
// 404 page. See menu_test_init().
if (variable_get('menu_test_record_active_trail', FALSE)) {
variable_set('menu_test_active_trail_final', menu_get_active_trail());
}
return 'This is menu_test_custom_403_404_callback().';
}
/**
* Page callback to use when testing the theme callback functionality.
*
* @param $inherited
* An optional boolean to set to TRUE when the requested page is intended to
* inherit the theme of its parent.
* @return
* A string describing the requested custom theme and actual theme being used
* for the current page request.
*/
function menu_test_theme_page_callback($inherited = FALSE) {
global $theme_key;
// Initialize the theme system so that $theme_key will be populated.
drupal_theme_initialize();
// Now check both the requested custom theme and the actual theme being used.
$custom_theme = menu_get_custom_theme();
$requested_theme = empty($custom_theme) ? 'NONE' : $custom_theme;
$output = "Custom theme: $requested_theme. Actual theme: $theme_key.";
if ($inherited) {
$output .= ' Theme callback inheritance is being tested.';
}
return $output;
}
/**
* Theme callback to use when testing the theme callback functionality.
*
* @param $argument
* The argument passed in from the URL.
* @return
* The name of the custom theme to request for the current page.
*/
function menu_test_theme_callback($argument) {
// Test using the variable administrative theme.
if ($argument == 'use-admin-theme') {
return variable_get('admin_theme');
}
// Test using a theme that exists, but may or may not be enabled.
elseif ($argument == 'use-stark-theme') {
return 'stark';
}
// Test using a theme that does not exist.
elseif ($argument == 'use-fake-theme') {
return 'fake_theme';
}
// For any other value of the URL argument, do not return anything. This
// allows us to test that returning nothing from a theme callback function
// causes the page to correctly fall back on using the main site theme.
}
/**
* Implement hook_custom_theme().
*
* @return
* The name of the custom theme to use for the current page.
*/
function menu_test_custom_theme() {
// If an appropriate variable has been set in the database, request the theme
// that is stored there. Otherwise, do not attempt to dynamically set the
// theme.
if ($theme = variable_get('menu_test_hook_custom_theme_name', FALSE)) {
return $theme;
}
}
/**
* Helper function for the testMenuName() test. Used to change the menu_name
* parameter of a menu.
*
* @param $new_name
* If set, will change the menu_name value.
* @return
* The menu_name value to use.
*/
function menu_test_menu_name($new_name = '') {
static $name = 'original';
if ($new_name) {
$name = $new_name;
}
return $name;
}
/**
* Implements hook_menu_link_insert().
*
* @return
* A random string.
*/
function menu_test_menu_link_insert($item) {
menu_test_static_variable('insert');
}
/**
* Implements hook_menu_link_update().
*
* @return
* A random string.
*/
function menu_test_menu_link_update($item) {
menu_test_static_variable('update');
}
/**
* Implements hook_menu_link_delete().
*
* @return
* A random string.
*/
function menu_test_menu_link_delete($item) {
menu_test_static_variable('delete');
}
/**
* Static function for testing hook results.
*
* @param $value
* The value to set or NULL to return the current value.
* @return
* A text string for comparison to test assertions.
*/
function menu_test_static_variable($value = NULL) {
static $variable;
if (!empty($value)) {
$variable = $value;
}
return $variable;
}
/**
* Implements hook_menu_site_status_alter().
*/
function menu_test_menu_site_status_alter(&$menu_site_status, $path) {
// Allow access to ?q=menu_login_callback even if in maintenance mode.
if ($menu_site_status == MENU_SITE_OFFLINE && $path == 'menu_login_callback') {
$menu_site_status = MENU_SITE_ONLINE;
}
}
/**
* Menu callback to be used as a login path.
*/
function menu_login_callback() {
return 'This is menu_login_callback().';
}
/**
* Concatenates a string, by using the t() function and a case number.
*
* @param $title
* Title string.
* @param $case_number
* The current case number which is tests (defaults to 3).
*/
function menu_test_title_callback($title, $case_no = 3) {
return t($title) . ' - Case ' . $case_no;
}

View file

@ -0,0 +1,346 @@
<?php
/**
* @file
* Tests for the module API.
*/
/**
* Unit tests for the module API.
*/
class ModuleUnitTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Module API',
'description' => 'Test low-level module functions.',
'group' => 'Module',
);
}
/**
* The basic functionality of module_list().
*/
function testModuleList() {
// Build a list of modules, sorted alphabetically.
$profile_info = install_profile_info('standard', 'en');
$module_list = $profile_info['dependencies'];
// Installation profile is a module that is expected to be loaded.
$module_list[] = 'standard';
sort($module_list);
// Compare this list to the one returned by module_list(). We expect them
// to match, since all default profile modules have a weight equal to 0
// (except for block.module, which has a lower weight but comes first in
// the alphabet anyway).
$this->assertModuleList($module_list, t('Standard profile'));
// Try to install a new module.
module_enable(array('contact'));
$module_list[] = 'contact';
sort($module_list);
$this->assertModuleList($module_list, t('After adding a module'));
// Try to mess with the module weights.
db_update('system')
->fields(array('weight' => 20))
->condition('name', 'contact')
->condition('type', 'module')
->execute();
// Reset the module list.
module_list(TRUE);
// Move contact to the end of the array.
unset($module_list[array_search('contact', $module_list)]);
$module_list[] = 'contact';
$this->assertModuleList($module_list, t('After changing weights'));
// Test the fixed list feature.
$fixed_list = array(
'system' => array('filename' => drupal_get_path('module', 'system')),
'menu' => array('filename' => drupal_get_path('module', 'menu')),
);
module_list(FALSE, FALSE, FALSE, $fixed_list);
$new_module_list = array_combine(array_keys($fixed_list), array_keys($fixed_list));
$this->assertModuleList($new_module_list, t('When using a fixed list'));
// Reset the module list.
module_list(TRUE);
$this->assertModuleList($module_list, t('After reset'));
}
/**
* Assert that module_list() return the expected values.
*
* @param $expected_values
* The expected values, sorted by weight and module name.
*/
protected function assertModuleList(Array $expected_values, $condition) {
$expected_values = array_combine($expected_values, $expected_values);
$this->assertEqual($expected_values, module_list(), format_string('@condition: module_list() returns correct results', array('@condition' => $condition)));
ksort($expected_values);
$this->assertIdentical($expected_values, module_list(FALSE, FALSE, TRUE), format_string('@condition: module_list() returns correctly sorted results', array('@condition' => $condition)));
}
/**
* Test module_implements() caching.
*/
function testModuleImplements() {
// Clear the cache.
cache_clear_all('module_implements', 'cache_bootstrap');
$this->assertFalse(cache_get('module_implements', 'cache_bootstrap'), 'The module implements cache is empty.');
$this->drupalGet('');
$this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), 'The module implements cache is populated after requesting a page.');
// Test again with an authenticated user.
$this->user = $this->drupalCreateUser();
$this->drupalLogin($this->user);
cache_clear_all('module_implements', 'cache_bootstrap');
$this->drupalGet('');
$this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), 'The module implements cache is populated after requesting a page.');
// Make sure group include files are detected properly even when the file is
// already loaded when the cache is rebuilt.
// For that activate the module_test which provides the file to load.
module_enable(array('module_test'));
module_load_include('inc', 'module_test', 'module_test.file');
$modules = module_implements('test_hook');
$static = drupal_static('module_implements');
$this->assertTrue(in_array('module_test', $modules), 'Hook found.');
$this->assertEqual($static['test_hook']['module_test'], 'file', 'Include file detected.');
}
/**
* Test that module_invoke() can load a hook defined in hook_hook_info().
*/
function testModuleInvoke() {
module_enable(array('module_test'), FALSE);
$this->resetAll();
$this->drupalGet('module-test/hook-dynamic-loading-invoke');
$this->assertText('success!', 'module_invoke() dynamically loads a hook defined in hook_hook_info().');
}
/**
* Test that module_invoke_all() can load a hook defined in hook_hook_info().
*/
function testModuleInvokeAll() {
module_enable(array('module_test'), FALSE);
$this->resetAll();
$this->drupalGet('module-test/hook-dynamic-loading-invoke-all');
$this->assertText('success!', 'module_invoke_all() dynamically loads a hook defined in hook_hook_info().');
}
/**
* Test dependency resolution.
*/
function testDependencyResolution() {
// Enable the test module, and make sure that other modules we are testing
// are not already enabled. (If they were, the tests below would not work
// correctly.)
module_enable(array('module_test'), FALSE);
$this->assertTrue(module_exists('module_test'), 'Test module is enabled.');
$this->assertFalse(module_exists('forum'), 'Forum module is disabled.');
$this->assertFalse(module_exists('poll'), 'Poll module is disabled.');
$this->assertFalse(module_exists('php'), 'PHP module is disabled.');
// First, create a fake missing dependency. Forum depends on poll, which
// depends on a made-up module, foo. Nothing should be installed.
variable_set('dependency_test', 'missing dependency');
drupal_static_reset('system_rebuild_module_data');
$result = module_enable(array('forum'));
$this->assertFalse($result, 'module_enable() returns FALSE if dependencies are missing.');
$this->assertFalse(module_exists('forum'), 'module_enable() aborts if dependencies are missing.');
// Now, fix the missing dependency. Forum module depends on poll, but poll
// depends on the PHP module. module_enable() should work.
variable_set('dependency_test', 'dependency');
drupal_static_reset('system_rebuild_module_data');
$result = module_enable(array('forum'));
$this->assertTrue($result, 'module_enable() returns the correct value.');
// Verify that the fake dependency chain was installed.
$this->assertTrue(module_exists('poll') && module_exists('php'), 'Dependency chain was installed by module_enable().');
// Verify that the original module was installed.
$this->assertTrue(module_exists('forum'), 'Module installation with unlisted dependencies succeeded.');
// Finally, verify that the modules were enabled in the correct order.
$this->assertEqual(variable_get('test_module_enable_order', array()), array('php', 'poll', 'forum'), 'Modules were enabled in the correct order by module_enable().');
// Now, disable the PHP module. Both forum and poll should be disabled as
// well, in the correct order.
module_disable(array('php'));
$this->assertTrue(!module_exists('forum') && !module_exists('poll'), 'Depedency chain was disabled by module_disable().');
$this->assertFalse(module_exists('php'), 'Disabling a module with unlisted dependents succeeded.');
$this->assertEqual(variable_get('test_module_disable_order', array()), array('forum', 'poll', 'php'), 'Modules were disabled in the correct order by module_disable().');
// Disable a module that is listed as a dependency by the installation
// profile. Make sure that the profile itself is not on the list of
// dependent modules to be disabled.
$profile = drupal_get_profile();
$info = install_profile_info($profile);
$this->assertTrue(in_array('comment', $info['dependencies']), 'Comment module is listed as a dependency of the installation profile.');
$this->assertTrue(module_exists('comment'), 'Comment module is enabled.');
module_disable(array('comment'));
$this->assertFalse(module_exists('comment'), 'Comment module was disabled.');
$disabled_modules = variable_get('test_module_disable_order', array());
$this->assertTrue(in_array('comment', $disabled_modules), 'Comment module is in the list of disabled modules.');
$this->assertFalse(in_array($profile, $disabled_modules), 'The installation profile is not in the list of disabled modules.');
// Try to uninstall the PHP module by itself. This should be rejected,
// since the modules which it depends on need to be uninstalled first, and
// that is too destructive to perform automatically.
$result = drupal_uninstall_modules(array('php'));
$this->assertFalse($result, 'Calling drupal_uninstall_modules() on a module whose dependents are not uninstalled fails.');
foreach (array('forum', 'poll', 'php') as $module) {
$this->assertNotEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, format_string('The @module module was not uninstalled.', array('@module' => $module)));
}
// Now uninstall all three modules explicitly, but in the incorrect order,
// and make sure that drupal_uninstal_modules() uninstalled them in the
// correct sequence.
$result = drupal_uninstall_modules(array('poll', 'php', 'forum'));
$this->assertTrue($result, 'drupal_uninstall_modules() returns the correct value.');
foreach (array('forum', 'poll', 'php') as $module) {
$this->assertEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, format_string('The @module module was uninstalled.', array('@module' => $module)));
}
$this->assertEqual(variable_get('test_module_uninstall_order', array()), array('forum', 'poll', 'php'), 'Modules were uninstalled in the correct order by drupal_uninstall_modules().');
// Uninstall the profile module from above, and make sure that the profile
// itself is not on the list of dependent modules to be uninstalled.
$result = drupal_uninstall_modules(array('comment'));
$this->assertTrue($result, 'drupal_uninstall_modules() returns the correct value.');
$this->assertEqual(drupal_get_installed_schema_version('comment'), SCHEMA_UNINSTALLED, 'Comment module was uninstalled.');
$uninstalled_modules = variable_get('test_module_uninstall_order', array());
$this->assertTrue(in_array('comment', $uninstalled_modules), 'Comment module is in the list of uninstalled modules.');
$this->assertFalse(in_array($profile, $uninstalled_modules), 'The installation profile is not in the list of uninstalled modules.');
// Enable forum module again, which should enable both the poll module and
// php module. But, this time do it with poll module declaring a dependency
// on a specific version of php module in its info file. Make sure that
// module_enable() still works.
variable_set('dependency_test', 'version dependency');
drupal_static_reset('system_rebuild_module_data');
$result = module_enable(array('forum'));
$this->assertTrue($result, 'module_enable() returns the correct value.');
// Verify that the fake dependency chain was installed.
$this->assertTrue(module_exists('poll') && module_exists('php'), 'Dependency chain was installed by module_enable().');
// Verify that the original module was installed.
$this->assertTrue(module_exists('forum'), 'Module installation with version dependencies succeeded.');
// Finally, verify that the modules were enabled in the correct order.
$enable_order = variable_get('test_module_enable_order', array());
$php_position = array_search('php', $enable_order);
$poll_position = array_search('poll', $enable_order);
$forum_position = array_search('forum', $enable_order);
$php_before_poll = $php_position !== FALSE && $poll_position !== FALSE && $php_position < $poll_position;
$poll_before_forum = $poll_position !== FALSE && $forum_position !== FALSE && $poll_position < $forum_position;
$this->assertTrue($php_before_poll && $poll_before_forum, 'Modules were enabled in the correct order by module_enable().');
}
}
/**
* Unit tests for module installation.
*/
class ModuleInstallTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Module installation',
'description' => 'Tests the installation of modules.',
'group' => 'Module',
);
}
function setUp() {
parent::setUp('module_test');
}
/**
* Test that calls to drupal_write_record() work during module installation.
*
* This is a useful function to test because modules often use it to insert
* initial data in their database tables when they are being installed or
* enabled. Furthermore, drupal_write_record() relies on the module schema
* information being available, so this also checks that the data from one of
* the module's hook implementations, in particular hook_schema(), is
* properly available during this time. Therefore, this test helps ensure
* that modules are fully functional while Drupal is installing and enabling
* them.
*/
function testDrupalWriteRecord() {
// Check for data that was inserted using drupal_write_record() while the
// 'module_test' module was being installed and enabled.
$data = db_query("SELECT data FROM {module_test}")->fetchCol();
$this->assertTrue(in_array('Data inserted in hook_install()', $data), 'Data inserted using drupal_write_record() in hook_install() is correctly saved.');
$this->assertTrue(in_array('Data inserted in hook_enable()', $data), 'Data inserted using drupal_write_record() in hook_enable() is correctly saved.');
}
}
/**
* Unit tests for module uninstallation and related hooks.
*/
class ModuleUninstallTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Module uninstallation',
'description' => 'Tests the uninstallation of modules.',
'group' => 'Module',
);
}
function setUp() {
parent::setUp('module_test', 'user');
}
/**
* Tests the hook_modules_uninstalled() of the user module.
*/
function testUserPermsUninstalled() {
// Uninstalls the module_test module, so hook_modules_uninstalled()
// is executed.
module_disable(array('module_test'));
drupal_uninstall_modules(array('module_test'));
// Are the perms defined by module_test removed from {role_permission}.
$count = db_query("SELECT COUNT(rid) FROM {role_permission} WHERE permission = :perm", array(':perm' => 'module_test perm'))->fetchField();
$this->assertEqual(0, $count, 'Permissions were all removed.');
}
}
class ModuleImplementsAlterTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Module implements alter',
'description' => 'Tests hook_module_implements_alter().',
'group' => 'Module',
);
}
/**
* Tests hook_module_implements_alter() adding an implementation.
*/
function testModuleImplementsAlter() {
module_enable(array('module_test'), FALSE);
$this->assertTrue(module_exists('module_test'), 'Test module is enabled.');
// Assert that module_test.module is now included.
$this->assertTrue(function_exists('module_test_permission'),
'The file module_test.module was successfully included.');
$modules = module_implements('permission');
$this->assertTrue(in_array('module_test', $modules), 'module_test implements hook_permission.');
$modules = module_implements('module_implements_alter');
$this->assertTrue(in_array('module_test', $modules), 'module_test implements hook_module_implements_alter().');
// Assert that module_test.implementations.inc is not included yet.
$this->assertFalse(function_exists('module_test_altered_test_hook'),
'The file module_test.implementations.inc is not included yet.');
// Assert that module_test_module_implements_alter(*, 'altered_test_hook')
// has added an implementation
$this->assertTrue(in_array('module_test', module_implements('altered_test_hook')),
'module_test implements hook_altered_test_hook().');
// Assert that module_test.implementations.inc was included as part of the process.
$this->assertTrue(function_exists('module_test_altered_test_hook'),
'The file module_test.implementations.inc was included.');
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* @file
* A file to test module_implements() loading includes.
*/
/**
* Implements hook_test_hook().
*/
function module_test_test_hook() {
return array('module_test' => 'success!');
}

View file

@ -0,0 +1,10 @@
<?php
/**
* Implements hook_altered_test_hook()
*
* @see module_test_module_implements_alter()
*/
function module_test_altered_test_hook() {
return __FUNCTION__;
}

View file

@ -0,0 +1,12 @@
name = "Module test"
description = "Support module for module system testing."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,42 @@
<?php
/**
* @file
* Install, update and uninstall functions for the module_test module.
*/
/**
* Implements hook_schema().
*/
function module_test_schema() {
$schema['module_test'] = array(
'description' => 'Dummy table to test the behavior of hook_schema() during module installation.',
'fields' => array(
'data' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'An example data column for the module.',
),
),
);
return $schema;
}
/**
* Implements hook_install().
*/
function module_test_install() {
$record = array('data' => 'Data inserted in hook_install()');
drupal_write_record('module_test', $record);
}
/**
* Implements hook_enable().
*/
function module_test_enable() {
$record = array('data' => 'Data inserted in hook_enable()');
drupal_write_record('module_test', $record);
}

View file

@ -0,0 +1,142 @@
<?php
/**
* Implements hook_permission().
*/
function module_test_permission() {
return array(
'module_test perm' => t('example perm for module_test module'),
);
}
/**
* Implements hook_system_info_alter().
*
* Manipulate module dependencies to test dependency chains.
*/
function module_test_system_info_alter(&$info, $file, $type) {
if (variable_get('dependency_test', FALSE) == 'missing dependency') {
if ($file->name == 'forum') {
// Make forum module depend on poll.
$info['dependencies'][] = 'poll';
}
elseif ($file->name == 'poll') {
// Make poll depend on a made-up module.
$info['dependencies'][] = 'foo';
}
}
elseif (variable_get('dependency_test', FALSE) == 'dependency') {
if ($file->name == 'forum') {
// Make the forum module depend on poll.
$info['dependencies'][] = 'poll';
}
elseif ($file->name == 'poll') {
// Make poll depend on php module.
$info['dependencies'][] = 'php';
}
}
elseif (variable_get('dependency_test', FALSE) == 'version dependency') {
if ($file->name == 'forum') {
// Make the forum module depend on poll.
$info['dependencies'][] = 'poll';
}
elseif ($file->name == 'poll') {
// Make poll depend on a specific version of php module.
$info['dependencies'][] = 'php (1.x)';
}
elseif ($file->name == 'php') {
// Set php module to a version compatible with the above.
$info['version'] = '7.x-1.0';
}
}
if ($file->name == 'seven' && $type == 'theme') {
$info['regions']['test_region'] = t('Test region');
}
}
/**
* Implements hook_hook_info().
*/
function module_test_hook_info() {
$hooks['test_hook'] = array(
'group' => 'file',
);
return $hooks;
}
/**
* Implements hook_menu().
*/
function module_test_menu() {
$items['module-test/hook-dynamic-loading-invoke'] = array(
'title' => 'Test hook dynamic loading (invoke)',
'page callback' => 'module_test_hook_dynamic_loading_invoke',
'access arguments' => array('access content'),
);
$items['module-test/hook-dynamic-loading-invoke-all'] = array(
'title' => 'Test hook dynamic loading (invoke_all)',
'page callback' => 'module_test_hook_dynamic_loading_invoke_all',
'access arguments' => array('access content'),
);
return $items;
}
/**
* Page callback for 'hook dynamic loading' test.
*
* If the hook is dynamically loaded correctly, the menu callback should
* return 'success!'.
*/
function module_test_hook_dynamic_loading_invoke() {
$result = module_invoke('module_test', 'test_hook');
return $result['module_test'];
}
/**
* Page callback for 'hook dynamic loading' test.
*
* If the hook is dynamically loaded correctly, the menu callback should
* return 'success!'.
*/
function module_test_hook_dynamic_loading_invoke_all() {
$result = module_invoke_all('test_hook');
return $result['module_test'];
}
/**
* Implements hook_modules_enabled().
*/
function module_test_modules_enabled($modules) {
// Record the ordered list of modules that were passed in to this hook so we
// can check that the modules were enabled in the correct sequence.
variable_set('test_module_enable_order', $modules);
}
/**
* Implements hook_modules_disabled().
*/
function module_test_modules_disabled($modules) {
// Record the ordered list of modules that were passed in to this hook so we
// can check that the modules were disabled in the correct sequence.
variable_set('test_module_disable_order', $modules);
}
/**
* Implements hook_modules_uninstalled().
*/
function module_test_modules_uninstalled($modules) {
// Record the ordered list of modules that were passed in to this hook so we
// can check that the modules were uninstalled in the correct sequence.
variable_set('test_module_uninstall_order', $modules);
}
/**
* Implements hook_module_implements_alter()
*/
function module_test_module_implements_alter(&$implementations, $hook) {
if ($hook === 'altered_test_hook') {
// Add a hook implementation, that will be found in
// module_test.implementations.inc.
$implementations['module_test'] = 'implementations';
}
}

View file

@ -0,0 +1,159 @@
<?php
/**
* @file
* Tests for pager functionality.
*/
/**
* Tests pager functionality.
*/
class PagerFunctionalWebTestCase extends DrupalWebTestCase {
protected $profile = 'testing';
public static function getInfo() {
return array(
'name' => 'Pager functionality',
'description' => 'Tests pager functionality.',
'group' => 'Pager',
);
}
function setUp() {
parent::setUp(array('dblog'));
// Insert 300 log messages.
for ($i = 0; $i < 300; $i++) {
watchdog('pager_test', $this->randomString(), NULL, WATCHDOG_DEBUG);
}
$this->admin_user = $this->drupalCreateUser(array(
'access site reports',
));
$this->drupalLogin($this->admin_user);
}
/**
* Tests markup and CSS classes of pager links.
*/
function testActiveClass() {
// Verify first page.
$this->drupalGet('admin/reports/dblog');
$current_page = 0;
$this->assertPagerItems($current_page);
// Verify any page but first/last.
$current_page++;
$this->drupalGet('admin/reports/dblog', array('query' => array('page' => $current_page)));
$this->assertPagerItems($current_page);
// Verify last page.
$elements = $this->xpath('//li[contains(@class, :class)]/a', array(':class' => 'pager-last'));
preg_match('@page=(\d+)@', $elements[0]['href'], $matches);
$current_page = (int) $matches[1];
$this->drupalGet($GLOBALS['base_root'] . $elements[0]['href'], array('external' => TRUE));
$this->assertPagerItems($current_page);
}
/**
* Asserts pager items and links.
*
* @param int $current_page
* The current pager page the internal browser is on.
*/
protected function assertPagerItems($current_page) {
$elements = $this->xpath('//ul[@class=:class]/li', array(':class' => 'pager'));
$this->assertTrue(!empty($elements), 'Pager found.');
// Make current page 1-based.
$current_page++;
// Extract first/previous and next/last items.
// first/previous only exist, if the current page is not the first.
if ($current_page > 1) {
$first = array_shift($elements);
$previous = array_shift($elements);
}
// next/last always exist, unless the current page is the last.
if ($current_page != count($elements)) {
$last = array_pop($elements);
$next = array_pop($elements);
}
// Verify items and links to pages.
foreach ($elements as $page => $element) {
// Make item/page index 1-based.
$page++;
if ($current_page == $page) {
$this->assertClass($element, 'pager-current', 'Item for current page has .pager-current class.');
$this->assertFalse(isset($element->a), 'Item for current page has no link.');
}
else {
$this->assertNoClass($element, 'pager-current', "Item for page $page has no .pager-current class.");
$this->assertClass($element, 'pager-item', "Item for page $page has .pager-item class.");
$this->assertTrue($element->a, "Link to page $page found.");
$this->assertNoClass($element->a, 'active', "Link to page $page is not active.");
}
unset($elements[--$page]);
}
// Verify that no other items remain untested.
$this->assertTrue(empty($elements), 'All expected items found.');
// Verify first/previous and next/last items and links.
if (isset($first)) {
$this->assertClass($first, 'pager-first', 'Item for first page has .pager-first class.');
$this->assertTrue($first->a, 'Link to first page found.');
$this->assertNoClass($first->a, 'active', 'Link to first page is not active.');
}
if (isset($previous)) {
$this->assertClass($previous, 'pager-previous', 'Item for first page has .pager-previous class.');
$this->assertTrue($previous->a, 'Link to previous page found.');
$this->assertNoClass($previous->a, 'active', 'Link to previous page is not active.');
}
if (isset($next)) {
$this->assertClass($next, 'pager-next', 'Item for next page has .pager-next class.');
$this->assertTrue($next->a, 'Link to next page found.');
$this->assertNoClass($next->a, 'active', 'Link to next page is not active.');
}
if (isset($last)) {
$this->assertClass($last, 'pager-last', 'Item for last page has .pager-last class.');
$this->assertTrue($last->a, 'Link to last page found.');
$this->assertNoClass($last->a, 'active', 'Link to last page is not active.');
}
}
/**
* Asserts that an element has a given class.
*
* @param SimpleXMLElement $element
* The element to test.
* @param string $class
* The class to assert.
* @param string $message
* (optional) A verbose message to output.
*/
protected function assertClass(SimpleXMLElement $element, $class, $message = NULL) {
if (!isset($message)) {
$message = "Class .$class found.";
}
$this->assertTrue(strpos($element['class'], $class) !== FALSE, $message);
}
/**
* Asserts that an element does not have a given class.
*
* @param SimpleXMLElement $element
* The element to test.
* @param string $class
* The class to assert.
* @param string $message
* (optional) A verbose message to output.
*/
protected function assertNoClass(SimpleXMLElement $element, $class, $message = NULL) {
if (!isset($message)) {
$message = "Class .$class not found.";
}
$this->assertTrue(strpos($element['class'], $class) === FALSE, $message);
}
}

View file

@ -0,0 +1,81 @@
<?php
/**
* @file
* Provides unit tests for password.inc.
*/
/**
* Unit tests for password hashing API.
*/
class PasswordHashingTest extends DrupalWebTestCase {
protected $profile = 'testing';
public static function getInfo() {
return array(
'name' => 'Password hashing',
'description' => 'Password hashing unit tests.',
'group' => 'System',
);
}
function setUp() {
require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
parent::setUp();
}
/**
* Test password hashing.
*/
function testPasswordHashing() {
// Set a log2 iteration count that is deliberately out of bounds to test
// that it is corrected to be within bounds.
variable_set('password_count_log2', 1);
// Set up a fake $account with a password 'baz', hashed with md5.
$password = 'baz';
$account = (object) array('name' => 'foo', 'pass' => md5($password));
// The md5 password should be flagged as needing an update.
$this->assertTrue(user_needs_new_hash($account), 'User with md5 password needs a new hash.');
// Re-hash the password.
$old_hash = $account->pass;
$account->pass = user_hash_password($password);
$this->assertIdentical(_password_get_count_log2($account->pass), DRUPAL_MIN_HASH_COUNT, 'Re-hashed password has the minimum number of log2 iterations.');
$this->assertTrue($account->pass != $old_hash, 'Password hash changed.');
$this->assertTrue(user_check_password($password, $account), 'Password check succeeds.');
// Since the log2 setting hasn't changed and the user has a valid password,
// user_needs_new_hash() should return FALSE.
$this->assertFalse(user_needs_new_hash($account), 'User does not need a new hash.');
// Increment the log2 iteration to MIN + 1.
variable_set('password_count_log2', DRUPAL_MIN_HASH_COUNT + 1);
$this->assertTrue(user_needs_new_hash($account), 'User needs a new hash after incrementing the log2 count.');
// Re-hash the password.
$old_hash = $account->pass;
$account->pass = user_hash_password($password);
$this->assertIdentical(_password_get_count_log2($account->pass), DRUPAL_MIN_HASH_COUNT + 1, 'Re-hashed password has the correct number of log2 iterations.');
$this->assertTrue($account->pass != $old_hash, 'Password hash changed again.');
// Now the hash should be OK.
$this->assertFalse(user_needs_new_hash($account), 'Re-hashed password does not need a new hash.');
$this->assertTrue(user_check_password($password, $account), 'Password check succeeds with re-hashed password.');
}
/**
* Verifies that passwords longer than 512 bytes are not hashed.
*/
public function testLongPassword() {
$password = str_repeat('x', 512);
$result = user_hash_password($password);
$this->assertFalse(empty($result), '512 byte long password is allowed.');
$password = str_repeat('x', 513);
$result = user_hash_password($password);
$this->assertFalse($result, '513 byte long password is not allowed.');
// Check a string of 3-byte UTF-8 characters.
$password = str_repeat('€', 170);
$result = user_hash_password($password);
$this->assertFalse(empty($result), '510 byte long password is allowed.');
$password .= 'xx';
$this->assertFalse(empty($result), '512 byte long password is allowed.');
$password = str_repeat('€', 171);
$result = user_hash_password($password);
$this->assertFalse($result, '513 byte long password is not allowed.');
}
}

View file

@ -0,0 +1,381 @@
<?php
/**
* @file
* Tests for path.inc.
*/
/**
* Unit tests for the drupal_match_path() function in path.inc.
*
* @see drupal_match_path().
*/
class DrupalMatchPathTestCase extends DrupalWebTestCase {
protected $front;
public static function getInfo() {
return array(
'name' => 'Drupal match path',
'description' => 'Tests the drupal_match_path() function to make sure it works properly.',
'group' => 'Path API',
);
}
function setUp() {
// Set up the database and testing environment.
parent::setUp();
// Set up a random site front page to test the '<front>' placeholder.
$this->front = $this->randomName();
variable_set('site_frontpage', $this->front);
// Refresh our static variables from the database.
$this->refreshVariables();
}
/**
* Run through our test cases, making sure each one works as expected.
*/
function testDrupalMatchPath() {
// Set up our test cases.
$tests = $this->drupalMatchPathTests();
foreach ($tests as $patterns => $cases) {
foreach ($cases as $path => $expected_result) {
$actual_result = drupal_match_path($path, $patterns);
$this->assertIdentical($actual_result, $expected_result, format_string('Tried matching the path <code>@path</code> to the pattern <pre>@patterns</pre> - expected @expected, got @actual.', array('@path' => $path, '@patterns' => $patterns, '@expected' => var_export($expected_result, TRUE), '@actual' => var_export($actual_result, TRUE))));
}
}
}
/**
* Helper function for testDrupalMatchPath(): set up an array of test cases.
*
* @return
* An array of test cases to cycle through.
*/
private function drupalMatchPathTests() {
return array(
// Single absolute paths.
'blog/1' => array(
'blog/1' => TRUE,
'blog/2' => FALSE,
'test' => FALSE,
),
// Single paths with wildcards.
'blog/*' => array(
'blog/1' => TRUE,
'blog/2' => TRUE,
'blog/3/edit' => TRUE,
'blog/' => TRUE,
'blog' => FALSE,
'test' => FALSE,
),
// Single paths with multiple wildcards.
'node/*/revisions/*' => array(
'node/1/revisions/3' => TRUE,
'node/345/revisions/test' => TRUE,
'node/23/edit' => FALSE,
'test' => FALSE,
),
// Single paths with '<front>'.
'<front>' => array(
$this->front => TRUE,
"$this->front/" => FALSE,
"$this->front/edit" => FALSE,
'node' => FALSE,
'' => FALSE,
),
// Paths with both '<front>' and wildcards (should not work).
'<front>/*' => array(
$this->front => FALSE,
"$this->front/" => FALSE,
"$this->front/edit" => FALSE,
'node/12' => FALSE,
'' => FALSE,
),
// Multiple paths with the \n delimiter.
"node/*\nnode/*/edit" => array(
'node/1' => TRUE,
'node/view' => TRUE,
'node/32/edit' => TRUE,
'node/delete/edit' => TRUE,
'node/50/delete' => TRUE,
'test/example' => FALSE,
),
// Multiple paths with the \r delimiter.
"user/*\rblog/*" => array(
'user/1' => TRUE,
'blog/1' => TRUE,
'user/1/blog/1' => TRUE,
'user/blog' => TRUE,
'test/example' => FALSE,
'user' => FALSE,
'blog' => FALSE,
),
// Multiple paths with the \r\n delimiter.
"test\r\n<front>" => array(
'test' => TRUE,
$this->front => TRUE,
'example' => FALSE,
),
// Test existing regular expressions (should be escaped).
'[^/]+?/[0-9]' => array(
'test/1' => FALSE,
'[^/]+?/[0-9]' => TRUE,
),
);
}
}
/**
* Tests hook_url_alter functions.
*/
class UrlAlterFunctionalTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => t('URL altering'),
'description' => t('Tests hook_url_inbound_alter() and hook_url_outbound_alter().'),
'group' => t('Path API'),
);
}
function setUp() {
parent::setUp('path', 'forum', 'url_alter_test');
}
/**
* Test that URL altering works and that it occurs in the correct order.
*/
function testUrlAlter() {
$account = $this->drupalCreateUser(array('administer url aliases'));
$this->drupalLogin($account);
$uid = $account->uid;
$name = $account->name;
// Test a single altered path.
$this->assertUrlInboundAlter("user/$name", "user/$uid");
$this->assertUrlOutboundAlter("user/$uid", "user/$name");
// Test that a path always uses its alias.
$path = array('source' => "user/$uid/test1", 'alias' => 'alias/test1');
path_save($path);
$this->assertUrlInboundAlter('alias/test1', "user/$uid/test1");
$this->assertUrlOutboundAlter("user/$uid/test1", 'alias/test1');
// Test that alias source paths are normalized in the interface.
$edit = array('source' => "user/$name/edit", 'alias' => 'alias/test2');
$this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
$this->assertText(t('The alias has been saved.'));
// Test that a path always uses its alias.
$this->assertUrlInboundAlter('alias/test2', "user/$uid/edit");
$this->assertUrlOutboundAlter("user/$uid/edit", 'alias/test2');
// Test a non-existent user is not altered.
$uid++;
$this->assertUrlInboundAlter("user/$uid", "user/$uid");
$this->assertUrlOutboundAlter("user/$uid", "user/$uid");
// Test that 'forum' is altered to 'community' correctly, both at the root
// level and for a specific existing forum.
$this->assertUrlInboundAlter('community', 'forum');
$this->assertUrlOutboundAlter('forum', 'community');
$forum_vid = db_query("SELECT vid FROM {taxonomy_vocabulary} WHERE module = 'forum'")->fetchField();
$tid = db_insert('taxonomy_term_data')
->fields(array(
'name' => $this->randomName(),
'vid' => $forum_vid,
))
->execute();
$this->assertUrlInboundAlter("community/$tid", "forum/$tid");
$this->assertUrlOutboundAlter("forum/$tid", "community/$tid");
}
/**
* Test current_path() and request_path().
*/
function testCurrentUrlRequestedPath() {
$this->drupalGet('url-alter-test/bar');
$this->assertRaw('request_path=url-alter-test/bar', 'request_path() returns the requested path.');
$this->assertRaw('current_path=url-alter-test/foo', 'current_path() returns the internal path.');
}
/**
* 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
* A string with the original path that is run through url().
* @param $final
* A string with the expected result after url().
* @return
* TRUE if $original was correctly altered to $final, FALSE otherwise.
*/
protected function assertUrlOutboundAlter($original, $final) {
// Test outbound altering.
$result = url($original);
$base_path = base_path() . (variable_get('clean_url', '0') ? '' : '?q=');
$result = substr($result, strlen($base_path));
$this->assertIdentical($result, $final, format_string('Altered outbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result)));
}
/**
* Assert that a inbound path is altered to an expected value.
*
* @param $original
* A string with the aliased or un-normal path that is run through
* drupal_get_normal_path().
* @param $final
* A string with the expected result after url().
* @return
* TRUE if $original was correctly altered to $final, FALSE otherwise.
*/
protected function assertUrlInboundAlter($original, $final) {
// Test inbound altering.
$result = drupal_get_normal_path($original);
$this->assertIdentical($result, $final, format_string('Altered inbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result)));
}
}
/**
* Unit test for drupal_lookup_path().
*/
class PathLookupTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => t('Path lookup'),
'description' => t('Tests that drupal_lookup_path() returns correct paths.'),
'group' => t('Path API'),
);
}
/**
* Test that drupal_lookup_path() returns the correct path.
*/
function testDrupalLookupPath() {
$account = $this->drupalCreateUser();
$uid = $account->uid;
$name = $account->name;
// Test the situation where the source is the same for multiple aliases.
// Start with a language-neutral alias, which we will override.
$path = array(
'source' => "user/$uid",
'alias' => 'foo',
);
path_save($path);
$this->assertEqual(drupal_lookup_path('alias', $path['source']), $path['alias'], 'Basic alias lookup works.');
$this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], 'Basic source lookup works.');
// Create a language specific alias for the default language (English).
$path = array(
'source' => "user/$uid",
'alias' => "users/$name",
'language' => 'en',
);
path_save($path);
$this->assertEqual(drupal_lookup_path('alias', $path['source']), $path['alias'], 'English alias overrides language-neutral alias.');
$this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], 'English source overrides language-neutral source.');
// Create a language-neutral alias for the same path, again.
$path = array(
'source' => "user/$uid",
'alias' => 'bar',
);
path_save($path);
$this->assertEqual(drupal_lookup_path('alias', $path['source']), "users/$name", 'English alias still returned after entering a language-neutral alias.');
// Create a language-specific (xx-lolspeak) alias for the same path.
$path = array(
'source' => "user/$uid",
'alias' => 'LOL',
'language' => 'xx-lolspeak',
);
path_save($path);
$this->assertEqual(drupal_lookup_path('alias', $path['source']), "users/$name", 'English alias still returned after entering a LOLspeak alias.');
// The LOLspeak alias should be returned if we really want LOLspeak.
$this->assertEqual(drupal_lookup_path('alias', $path['source'], 'xx-lolspeak'), 'LOL', 'LOLspeak alias returned if we specify xx-lolspeak to drupal_lookup_path().');
// Create a new alias for this path in English, which should override the
// previous alias for "user/$uid".
$path = array(
'source' => "user/$uid",
'alias' => 'users/my-new-path',
'language' => 'en',
);
path_save($path);
$this->assertEqual(drupal_lookup_path('alias', $path['source']), $path['alias'], 'Recently created English alias returned.');
$this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], 'Recently created English source returned.');
// Remove the English aliases, which should cause a fallback to the most
// recently created language-neutral alias, 'bar'.
db_delete('url_alias')
->condition('language', 'en')
->execute();
drupal_clear_path_cache();
$this->assertEqual(drupal_lookup_path('alias', $path['source']), 'bar', 'Path lookup falls back to recently created language-neutral alias.');
// Test the situation where the alias and language are the same, but
// the source differs. The newer alias record should be returned.
$account2 = $this->drupalCreateUser();
$path = array(
'source' => 'user/' . $account2->uid,
'alias' => 'bar',
);
path_save($path);
$this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], 'Newer alias record is returned when comparing two LANGUAGE_NONE paths with the same alias.');
}
}
/**
* Tests the path_save() function.
*/
class PathSaveTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => t('Path save'),
'description' => t('Tests that path_save() exposes the previous alias value.'),
'group' => t('Path API'),
);
}
function setUp() {
// Enable a helper module that implements hook_path_update().
parent::setUp('path_test');
path_test_reset();
}
/**
* Tests that path_save() makes the original path available to modules.
*/
function testDrupalSaveOriginalPath() {
$account = $this->drupalCreateUser();
$uid = $account->uid;
$name = $account->name;
// Create a language-neutral alias.
$path = array(
'source' => "user/$uid",
'alias' => 'foo',
);
$path_original = $path;
path_save($path);
// Alter the path.
$path['alias'] = 'bar';
path_save($path);
// Test to see if the original alias is available to modules during
// hook_path_update().
$results = variable_get('path_test_results', array());
$this->assertIdentical($results['hook_path_update']['original']['alias'], $path_original['alias'], 'Old path alias available to modules during hook_path_update.');
$this->assertIdentical($results['hook_path_update']['original']['source'], $path_original['source'], 'Old path alias available to modules during hook_path_update.');
}
}

View file

@ -0,0 +1,12 @@
name = "Hook path tests"
description = "Support module for path hook testing."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,23 @@
<?php
/**
* @file
* Helper module for the path tests.
*/
/**
* Resets the path test results.
*/
function path_test_reset() {
variable_set('path_test_results', array());
}
/**
* Implements hook_path_update().
*/
function path_test_path_update($path) {
$results = variable_get('path_test_results', array());
$results['hook_path_update'] = $path;
variable_set('path_test_results', $results);
}

View file

@ -0,0 +1,18 @@
<?php
namespace Drupal\psr_0_test\Tests;
class ExampleTest extends \DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'PSR0 example test: PSR-0 in disabled modules.',
'description' => 'We want to assert that this test case is being discovered.',
'group' => 'SimpleTest',
);
}
function testArithmetics() {
$this->assert(1 + 1 == 2, '1 + 1 == 2');
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Drupal\psr_0_test\Tests\Nested;
class NestedExampleTest extends \DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'PSR0 example test: PSR-0 in nested subfolders.',
'description' => 'We want to assert that this PSR-0 test case is being discovered.',
'group' => 'SimpleTest',
);
}
function testArithmetics() {
$this->assert(1 + 1 == 2, '1 + 1 == 2');
}
}

View file

@ -0,0 +1,12 @@
name = PSR-0 Test cases
description = Test classes to be discovered by simpletest.
core = 7.x
hidden = TRUE
package = Testing
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1 @@
<?php

View file

@ -0,0 +1,12 @@
name = PSR-4 Test cases
description = Test classes to be discovered by simpletest.
core = 7.x
hidden = TRUE
package = Testing
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1 @@
<?php

View file

@ -0,0 +1,18 @@
<?php
namespace Drupal\psr_4_test\Tests;
class ExampleTest extends \DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'PSR4 example test: PSR-4 in disabled modules.',
'description' => 'We want to assert that this test case is being discovered.',
'group' => 'SimpleTest',
);
}
function testArithmetics() {
$this->assert(1 + 1 == 2, '1 + 1 == 2');
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Drupal\psr_4_test\Tests\Nested;
class NestedExampleTest extends \DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'PSR4 example test: PSR-4 in nested subfolders.',
'description' => 'We want to assert that this PSR-4 test case is being discovered.',
'group' => 'SimpleTest',
);
}
function testArithmetics() {
$this->assert(1 + 1 == 2, '1 + 1 == 2');
}
}

View file

@ -0,0 +1,142 @@
<?php
class RegistryParseFileTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Registry parse file test',
'description' => 'Parse a simple file and check that its resources are saved to the database.',
'group' => 'System'
);
}
function setUp() {
$chrs = hash('sha256', microtime() . mt_rand());
$this->fileName = 'registry_test_' . substr($chrs, 0, 16);
$this->className = 'registry_test_class' . substr($chrs, 16, 16);
$this->interfaceName = 'registry_test_interface' . substr($chrs, 32, 16);
parent::setUp();
}
/**
* testRegistryParseFile
*/
function testRegistryParseFile() {
_registry_parse_file($this->fileName, $this->getFileContents());
foreach (array('className', 'interfaceName') as $resource) {
$foundName = db_query('SELECT name FROM {registry} WHERE name = :name', array(':name' => $this->$resource))->fetchField();
$this->assertTrue($this->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$resource)));
}
}
/**
* getFileContents
*/
function getFileContents() {
$file_contents = <<<CONTENTS
<?php
class {$this->className} {}
interface {$this->interfaceName} {}
CONTENTS;
return $file_contents;
}
}
class RegistryParseFilesTestCase extends DrupalWebTestCase {
protected $fileTypes = array('new', 'existing_changed');
public static function getInfo() {
return array(
'name' => 'Registry parse files test',
'description' => 'Read two a simple files from disc, and check that their resources are saved to the database.',
'group' => 'System'
);
}
function setUp() {
parent::setUp();
// Create files with some php to parse - one 'new', one 'existing' so
// we test all the important code paths in _registry_parse_files.
foreach ($this->fileTypes as $fileType) {
$chrs = hash('sha256', microtime() . mt_rand());
$this->$fileType = new stdClass();
$this->$fileType->fileName = 'public://registry_test_' . substr($chrs, 0, 16);
$this->$fileType->className = 'registry_test_class' . substr($chrs, 16, 16);
$this->$fileType->interfaceName = 'registry_test_interface' . substr($chrs, 32, 16);
$this->$fileType->contents = $this->getFileContents($fileType);
file_save_data($this->$fileType->contents, $this->$fileType->fileName);
if ($fileType == 'existing_changed') {
// Add a record with an incorrect hash.
$this->$fileType->fakeHash = hash('sha256', mt_rand());
db_insert('registry_file')
->fields(array(
'hash' => $this->$fileType->fakeHash,
'filename' => $this->$fileType->fileName,
))
->execute();
// Insert some fake resource records.
foreach (array('class', 'interface') as $type) {
db_insert('registry')
->fields(array(
'name' => $type . hash('sha256', microtime() . mt_rand()),
'type' => $type,
'filename' => $this->$fileType->fileName,
))
->execute();
}
}
}
}
/**
* testRegistryParseFiles
*/
function testRegistryParseFiles() {
_registry_parse_files($this->getFiles());
foreach ($this->fileTypes as $fileType) {
// Test that we have all the right resources.
foreach (array('className', 'interfaceName') as $resource) {
$foundName = db_query('SELECT name FROM {registry} WHERE name = :name', array(':name' => $this->$fileType->$resource))->fetchField();
$this->assertTrue($this->$fileType->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$fileType->$resource)));
}
// Test that we have the right hash.
$hash = db_query('SELECT hash FROM {registry_file} WHERE filename = :filename', array(':filename' => $this->$fileType->fileName))->fetchField();
$this->assertTrue(hash('sha256', $this->$fileType->contents) == $hash, t('sha-256 for "@filename" matched.' . $fileType . $hash, array('@filename' => $this->$fileType->fileName)));
}
}
/**
* getFiles
*/
function getFiles() {
$files = array();
foreach ($this->fileTypes as $fileType) {
$files[$this->$fileType->fileName] = array('module' => '', 'weight' => 0);
if ($fileType == 'existing_changed') {
$files[$this->$fileType->fileName]['hash'] = $this->$fileType->fakeHash;
}
}
return $files;
}
/**
* getFileContents
*/
function getFileContents($fileType) {
$file_contents = <<<CONTENTS
<?php
class {$this->$fileType->className} {}
interface {$this->$fileType->interfaceName} {}
CONTENTS;
return $file_contents;
}
}

View file

@ -0,0 +1,12 @@
name = Requirements 1 Test
description = "Tests that a module is not installed when it fails hook_requirements('install')."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,21 @@
<?php
/**
* Implements hook_requirements().
*/
function requirements1_test_requirements($phase) {
$requirements = array();
// Ensure translations don't break during installation.
$t = get_t();
// Always fails requirements.
if ('install' == $phase) {
$requirements['requirements1_test'] = array(
'title' => $t('Requirements 1 Test'),
'severity' => REQUIREMENT_ERROR,
'description' => $t('Requirements 1 Test failed requirements.'),
);
}
return $requirements;
}

View file

@ -0,0 +1,7 @@
<?php
/**
* @file
* Tests that a module is not installed when it fails
* hook_requirements('install').
*/

View file

@ -0,0 +1,14 @@
name = Requirements 2 Test
description = "Tests that a module is not installed when the one it depends on fails hook_requirements('install)."
dependencies[] = requirements1_test
dependencies[] = comment
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

Some files were not shown because too many files have changed in this diff Show more