Newer
Older
<?php
Dries Buytaert
committed
/**
* Global variable that holds information about the tests being run.
*
* An array, with the following keys:
* - 'test_run_id': the ID of the test being run, in the form 'simpletest_%"
* - 'in_child_site': TRUE if the current request is a cURL request from
* the parent site.
*
* @var array
*/
global $drupal_test_info;
/**
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;
/**
Dries Buytaert
committed
* The database prefix of this test run.
*
* @var string
*/
Dries Buytaert
committed
protected $databasePrefix = 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
*/
protected $timeLimit = 500;
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);
Angie Byron
committed
/**
* Flag to indicate whether the test has been set up.
*
* The setUp() method isolates the test from the parent Drupal site by
* creating a random prefix for the database and setting up a clean file
* storage directory. The tearDown() method then cleans up this test
* environment. We must ensure that setUp() has been run. Otherwise,
* tearDown() will act on the parent Drupal site rather than the test
* environment, destroying live data.
*/
protected $setup = FALSE;
David Rothstein
committed
protected $setupDatabasePrefix = FALSE;
protected $setupEnvironment = FALSE;
Dries Buytaert
committed
/**
Dries Buytaert
committed
* Constructor for DrupalTestCase.
Dries Buytaert
committed
*
* @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) {
// 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
}
// 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.
Angie Byron
committed
try {
$connection = Database::getConnection('default', 'simpletest_original_default');
}
catch (DatabaseConnectionNotDefinedException $e) {
// If the test was not set up, the simpletest_original_default
// connection does not exist.
$connection = Database::getConnection('default', 'default');
}
$connection
Dries Buytaert
committed
->insert('simpletest')
Dries Buytaert
committed
->fields($assertion)
->execute();
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
*
* @return
* Message ID of the stored assertion.
*
Dries Buytaert
committed
* @see DrupalTestCase::assert()
* @see DrupalTestCase::deleteAssert()
Dries Buytaert
committed
*/
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'],
);
return db_insert('simpletest')
Dries Buytaert
committed
->fields($assertion)
->execute();
}
/**
* Delete an assertion record by message ID.
Angie Byron
committed
*
* @param $message_id
* Message ID of the assertion to delete.
* @return
* TRUE if the assertion was deleted, FALSE otherwise.
Angie Byron
committed
*
* @see DrupalTestCase::insertAssert()
*/
public static function deleteAssert($message_id) {
return (bool) db_delete('simpletest')
->condition('message_id', $message_id)
->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 @value is TRUE.', array('@value' => var_export($value, 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 @value is FALSE.', array('@value' => var_export($value, TRUE))), $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 @value is NULL.', array('@value' => var_export($value, TRUE))), $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 @value is not NULL.', array('@value' => var_export($value, TRUE))), $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('Value @first is equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $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('Value @first is not equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $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('Value @first is identical to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $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('Value @first is not identical to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $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
}
/**
* Logs verbose message in a text file.
*
* The a link to the vebose message will be placed in the test results via
* as a passing assertion with the text '[verbose message]'.
*
* @param $message
* The verbose message to be stored.
*
* @see simpletest_verbose()
*/
protected function verbose($message) {
if ($id = simpletest_verbose($message)) {
$url = file_create_url($this->originalFileDirectory . '/simpletest/verbose/' . get_class($this) . '-' . $id . '.html');
$this->error(l(t('Verbose message'), $url, array('attributes' => array('target' => '_blank'))), 'User notice');
}
}
Dries Buytaert
committed
/**
* Run all tests in this class.
*
* Regardless of whether $methods are passed or not, only method names
* starting with "test" are executed.
*
* @param $methods
* (optional) A list of method names in the test case class to run; e.g.,
* array('testFoo', 'testBar'). By default, all methods of the class are
* taken into account, but it can be useful to only run a few selected test
* methods during debugging.
*/
public function run(array $methods = array()) {
// Initialize verbose debugging.
simpletest_verbose(NULL, variable_get('file_public_path', conf_path() . '/files'), 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
$this->httpauth_method = variable_get('simpletest_httpauth_method', CURLAUTH_BASIC);
$username = variable_get('simpletest_httpauth_username', NULL);
$password = variable_get('simpletest_httpauth_password', NULL);
Dries Buytaert
committed
if ($username && $password) {
$this->httpauth_credentials = $username . ':' . $password;
}
Angie Byron
committed
Dries Buytaert
committed
set_error_handler(array($this, 'errorHandler'));
$class = get_class($this);
// Iterate through all the methods in this class, unless a specific list of
// methods to run was passed.
$class_methods = get_class_methods($class);
if ($methods) {
$class_methods = array_intersect($class_methods, $methods);
}
foreach ($class_methods as $method) {
Dries Buytaert
committed
// If the current method starts with "test", run it - it's a test.
if (strtolower(substr($method, 0, 4)) == 'test') {
// Insert a fail record. This will be deleted on completion to ensure
// that testing completed.
$method_info = new ReflectionMethod($class, $method);
$caller = array(
'file' => $method_info->getFileName(),
'line' => $method_info->getStartLine(),
'function' => $class . '->' . $method . '()',
);
$completion_check_id = DrupalTestCase::insertAssert($this->testId, $class, FALSE, t('The test did not complete due to a fatal error.'), 'Completion check', $caller);
$this->setUp();
Angie Byron
committed
if ($this->setup) {
try {
$this->$method();
// Finish up.
}
catch (Exception $e) {
$this->exceptionHandler($e);
}
$this->tearDown();
Angie Byron
committed
else {
$this->fail(t("The test cannot be executed because it has not been set up properly."));
// Remove the completion check record.
DrupalTestCase::deleteAssert($completion_check_id);
}
}
// Clear out the error messages and restore error handler.
drupal_get_messages();
Dries Buytaert
committed
restore_error_handler();
}
/**
* Handle errors during test runs.
Dries Buytaert
committed
*
* Because this is registered in set_error_handler(), it has to be public.
Dries Buytaert
committed
* @see set_error_handler
*/
public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
Angie Byron
committed
if ($severity & error_reporting()) {
$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(),
));
Dries Buytaert
committed
require_once DRUPAL_ROOT . '/includes/errors.inc';
Dries Buytaert
committed
// The exception message is run through check_plain() by _drupal_decode_exception().
$this->error(t('%type: !message in %function (line %line of %file).', _drupal_decode_exception($exception)), 'Uncaught exception', _drupal_get_last_caller($backtrace));
}
Dries Buytaert
committed
/**
* Generates a random string of ASCII characters of codes 32 to 126.
*
* The generated string includes alpha-numeric characters and common
* miscellaneous characters. Use this method when testing general input
* where the content is not restricted.
*
* Do not use this method when special characters are not possible (e.g., in
* machine or file names that have already been validated); instead,
* use DrupalWebTestCase::randomName().
Dries Buytaert
committed
*
* @param $length
Dries Buytaert
committed
* Length of random string to generate.
*
Dries Buytaert
committed
* @return
* Randomly generated string.
*
* @see DrupalWebTestCase::randomName()
Dries Buytaert
committed
*/
public static function randomString($length = 8) {
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= chr(mt_rand(32, 126));
}
Dries Buytaert
committed
return $str;
Dries Buytaert
committed
}
/**
* Generates a random string containing letters and numbers.
*
Dries Buytaert
committed
* The string will always start with a letter. 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 (i.e. without spaces and non-standard
* characters) this method is best.
Dries Buytaert
committed
*
* Do not use this method when testing unvalidated user input. Instead, use
* DrupalWebTestCase::randomString().
*
Dries Buytaert
committed
* @param $length
Dries Buytaert
committed
* Length of random string to generate.
*
Dries Buytaert
committed
* @return
* Randomly generated string.
*
* @see DrupalWebTestCase::randomString()
Dries Buytaert
committed
*/
public static function randomName($length = 8) {
$values = array_merge(range(65, 90), range(97, 122), range(48, 57));
$max = count($values) - 1;
Dries Buytaert
committed
$str = chr(mt_rand(97, 122));
for ($i = 1; $i < $length; $i++) {
Dries Buytaert
committed
$str .= chr($values[mt_rand(0, $max)]);
}
Dries Buytaert
committed
return $str;
Dries Buytaert
committed
}
Angie Byron
committed
/**
* Converts a list of possible parameters into a stack of permutations.
*
* Takes a list of parameters containing possible values, and converts all of
* them into a list of items containing every possible permutation.
*
* Example:
* @code
* $parameters = array(
* 'one' => array(0, 1),
* 'two' => array(2, 3),
* );
* $permutations = DrupalTestCase::generatePermutations($parameters)
Angie Byron
committed
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
* // Result:
* $permutations == array(
* array('one' => 0, 'two' => 2),
* array('one' => 1, 'two' => 2),
* array('one' => 0, 'two' => 3),
* array('one' => 1, 'two' => 3),
* )
* @endcode
*
* @param $parameters
* An associative array of parameters, keyed by parameter name, and whose
* values are arrays of parameter values.
*
* @return
* A list of permutations, which is an array of arrays. Each inner array
* contains the full list of parameters that have been passed, but with a
* single value only.
*/
public static function generatePermutations($parameters) {
$all_permutations = array(array());
foreach ($parameters as $parameter => $values) {
$new_permutations = array();
// Iterate over all values of the parameter.
foreach ($values as $value) {
// Iterate over all existing permutations.
foreach ($all_permutations as $permutation) {
// Add the new parameter value to existing permutations.
$new_permutations[] = $permutation + array($parameter => $value);
}
}
// Replace the old permutations with the new permutations.
$all_permutations = $new_permutations;
}
return $all_permutations;
}
Dries Buytaert
committed
}
/**
* 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(), module_implements(), module_invoke_all() etc.
Dries Buytaert
committed
*/
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
/**
* Sets up unit test environment.
*
* Unlike DrupalWebTestCase::setUp(), DrupalUnitTestCase::setUp() does not
* install modules because tests are performed without accessing the database.
* Any required files must be explicitly included by the child class setUp()
* method.
*/
protected function setUp() {
Dries Buytaert
committed
global $conf;
Dries Buytaert
committed
Dries Buytaert
committed
// Store necessary current values before switching to the test environment.
$this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
Dries Buytaert
committed
Dries Buytaert
committed
// Reset all statics so that test is performed with a clean environment.
drupal_static_reset();
Dries Buytaert
committed
// Generate temporary prefixed database to ensure that tests have a clean starting point.
Dries Buytaert
committed
$this->databasePrefix = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}');
Dries Buytaert
committed
// Create test directory.
$public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10);
file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
$conf['file_public_path'] = $public_files_directory;
Dries Buytaert
committed
// Clone the current connection and replace the current prefix.
$connection_info = Database::getConnectionInfo('default');
Database::renameConnection('default', 'simpletest_original_default');
foreach ($connection_info as $target => $value) {
$connection_info[$target]['prefix'] = array(
'default' => $value['prefix']['default'] . $this->databasePrefix,
);
}
Database::addConnectionInfo('default', 'default', $connection_info['default']);
Dries Buytaert
committed
Angie Byron
committed
// Set user agent to be consistent with web test case.
Dries Buytaert
committed
$_SERVER['HTTP_USER_AGENT'] = $this->databasePrefix;
Angie Byron
committed
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
}
Angie Byron
committed
$this->setup = TRUE;
Dries Buytaert
committed
}
protected function tearDown() {
Dries Buytaert
committed
global $conf;
// Get back to the original connection.
Database::removeConnection('default');
Database::renameConnection('simpletest_original_default', 'default');
$conf['file_public_path'] = $this->originalFileDirectory;
// Restore modules if necessary.
if (isset($this->originalModuleList)) {
module_list(TRUE, FALSE, FALSE, $this->originalModuleList);
Dries Buytaert
committed
}
}
}
/**
* Test case for typical Drupal tests.
*/
class DrupalWebTestCase extends DrupalTestCase {
Dries Buytaert
committed
/**
* The profile to install as a basis for testing.
*
* @var string
*/
protected $profile = 'standard';
Dries Buytaert
committed
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
/**
* 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;
Dries Buytaert
committed
/**
* The value of the Drupal.settings JavaScript variable for the page currently loaded in the internal browser.
*
* @var Array
*/
protected $drupalSettings;
Dries Buytaert
committed
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
/**
* 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;
/**
* The original shutdown handlers array, before it was cleaned for testing purposes.
*
* @var array
*/
protected $originalShutdownCallbacks = array();
Dries Buytaert
committed
/**
* HTTP authentication method
*/
protected $httpauth_method = CURLAUTH_BASIC;
Dries Buytaert
committed
/**
* 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
/**
* Whether the files were copied to the test files directory.
*/
protected $generatedTestFiles = FALSE;
Dries Buytaert
committed
/**
* The number of redirects followed during the handling of a request.
*/
protected $redirect_count;
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.
*
Angie Byron
committed
* @param $title
* A node title, usually generated by $this->randomName().
Dries Buytaert
committed
* @param $reset
* (optional) Whether to reset the internal node_load() cache.
*
* @return
* A node object matching $title.
*/
Dries Buytaert
committed
function drupalGetNodeByTitle($title, $reset = FALSE) {
$nodes = node_load_multiple(array(), array('title' => $title), $reset);
// 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(
'body' => array(LANGUAGE_NONE => array(array())),
'title' => $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,
'language' => LANGUAGE_NONE,
);
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
);
$settings['body'][$settings['language']][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,