Newer
Older
<?php
// $Id$
/**
Dries Buytaert
committed
* Base class for Drupal tests.
*
* Do not extend this class, use one of the subclasses in this file.
*/
Dries Buytaert
committed
abstract class DrupalTestCase {
/**
* The test run ID.
*
* @var string
*/
protected $testId;
/**
* The original database prefix, before it was changed for testing purposes.
*
* @var string
*/
protected $originalPrefix = NULL;
/**
* The original file directory, before it was changed for testing purposes.
*
* @var string
*/
protected $originalFileDirectory = NULL;
Angie Byron
committed
/**
Dries Buytaert
committed
* Time limit for the test.
Angie Byron
committed
*/
Dries Buytaert
committed
protected $timeLimit = 180;
Angie Byron
committed
/**
* Current results of this test case.
*
* @var Array
*/
public $results = array(
'#pass' => 0,
'#fail' => 0,
'#exception' => 0,
Angie Byron
committed
'#debug' => 0,
);
/**
* Assertions thrown in that test case.
*
* @var Array
*/
protected $assertions = array();
Dries Buytaert
committed
Dries Buytaert
committed
* This class is skipped when looking for the source of an assertion.
*
* When displaying which function an assert comes from, it's not too useful
* to see "drupalWebTestCase->drupalLogin()', we would like to see the test
* that called it. So we need to skip the classes defining these helper
* methods.
Angie Byron
committed
*/
Dries Buytaert
committed
protected $skipClasses = array(__CLASS__ => TRUE);
Dries Buytaert
committed
/**
* Constructor for DrupalWebTestCase.
*
* @param $test_id
Dries Buytaert
committed
* Tests with the same id are reported together.
*/
public function __construct($test_id = NULL) {
$this->testId = $test_id;
Dries Buytaert
committed
}
/**
* Internal helper: stores the assert.
Dries Buytaert
committed
*
* @param $status
* Can be 'pass', 'fail', 'exception'.
* TRUE is a synonym for 'pass', FALSE for 'fail'.
Dries Buytaert
committed
* @param $message
* The message string.
* @param $group
Dries Buytaert
committed
* Which group this assert belongs to.
* @param $caller
Dries Buytaert
committed
* By default, the assert comes from a function whose name starts with
Dries Buytaert
committed
* 'test'. Instead, you can specify where this assert originates from
* by passing in an associative array as $caller. Key 'file' is
Dries Buytaert
committed
* the name of the source file, 'line' is the line number and 'function'
* is the caller function itself.
*/
Dries Buytaert
committed
protected function assert($status, $message = '', $group = 'Other', array $caller = NULL) {
Dries Buytaert
committed
global $db_prefix;
// Convert boolean status to string status.
Dries Buytaert
committed
if (is_bool($status)) {
$status = $status ? 'pass' : 'fail';
}
// Increment summary result counter.
$this->results['#' . $status]++;
// Get the function information about the call to the assertion method.
if (!$caller) {
$caller = $this->getAssertionCall();
Dries Buytaert
committed
}
// Switch to non-testing database to store results in.
Dries Buytaert
committed
$current_db_prefix = $db_prefix;
$db_prefix = $this->originalPrefix;
// Creation assertion array that can be displayed while tests are running.
$this->assertions[] = $assertion = array(
'test_id' => $this->testId,
'test_class' => get_class($this),
'status' => $status,
'message' => $message,
'message_group' => $group,
'function' => $caller['function'],
'line' => $caller['line'],
'file' => $caller['file'],
);
// Store assertion for display after the test has completed.
Dries Buytaert
committed
db_insert('simpletest')
->fields($assertion)
->execute();
// Return to testing prefix.
Dries Buytaert
committed
$db_prefix = $current_db_prefix;
Angie Byron
committed
// We do not use a ternary operator here to allow a breakpoint on
// test failure.
if ($status == 'pass') {
return TRUE;
}
else {
return FALSE;
}
Dries Buytaert
committed
}
Dries Buytaert
committed
/**
Dries Buytaert
committed
* Store an assertion from outside the testing context.
*
* This is useful for inserting assertions that can only be recorded after
* the test case has been destroyed, such as PHP fatal errors. The caller
* information is not automatically gathered since the caller is most likely
* inserting the assertion on behalf of other code. In all other respects
* the method behaves just like DrupalTestCase::assert() in terms of storing
* the assertion.
Dries Buytaert
committed
*
* @see DrupalTestCase::assert()
*/
Dries Buytaert
committed
public static function insertAssert($test_id, $test_class, $status, $message = '', $group = 'Other', array $caller = array()) {
Dries Buytaert
committed
// Convert boolean status to string status.
if (is_bool($status)) {
$status = $status ? 'pass' : 'fail';
}
$caller += array(
Dries Buytaert
committed
'function' => t('Unknown'),
'line' => 0,
'file' => t('Unknown'),
Dries Buytaert
committed
);
$assertion = array(
'test_id' => $test_id,
'test_class' => $test_class,
'status' => $status,
'message' => $message,
'message_group' => $group,
'function' => $caller['function'],
'line' => $caller['line'],
'file' => $caller['file'],
);
db_insert('simpletest')
->fields($assertion)
->execute();
}
/**
* Cycles through backtrace until the first non-assertion method is found.
*
* @return
* Array representing the true caller.
*/
protected function getAssertionCall() {
$backtrace = debug_backtrace();
// The first element is the call. The second element is the caller.
Dries Buytaert
committed
// We skip calls that occurred in one of the methods of our base classes
// or in an assertion function.
Dries Buytaert
committed
while (($caller = $backtrace[1]) &&
((isset($caller['class']) && isset($this->skipClasses[$caller['class']])) ||
substr($caller['function'], 0, 6) == 'assert')) {
// We remove that call.
array_shift($backtrace);
}
return _drupal_get_last_caller($backtrace);
}
Dries Buytaert
committed
/**
* Check to see if a value is not false (not an empty string, 0, NULL, or FALSE).
*
* @param $value
* The value on which the assertion is to be done.
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
Dries Buytaert
committed
*/
protected function assertTrue($value, $message = '', $group = 'Other') {
return $this->assert((bool) $value, $message ? $message : t('Value is TRUE'), $group);
Dries Buytaert
committed
}
/**
* Check to see if a value is false (an empty string, 0, NULL, or FALSE).
*
* @param $value
* The value on which the assertion is to be done.
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
Dries Buytaert
committed
*/
protected function assertFalse($value, $message = '', $group = 'Other') {
return $this->assert(!$value, $message ? $message : t('Value is FALSE'), $group);
Dries Buytaert
committed
}
/**
* Check to see if a value is NULL.
*
* @param $value
* The value on which the assertion is to be done.
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
Dries Buytaert
committed
*/
protected function assertNull($value, $message = '', $group = 'Other') {
return $this->assert(!isset($value), $message ? $message : t('Value is NULL'), $group);
Dries Buytaert
committed
}
/**
* Check to see if a value is not NULL.
*
* @param $value
* The value on which the assertion is to be done.
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
Dries Buytaert
committed
*/
protected function assertNotNull($value, $message = '', $group = 'Other') {
return $this->assert(isset($value), $message ? $message : t('Value is not NULL'), $group);
Dries Buytaert
committed
}
/**
* Check to see if two values are equal.
*
* @param $first
* The first value to check.
* @param $second
* The second value to check.
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
Dries Buytaert
committed
*/
protected function assertEqual($first, $second, $message = '', $group = 'Other') {
return $this->assert($first == $second, $message ? $message : t('First value is equal to second value'), $group);
Dries Buytaert
committed
}
/**
* Check to see if two values are not equal.
*
* @param $first
* The first value to check.
* @param $second
* The second value to check.
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
Dries Buytaert
committed
*/
protected function assertNotEqual($first, $second, $message = '', $group = 'Other') {
return $this->assert($first != $second, $message ? $message : t('First value is not equal to second value'), $group);
Dries Buytaert
committed
}
/**
* Check to see if two values are identical.
*
* @param $first
* The first value to check.
* @param $second
* The second value to check.
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
Dries Buytaert
committed
*/
protected function assertIdentical($first, $second, $message = '', $group = 'Other') {
return $this->assert($first === $second, $message ? $message : t('First value is identical to second value'), $group);
Dries Buytaert
committed
}
/**
* Check to see if two values are not identical.
*
* @param $first
* The first value to check.
* @param $second
* The second value to check.
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
Dries Buytaert
committed
*/
protected function assertNotIdentical($first, $second, $message = '', $group = 'Other') {
return $this->assert($first !== $second, $message ? $message : t('First value is not identical to second value'), $group);
Dries Buytaert
committed
}
/**
* Fire an assertion that is always positive.
*
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* TRUE.
*/
protected function pass($message = NULL, $group = 'Other') {
return $this->assert(TRUE, $message, $group);
Dries Buytaert
committed
}
/**
Dries Buytaert
committed
* Fire an assertion that is always negative.
*
Dries Buytaert
committed
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @return
* FALSE.
*/
protected function fail($message = NULL, $group = 'Other') {
return $this->assert(FALSE, $message, $group);
Dries Buytaert
committed
}
/**
* Fire an error assertion.
*
* @param $message
* The message to display along with the assertion.
* @param $group
* The type of assertion - examples are "Browser", "PHP".
* @param $caller
Dries Buytaert
committed
* The caller of the error.
* @return
* FALSE.
Dries Buytaert
committed
*/
Dries Buytaert
committed
protected function error($message = '', $group = 'Other', array $caller = NULL) {
Angie Byron
committed
if ($group == 'User notice') {
// Since 'User notice' is set by trigger_error() which is used for debug
// set the message to a status of 'debug'.
return $this->assert('debug', $message, 'Debug', $caller);
}
return $this->assert('exception', $message, $group, $caller);
Dries Buytaert
committed
}
/**
* Run all tests in this class.
*/
public function run() {
// Initialize verbose debugging.
Angie Byron
committed
simpletest_verbose(NULL, file_directory_path(), get_class($this));
Angie Byron
committed
// HTTP auth settings (<username>:<password>) for the simpletest browser
// when sending requests to the test site.
Dries Buytaert
committed
$username = variable_get('simpletest_username', NULL);
$password = variable_get('simpletest_password', NULL);
if ($username && $password) {
$this->httpauth_credentials = $username . ':' . $password;
}
Angie Byron
committed
Dries Buytaert
committed
set_error_handler(array($this, 'errorHandler'));
$methods = array();
// Iterate through all the methods in this class.
foreach (get_class_methods(get_class($this)) as $method) {
// If the current method starts with "test", run it - it's a test.
if (strtolower(substr($method, 0, 4)) == 'test') {
$this->setUp();
try {
$this->$method();
// Finish up.
}
catch (Exception $e) {
$this->exceptionHandler($e);
}
$this->tearDown();
}
}
// Clear out the error messages and restore error handler.
drupal_get_messages();
Dries Buytaert
committed
restore_error_handler();
}
/**
* Handle errors.
*
* Because this is registered in set_error_handler(), it has to be public.
Dries Buytaert
committed
* @see set_error_handler
Dries Buytaert
committed
*/
public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
Dries Buytaert
committed
if ($severity & error_reporting()) {
Dries Buytaert
committed
$error_map = array(
E_STRICT => 'Run-time notice',
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core error',
E_CORE_WARNING => 'Core warning',
E_USER_ERROR => 'User error',
E_USER_WARNING => 'User warning',
E_USER_NOTICE => 'User notice',
E_RECOVERABLE_ERROR => 'Recoverable error',
);
$backtrace = debug_backtrace();
$this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace));
Dries Buytaert
committed
}
return TRUE;
}
/**
* Handle exceptions.
*
* @see set_exception_handler
*/
protected function exceptionHandler($exception) {
$backtrace = $exception->getTrace();
// Push on top of the backtrace the call that generated the exception.
array_unshift($backtrace, array(
'line' => $exception->getLine(),
'file' => $exception->getFile(),
));
$this->error($exception->getMessage(), 'Uncaught exception', _drupal_get_last_caller($backtrace));
}
Dries Buytaert
committed
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
/**
* Generates a random string of ASCII characters of codes 32 to 126.
*
* The generated string includes alpha-numeric characters and common misc
* characters. Use this method when testing general input where the content
* is not restricted.
*
* @param $length
* Length of random string to generate which will be appended to $db_prefix.
* @return
* Randomly generated string.
*/
public static function randomString($length = 8) {
global $db_prefix;
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= chr(mt_rand(32, 126));
}
return str_replace('simpletest', 's', $db_prefix) . $str;
}
/**
* Generates a random string containing letters and numbers.
*
* The letters may be upper or lower case. This method is better for
* restricted inputs that do not accept certain characters. For example,
* when testing input fields that require machine readable values (ie without
* spaces and non-standard characters) this method is best.
*
* @param $length
* Length of random string to generate which will be appended to $db_prefix.
* @return
* Randomly generated string.
*/
public static function randomName($length = 8) {
global $db_prefix;
$values = array_merge(range(65, 90), range(97, 122), range(48, 57));
$max = count($values) - 1;
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= chr($values[mt_rand(0, $max)]);
}
return str_replace('simpletest', 's', $db_prefix) . $str;
}
}
/**
* Test case for Drupal unit tests.
*
* These tests can not access the database nor files. Calling any Drupal
* function that needs the database will throw exceptions. These include
* watchdog(), function_exists(), module_implements(),
Dries Buytaert
committed
* module_invoke_all() etc.
*/
class DrupalUnitTestCase extends DrupalTestCase {
/**
* Constructor for DrupalUnitTestCase.
*/
function __construct($test_id = NULL) {
parent::__construct($test_id);
$this->skipClasses[__CLASS__] = TRUE;
}
Dries Buytaert
committed
Dries Buytaert
committed
function setUp() {
global $db_prefix, $conf;
// Store necessary current values before switching to prefixed database.
$this->originalPrefix = $db_prefix;
$this->originalFileDirectory = file_directory_path();
// Generate temporary prefixed database to ensure that tests have a clean starting point.
$db_prefix = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}');
$conf['file_public_path'] = $this->originalFileDirectory . '/' . $db_prefix;
Dries Buytaert
committed
// If locale is enabled then t() will try to access the database and
// subsequently will fail as the database is not accessible.
$module_list = module_list();
if (isset($module_list['locale'])) {
$this->originalModuleList = $module_list;
unset($module_list['locale']);
module_list(TRUE, FALSE, FALSE, $module_list);
Dries Buytaert
committed
}
}
function tearDown() {
global $db_prefix, $conf;
if (preg_match('/simpletest\d+/', $db_prefix)) {
$conf['file_public_path'] = $this->originalFileDirectory;
Dries Buytaert
committed
// Return the database prefix to the original.
$db_prefix = $this->originalPrefix;
// Restore modules if necessary.
if (isset($this->originalModuleList)) {
module_list(TRUE, FALSE, FALSE, $this->originalModuleList);
Dries Buytaert
committed
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
}
}
}
}
/**
* Test case for typical Drupal tests.
*/
class DrupalWebTestCase extends DrupalTestCase {
/**
* The URL currently loaded in the internal browser.
*
* @var string
*/
protected $url;
/**
* The handle of the current cURL connection.
*
* @var resource
*/
protected $curlHandle;
/**
* The headers of the page currently loaded in the internal browser.
*
* @var Array
*/
protected $headers;
/**
* The content of the page currently loaded in the internal browser.
*
* @var string
*/
protected $content;
/**
* The content of the page currently loaded in the internal browser (plain text version).
*
* @var string
*/
protected $plainTextContent;
/**
* The parsed version of the page.
*
* @var SimpleXMLElement
*/
protected $elements = NULL;
/**
* The current user logged in using the internal browser.
*
* @var bool
*/
protected $loggedInUser = FALSE;
/**
* The current cookie file used by cURL.
*
* We do not reuse the cookies in further runs, so we do not need a file
* but we still need cookie handling, so we set the jar to NULL.
*/
protected $cookieFile = NULL;
/**
* Additional cURL options.
*
* DrupalWebTestCase itself never sets this but always obeys what is set.
*/
protected $additionalCurlOptions = array();
/**
* The original user, before it was changed to a clean uid = 1 for testing purposes.
*
* @var object
*/
protected $originalUser = NULL;
/**
* HTTP authentication credentials (<username>:<password>).
*/
protected $httpauth_credentials = NULL;
/**
* The current session name, if available.
*/
protected $session_name = NULL;
/**
* The current session ID, if available.
*/
protected $session_id = NULL;
Dries Buytaert
committed
/**
* Constructor for DrupalWebTestCase.
*/
function __construct($test_id = NULL) {
parent::__construct($test_id);
$this->skipClasses[__CLASS__] = TRUE;
}
/**
* Get a node from the database based on its title.
*
* @param title
* A node title, usually generated by $this->randomName().
*
* @return
* A node object matching $title.
*/
function drupalGetNodeByTitle($title) {
$nodes = node_load_multiple(array(), array('title' => $title));
// Load the first node returned from the database.
$returned_node = reset($nodes);
return $returned_node;
}
/**
* Creates a node based on default settings.
*
Dries Buytaert
committed
* @param $settings
* An associative array of settings to change from the defaults, keys are
Dries Buytaert
committed
* node properties, for example 'title' => 'Hello, world!'.
* @return
* Created node object.
*/
protected function drupalCreateNode($settings = array()) {
// Populate defaults array.
Dries Buytaert
committed
$settings += array(
Angie Byron
committed
'body' => array(FIELD_LANGUAGE_NONE => array(array())),
'title' => array(FIELD_LANGUAGE_NONE => array(array('value' => $this->randomName(8)))),
'comment' => 2,
Dries Buytaert
committed
'changed' => REQUEST_TIME,
'moderate' => 0,
'promote' => 0,
'revision' => 1,
'log' => '',
'status' => 1,
'sticky' => 0,
'type' => 'page',
'revisions' => NULL,
'taxonomy' => NULL,
);
Dries Buytaert
committed
// Use the original node's created time for existing nodes.
if (isset($settings['created']) && !isset($settings['date'])) {
$settings['date'] = format_date($settings['created'], 'custom', 'Y-m-d H:i:s O');
}
Dries Buytaert
committed
// If the node's user uid is not specified manually, use the currently
// logged in user if available, or else the user running the test.
if (!isset($settings['uid'])) {
if ($this->loggedInUser) {
$settings['uid'] = $this->loggedInUser->uid;
}
else {
global $user;
$settings['uid'] = $user->uid;
}
}
Dries Buytaert
committed
// Merge body field value and format separately.
$body = array(
'value' => $this->randomName(32),
'format' => filter_default_format(),
Dries Buytaert
committed
);
Angie Byron
committed
$settings['body'][FIELD_LANGUAGE_NONE][0] += $body;
Dries Buytaert
committed
Dries Buytaert
committed
$node = (object) $settings;
node_save($node);
// Small hack to link revisions to our test user.
Dries Buytaert
committed
db_update('node_revision')
->fields(array('uid' => $node->uid))
->condition('vid', $node->vid)
->execute();
return $node;
}
/**
* Creates a custom content type based on default settings.
*
Dries Buytaert
committed
* @param $settings
* An array of settings to change from the defaults.
* Example: 'type' => 'foo'.
Dries Buytaert
committed
* @return
* Created content type.
*/
protected function drupalCreateContentType($settings = array()) {
// Find a non-existent random type name.
do {
Dries Buytaert
committed
$name = strtolower($this->randomName(8));
Angie Byron
committed
} while (node_type_get_type($name));
// Populate defaults array.
$defaults = array(
'type' => $name,
'name' => $name,
Dries Buytaert
committed
'base' => 'node_content',
'description' => '',
'help' => '',
'title_label' => 'Title',
'body_label' => 'Body',
'has_title' => 1,
'has_body' => 1,
);
// Imposed values for a custom type.
$forced = array(
'orig_type' => '',
'old_type' => '',
'module' => 'node',
'custom' => 1,
'modified' => 1,
'locked' => 0,
);
$type = $forced + $settings + $defaults;
$type = (object)$type;
Dries Buytaert
committed
$saved_type = node_type_save($type);
node_types_rebuild();
Dries Buytaert
committed
menu_rebuild();
Dries Buytaert
committed
$this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type)));
Dries Buytaert
committed
// Reset permissions so that permissions for this content type are available.
$this->checkPermissions(array(), TRUE);
return $type;
}
/**
* Get a list files that can be used in tests.
*
Dries Buytaert
committed
* @param $type
* File type, possible values: 'binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'.
* @param $size
* File size in bytes to match. Please check the tests/files folder.
* @return
* List of files that match filter.
*/
protected function drupalGetTestFiles($type, $size = NULL) {
$files = array();
// Make sure type is valid.
if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) {
Dries Buytaert
committed
// Use original file directory instead of one created during setUp().
$path = $this->originalFileDirectory . '/simpletest';
$files = file_scan_directory($path, '/' . $type . '\-.*/');
// If size is set then remove any files that are not of that size.
if ($size !== NULL) {
foreach ($files as $file) {
$stats = stat($file->uri);
if ($stats['size'] != $size) {
unset($files[$file->uri]);
}
}
}
}
usort($files, array($this, 'drupalCompareFiles'));
return $files;
}
/**
* Compare two files based on size and file name.
*/
protected function drupalCompareFiles($file1, $file2) {
$compare_size = filesize($file1->uri) - filesize($file2->uri);
if ($compare_size) {
// Sort by file size.
return $compare_size;
}
else {
// The files were the same size, so sort alphabetically.
return strnatcmp($file1->name, $file2->name);
}
}
/**
* Create a user with a given set of permissions. The permissions correspond to the
* names given on the privileges page.
*
Dries Buytaert
committed
* @param $permissions
* Array of permission names to assign to user.
* @return
* A fully loaded user object with pass_raw property, or FALSE if account
* creation fails.
*/
protected function drupalCreateUser($permissions = array('access comments', 'access content', 'post comments', 'post comments without approval')) {
// Create a role with the given permission set.
if (!($rid = $this->drupalCreateRole($permissions))) {
return FALSE;
}
// Create a user assigned to that role.
$edit = array();
$edit['name'] = $this->randomName();
$edit['mail'] = $edit['name'] . '@example.com';
$edit['roles'] = array($rid => $rid);
$edit['pass'] = user_password();
$edit['status'] = 1;
$account = user_save(drupal_anonymous_user(), $edit);
$this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login'));
if (empty($account->uid)) {
return FALSE;
}
// Add the raw password so that we can log in as this user.
$account->pass_raw = $edit['pass'];
return $account;
}
/**
* Internal helper function; Create a role with specified permissions.
*
Dries Buytaert
committed
* @param $permissions
* Array of permission names to assign to role.
Dries Buytaert
committed
* @param $name
* (optional) String for the name of the role. Defaults to a random string.
Dries Buytaert
committed
* @return
* Role ID of newly created role, or FALSE if role creation failed.
*/
Dries Buytaert
committed
protected function drupalCreateRole(array $permissions, $name = NULL) {
// Generate random name if it was not passed.
if (!$name) {
$name = $this->randomName();
}
Dries Buytaert
committed
// Check the all the permissions strings are valid.
Dries Buytaert
committed
if (!$this->checkPermissions($permissions)) {
return FALSE;
}
// Create new role.
Dries Buytaert
committed
$role = new stdClass();
$role->name = $name;
user_role_save($role);
Dries Buytaert
committed
user_role_grant_permissions($role->rid, $permissions);
Dries Buytaert
committed
$this->assertTrue(isset($role->rid), t('Created role of name: @name, id: @rid', array('@name' => $name, '@rid' => (isset($role->rid) ? $role->rid : t('-n/a-')))), t('Role'));
if ($role && !empty($role->rid)) {
Dries Buytaert
committed
$count = db_query('SELECT COUNT(*) FROM {role_permission} WHERE rid = :rid', array(':rid' => $role->rid))->fetchField();
$this->assertTrue($count == count($permissions), t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role'));
return $role->rid;
}
else {
return FALSE;
}
}
Dries Buytaert
committed
/**
* Check to make sure that the array of permissions are valid.
*
Dries Buytaert
committed
* @param $permissions
* Permissions to check.
* @param $reset
* Reset cached available permissions.
* @return
* TRUE or FALSE depending on whether the permissions are valid.
Dries Buytaert
committed
*/
protected function checkPermissions(array $permissions, $reset = FALSE) {
$available = &drupal_static(__FUNCTION__);
Dries Buytaert
committed
if (!isset($available) || $reset) {
$available = array_keys(module_invoke_all('permission'));
Dries Buytaert
committed
}
$valid = TRUE;
foreach ($permissions as $permission) {
if (!in_array($permission, $available)) {
$this->fail(t('Invalid permission %permission.', array('%permission' => $permission)), t('Role'));
$valid = FALSE;
}
}
return $valid;
}
/**
Dries Buytaert
committed
* Log in a user with the internal browser.
*
* If a user is already logged in, then the current user is logged out before
* logging in the specified user.
*
* Please note that neither the global $user nor the passed in user object is
* populated with data of the logged in user. If you need full access to the
* user object after logging in, it must be updated manually. If you also need
* access to the plain-text password of the user (set by drupalCreateUser()),
* e.g. to login the same user again, then it must be re-assigned manually.
* For example:
* @code
* // Create a user.
* $account = $this->drupalCreateUser(array());
* $this->drupalLogin($account);
* // Load real user object.
* $pass_raw = $account->pass_raw;
* $account = user_load($account->uid);
* $account->pass_raw = $pass_raw;
* @endcode
*
Dries Buytaert
committed
* @param $user
* User object representing the user to login.
Dries Buytaert
committed
*
* @see drupalCreateUser()
*/
protected function drupalLogin(stdClass $user) {
Dries Buytaert
committed
if ($this->loggedInUser) {
$this->drupalLogout();
}
$edit = array(
'name' => $user->name,
'pass' => $user->pass_raw
);
$this->drupalPost('user', $edit, t('Log in'));
Dries Buytaert
committed
// If a "log out" link appears on the page, it is almost certainly because
// the login was successful.
$pass = $this->assertLink(t('Log out'), 0, t('User %name successfully logged in.', array('%name' => $user->name)), t('User login'));
Dries Buytaert
committed
if ($pass) {
$this->loggedInUser = $user;
}
}
/**
* Generate a token for the currently logged in user.
*/
protected function drupalGetToken($value = '') {
$private_key = drupal_get_private_key();
return md5($this->session_id . $value . $private_key);
}