Newer
Older
<?php
// $Id$
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);
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.
Dries Buytaert
committed
Database::getConnection('default', 'simpletest_original_default')
->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();
try {
$this->$method();
// Finish up.
}
catch (Exception $e) {
$this->exceptionHandler($e);
}
$this->tearDown();
// 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 misc
* characters. Use this method when testing general input where the content
* is not restricted.
*
* @param $length
Dries Buytaert
committed
* Length of random string to generate.
Dries Buytaert
committed
* @return
* Randomly generated string.
*/
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
*
* @param $length
Dries Buytaert
committed
* Length of random string to generate.
Dries Buytaert
committed
* @return
* Randomly generated string.
*/
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
}
}
/**
* 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
}
}
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
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
/**
* 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
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
/**
* 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.
*
* @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,
'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();
node_add_body_field($type);
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) {
Dries Buytaert
committed
if (empty($this->generatedTestFiles)) {
// Generate binary test files.
$lines = array(64, 1024);
$count = 0;
foreach ($lines as $line) {
simpletest_generate_file('binary-' . $count++, 64, $line, 'binary');
}
// Generate text test files.
$lines = array(16, 256, 1024, 2048, 20480);
$count = 0;
foreach ($lines as $line) {
simpletest_generate_file('text-' . $count++, 64, $line);
}
// Copy other test files from simpletest.
$original = drupal_get_path('module', 'simpletest') . '/files';
$files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/');
foreach ($files as $file) {
file_unmanaged_copy($file->uri, variable_get('file_public_path', conf_path() . '/files'));
Dries Buytaert
committed
}
Dries Buytaert
committed
$this->generatedTestFiles = TRUE;
}
$files = array();
// Make sure type is valid.
if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) {
Angie Byron
committed
$files = file_scan_directory('public://', '/' . $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