0, '#fail' => 0, '#exception' => 0, '#debug' => 0, ); /** * Assertions thrown in that test case. * * @var Array */ protected $assertions = array(); /** * 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 "WebTestBase->drupalLogin()', we would like to see the test * that called it. So we need to skip the classes defining these helper * methods. */ protected $skipClasses = array(__CLASS__ => TRUE); /** * Flag to indicate whether the test has been set up. * * The setUp() method isolates the test from the parent Drupal site by * creating a random prefix for the database and setting up a clean file * storage directory. The tearDown() method then cleans up this test * environment. We must ensure that setUp() has been run. Otherwise, * tearDown() will act on the parent Drupal site rather than the test * environment, destroying live data. */ protected $setup = FALSE; protected $setupDatabasePrefix = FALSE; protected $setupEnvironment = FALSE; /** * TRUE if verbose debugging is enabled. * * @var boolean */ protected $verbose = FALSE; /** * Incrementing identifier for verbose output filenames. * * @var integer */ protected $verboseId = 0; /** * Safe class name for use in verbose output filenames. * * Namespaces separator (\) replaced with _. * * @var string */ protected $verboseClassName; /** * Directory where verbose output files are put. * * @var string */ protected $verboseDirectory; /** * The original database prefix when running inside Simpletest. * * @var string */ protected $originalPrefix; /** * URL to the verbose output file directory. * * @var string */ protected $verboseDirectoryUrl; /** * The settings array. */ protected $originalSettings; /** * Constructor for Test. * * @param $test_id * Tests with the same id are reported together. */ public function __construct($test_id = NULL) { $this->testId = $test_id; } /** * Checks the matching requirements for Test. * * @return * Array of errors containing a list of unmet requirements. */ protected function checkRequirements() { return array(); } /** * Internal helper: stores the assert. * * @param $status * Can be 'pass', 'fail', 'exception'. * TRUE is a synonym for 'pass', FALSE for 'fail'. * @param $message * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @param $caller * By default, the assert comes from a function whose name starts with * 'test'. Instead, you can specify where this assert originates from * by passing in an associative array as $caller. Key 'file' is * the name of the source file, 'line' is the line number and 'function' * is the caller function itself. */ protected function assert($status, $message = '', $group = 'Other', array $caller = NULL) { // Convert boolean status to string status. 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(); } // 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. self::getDatabaseConnection() ->insert('simpletest') ->fields($assertion) ->execute(); // We do not use a ternary operator here to allow a breakpoint on // test failure. if ($status == 'pass') { return TRUE; } else { return FALSE; } } /** * 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 Drupal\simpletest\TestBase::assert() in terms * of storing the assertion. * * @return * Message ID of the stored assertion. * * @see Drupal\simpletest\TestBase::assert() * @see Drupal\simpletest\TestBase::deleteAssert() */ public static function insertAssert($test_id, $test_class, $status, $message = '', $group = 'Other', array $caller = array()) { // Convert boolean status to string status. if (is_bool($status)) { $status = $status ? 'pass' : 'fail'; } $caller += array( 'function' => t('Unknown'), 'line' => 0, 'file' => t('Unknown'), ); $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 self::getDatabaseConnection() ->insert('simpletest') ->fields($assertion) ->execute(); } /** * Delete an assertion record by message ID. * * @param $message_id * Message ID of the assertion to delete. * @return * TRUE if the assertion was deleted, FALSE otherwise. * * @see Drupal\simpletest\TestBase::insertAssert() */ public static function deleteAssert($message_id) { return (bool) self::getDatabaseConnection() ->delete('simpletest') ->condition('message_id', $message_id) ->execute(); } /** * Returns the database connection to the site running Simpletest. * * @return Drupal\Core\Database\Connection * The database connection to use for inserting assertions. */ public static function getDatabaseConnection() { try { $connection = Database::getConnection('default', 'simpletest_original_default'); } catch (ConnectionNotDefinedException $e) { // If the test was not set up, the simpletest_original_default // connection does not exist. $connection = Database::getConnection('default', 'default'); } return $connection; } /** * 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. // We skip calls that occurred in one of the methods of our base classes // or in an assertion function. 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); } /** * 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 * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * TRUE if the assertion succeeded, FALSE otherwise. */ 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); } /** * 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 * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * TRUE if the assertion succeeded, FALSE otherwise. */ 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); } /** * Check to see if a value is NULL. * * @param $value * The value on which the assertion is to be done. * @param $message * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * TRUE if the assertion succeeded, FALSE otherwise. */ 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); } /** * Check to see if a value is not NULL. * * @param $value * The value on which the assertion is to be done. * @param $message * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * TRUE if the assertion succeeded, FALSE otherwise. */ 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); } /** * Check to see if two values are equal. * * @param $first * The first value to check. * @param $second * The second value to check. * @param $message * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * TRUE if the assertion succeeded, FALSE otherwise. */ 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); } /** * 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 * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * TRUE if the assertion succeeded, FALSE otherwise. */ 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); } /** * Check to see if two values are identical. * * @param $first * The first value to check. * @param $second * The second value to check. * @param $message * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * TRUE if the assertion succeeded, FALSE otherwise. */ 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); } /** * 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 * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * TRUE if the assertion succeeded, FALSE otherwise. */ 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); } /** * Checks to see if two objects are identical. * * @param object $object1 * The first object to check. * @param object $object2 * The second object to check. * @param $message * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * * @return * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertIdenticalObject($object1, $object2, $message = '', $group = 'Other') { $message = $message ?: format_string('!object1 is identical to !object2', array( '!object1' => var_export($object1, TRUE), '!object2' => var_export($object2, TRUE), )); $identical = TRUE; foreach ($object1 as $key => $value) { $identical = $identical && isset($object2->$key) && $object2->$key === $value; } return $this->assertTrue($identical, $message, $group); } /** * Fire an assertion that is always positive. * * @param $message * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * TRUE. */ protected function pass($message = NULL, $group = 'Other') { return $this->assert(TRUE, $message, $group); } /** * Fire an assertion that is always negative. * * @param $message * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @return * FALSE. */ protected function fail($message = NULL, $group = 'Other') { return $this->assert(FALSE, $message, $group); } /** * Fire an error assertion. * * @param $message * (optional) A message to display with the assertion. Do not translate * messages: use format_string() to embed variables in the message text, not * t(). If left blank, a default message will be displayed. * @param $group * (optional) The group this message is in, which is displayed in a column * in test output. Use 'Debug' to indicate this is debugging output. Do not * translate this string. Defaults to 'Other'; most tests do not override * this default. * @param $caller * The caller of the error. * @return * FALSE. */ protected function error($message = '', $group = 'Other', array $caller = NULL) { 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); } /** * 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) { // Do nothing if verbose debugging is disabled. if (!$this->verbose) { return; } $message = '
' . format_backtrace($verbose_backtrace) . ''; } $this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace)); } return TRUE; } /** * Handle exceptions. * * @see set_exception_handler */ protected function exceptionHandler($exception) { require_once DRUPAL_ROOT . '/core/includes/errors.inc'; $backtrace = $exception->getTrace(); $verbose_backtrace = $backtrace; // Push on top of the backtrace the call that generated the exception. array_unshift($backtrace, array( 'line' => $exception->getLine(), 'file' => $exception->getFile(), )); // The exception message is run through check_plain() by _drupal_decode_exception(). $decoded_exception = _drupal_decode_exception($exception); unset($decoded_exception['backtrace']); $message = format_string('%type: !message in %function (line %line of %file).
!backtrace', $decoded_exception + array( '!backtrace' => format_backtrace($verbose_backtrace), )); $this->error($message, 'Uncaught exception', _drupal_get_last_caller($backtrace)); } /** * Changes in memory settings. * * @param $name * The name of the setting to return. * @param $value * The value of the setting. * * @see \Drupal\Component\Utility\Settings::get() */ protected function settingsSet($name, $value) { $settings = settings()->getAll(); $settings[$name] = $value; new Settings($settings); } /** * 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 * Drupal\simpletest\TestBase::randomName(). * * @param $length * Length of random string to generate. * * @return * Randomly generated string. * * @see Drupal\simpletest\TestBase::randomName() */ public static function randomString($length = 8) { $str = ''; for ($i = 0; $i < $length; $i++) { $str .= chr(mt_rand(32, 126)); } return $str; } /** * Generates a random string containing letters and numbers. * * 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. * * Do not use this method when testing unvalidated user input. Instead, use * Drupal\simpletest\TestBase::randomString(). * * @param $length * Length of random string to generate. * * @return * Randomly generated string. * * @see Drupal\simpletest\TestBase::randomString() */ public static function randomName($length = 8) { $values = array_merge(range(65, 90), range(97, 122), range(48, 57)); $max = count($values) - 1; $str = chr(mt_rand(97, 122)); for ($i = 1; $i < $length; $i++) { $str .= chr($values[mt_rand(0, $max)]); } return $str; } /** * Generates a random PHP object. * * @param int $size * The number of random keys to add to the object. * * @return \stdClass * The generated object, with the specified number of random keys. Each key * has a random string value. */ public static function randomObject($size = 4) { $object = new \stdClass(); for ($i = 0; $i < $size; $i++) { $random_key = self::randomName(); $random_value = self::randomString(); $object->{$random_key} = $random_value; } return $object; } /** * 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 = TestBase::generatePermutations($parameters); * // 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; } /** * Ensures test files are deletable within file_unmanaged_delete_recursive(). * * Some tests chmod generated files to be read only. During tearDown() and * other cleanup operations, these files need to get deleted too. */ public static function filePreDeleteCallback($path) { chmod($path, 0700); } }