- <?php
- * @file
- * Provides testing functionality.
- */
-
- * Implements hook_menu().
- */
- function simpletest_menu() {
- $items['admin/config/development/testing'] = array(
- 'title' => 'Testing',
- 'page callback' => 'backdrop_get_form',
- 'page arguments' => array('simpletest_test_form'),
- 'description' => 'Run tests against Backdrop core and your modules. These tests help assure that your site code is working as designed.',
- 'access arguments' => array('administer unit tests'),
- 'file' => 'simpletest.pages.inc',
- 'weight' => -5,
- );
- $items['admin/config/development/testing/list'] = array(
- 'title' => 'List tests',
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- );
- $items['admin/config/development/testing/settings'] = array(
- 'title' => 'Settings',
- 'page callback' => 'backdrop_get_form',
- 'page arguments' => array('simpletest_settings_form'),
- 'access arguments' => array('administer unit tests'),
- 'type' => MENU_LOCAL_TASK,
- 'file' => 'simpletest.pages.inc',
- );
- $items['admin/config/development/testing/results/%'] = array(
- 'title' => 'Test result',
- 'page callback' => 'backdrop_get_form',
- 'page arguments' => array('simpletest_result_form', 5),
- 'description' => 'View result of tests.',
- 'access arguments' => array('administer unit tests'),
- 'file' => 'simpletest.pages.inc',
- );
- return $items;
- }
-
- * Implements hook_permission().
- */
- function simpletest_permission() {
- return array(
- 'administer unit tests' => array(
- 'title' => t('Administer tests'),
- 'restrict access' => TRUE,
- 'warning' => t('Tests can use a large amount of CPU resources. Running them can potentially cause performance issues on the site.'),
- ),
- );
- }
-
- * Implements hook_theme().
- */
- function simpletest_theme() {
- $base = array(
- 'file' => 'simpletest.theme.inc',
- );
-
- return array(
- 'simpletest_test_table' => array(
- 'render element' => 'table',
- ) + $base,
- 'simpletest_result_summary' => array(
- 'render element' => 'form',
- ) + $base,
- );
- }
-
- * Implements hook_config_info().
- */
- function simpletest_config_info() {
- $prefixes['simpletest.settings'] = array(
- 'label' => t('Testing settings'),
- 'group' => t('Configuration'),
- );
- $prefixes['simpletest.testconfig'] = array(
- 'label' => t('Configuration file for testing purposes'),
- 'group' => t('Test Data'),
- );
- $prefixes['simpletest.secondconfig'] = array(
- 'label' => t('Second configuration file for testing purposes'),
- 'group' => t('Test Data'),
- );
- return $prefixes;
- }
-
- * Implements hook_autoload_info().
- */
- function simpletest_autoload_info() {
- return array(
- 'BackdropTestCase' => 'backdrop_web_test_case.php',
- 'BackdropUnitTestCase' => 'backdrop_web_test_case.php',
- 'BackdropWebTestCase' => 'backdrop_web_test_case.php',
- 'BackdropWebTestCaseCache' => 'backdrop_web_test_case_cache.php',
- );
- }
-
- * Implements hook_js_alter().
- */
- function simpletest_js_alter(&$javascript) {
-
-
- $simpletest = backdrop_get_path('module', 'simpletest') . '/js/simpletest.js';
- if (array_key_exists($simpletest, $javascript) && array_key_exists('core/misc/tableselect.js', $javascript)) {
- $javascript[$simpletest]['weight'] = $javascript['core/misc/tableselect.js']['weight'] - 1;
- }
- }
-
- function _simpletest_format_summary_line($summary) {
- $args = array(
- '@pass' => format_plural(isset($summary['#pass']) ? $summary['#pass'] : 0, '1 pass', '@count passes'),
- '@fail' => format_plural(isset($summary['#fail']) ? $summary['#fail'] : 0, '1 fail', '@count fails'),
- '@exception' => format_plural(isset($summary['#exception']) ? $summary['#exception'] : 0, '1 exception', '@count exceptions'),
- );
- if (!$summary['#debug']) {
- $message = t('@pass, @fail, and @exception.', $args);
- }
- else {
- $args['@debug'] = format_plural(isset($summary['#debug']) ? $summary['#debug'] : 0, '1 debug message', '@count debug messages');
- $message = t('@pass, @fail, @exception, and @debug.', $args);
- }
- if (isset($summary['#duration'])) {
- $message .= ' [' . $summary['#duration'] . 's]';
- }
- return $message;
- }
-
- * Actually runs tests.
- *
- * @param $test_list
- * List of tests to run.
- * @param $reporter
- * Which reporter to use. Allowed values are: text, xml, html and backdrop,
- * backdrop being the default.
- */
- function simpletest_run_tests($test_list, $reporter = 'backdrop') {
- $test_id = db_query("SELECT MAX(test_id) FROM {simpletest_prefix}")->fetchField() + 1;
-
-
- if (file_exists('public://simpletest/verbose')) {
- file_unmanaged_delete_recursive('public://simpletest/verbose');
- }
-
- $cache = config_get('simpletest.settings', 'simpletest_cache');
- if ($cache) {
- $profiles = simpletest_get_profiles();
- foreach ($profiles as $profile) {
- try {
- $test_cache = new BackdropWebTestCaseCache();
- $test_cache->setProfile($profile->name);
- if (!$test_cache->isCached()) {
- $test_cache->prepareCache(TRUE);
- }
- }
- catch (Exception $e) {
- watchdog_exception('simpletest', $e, $e->getMessage());
- }
- }
- }
-
-
- $first_test = array_shift($test_list);
- $test_info = simpletest_test_get_by_class($first_test);
- include_once BACKDROP_ROOT . '/' . $test_info['file path'] . '/' . $test_info['file'];
- $first_instance = new $first_test();
- array_unshift($test_list, $first_test);
- $info = simpletest_test_get_by_class(get_class($first_instance));
-
- $batch = array(
- 'title' => t('Running tests'),
- 'operations' => array(
- array('_simpletest_batch_operation', array($test_list, $test_id)),
- ),
- 'finished' => '_simpletest_batch_finished',
- 'progress_message' => '',
- 'css' => array(backdrop_get_path('module', 'simpletest') . '/css/simpletest.css'),
- 'init_message' => t('Processing test @num of @max - %test.', array('%test' => $info['name'], '@num' => '1', '@max' => count($test_list))),
- );
- batch_set($batch);
-
- module_invoke_all('test_group_started');
-
- return $test_id;
- }
-
- * Batch operation callback.
- */
- function _simpletest_batch_operation($test_list_init, $test_id, &$context) {
-
- if (!isset($context['sandbox']['max'])) {
-
- $test_list = $test_list_init;
- $context['sandbox']['max'] = count($test_list);
- $test_results = array('#pass' => 0, '#fail' => 0, '#exception' => 0, '#debug' => 0, '#duration' => 0);
- }
- else {
-
- $test_list = $context['sandbox']['tests'];
- $test_results = $context['sandbox']['test_results'];
- }
- $max = $context['sandbox']['max'];
-
-
- $test_class = array_shift($test_list);
- $test_info = simpletest_test_get_by_class($test_class);
- include_once BACKDROP_ROOT . '/' . $test_info['file path'] . '/' . $test_info['file'];
- $test = new $test_class($test_id);
- $test->run();
- $size = count($test_list);
- $info = simpletest_test_get_by_class(get_class($test));
-
- module_invoke_all('test_finished', $test->results);
-
-
- $test_results[$test_class] = $test->results;
- foreach ($test_results[$test_class] as $key => $value) {
- $test_results[$key] += $value;
- }
- $test_results[$test_class]['#name'] = $info['name'];
- $items = array();
- foreach (element_children($test_results) as $class) {
- array_unshift($items, '<div class="simpletest-' . ($test_results[$class]['#fail'] + $test_results[$class]['#exception'] ? 'fail' : 'pass') . '">' . t('@name: @summary', array('@name' => $test_results[$class]['#name'], '@summary' => _simpletest_format_summary_line($test_results[$class]))) . '</div>');
- }
- $context['message'] = t('Processed test @num of @max - %test.', array('%test' => $info['name'], '@num' => $max - $size, '@max' => $max));
- $context['message'] .= '<div class="simpletest-' . ($test_results['#fail'] + $test_results['#exception'] ? 'fail' : 'pass') . '">Overall results: ' . _simpletest_format_summary_line($test_results) . '</div>';
- $context['message'] .= theme('item_list', array('items' => $items));
-
-
- $context['sandbox']['tests'] = $test_list;
- $context['sandbox']['test_results'] = $test_results;
-
- $context['results']['test_id'] = $test_id;
-
-
- $context['finished'] = 1 - $size / $max;
- }
-
- function _simpletest_batch_finished($success, $results, $operations, $elapsed) {
- if ($success) {
- backdrop_set_message(t('The test run finished in @elapsed.', array('@elapsed' => $elapsed)));
- }
- else {
-
-
- if (!empty($operations)) {
-
- $next_operation = reset($operations);
- $test_id = $next_operation[1][1];
-
-
-
-
- list($last_prefix, $last_test_class) = simpletest_last_test_get($test_id);
- simpletest_log_read($test_id, $last_prefix, $last_test_class);
- }
-
- backdrop_set_message(t('The test run did not successfully finish.'), 'error');
- backdrop_set_message(t('Use the <em>Clean environment</em> button to clean-up temporary files and tables.'), 'warning');
- }
- module_invoke_all('test_group_finished');
- }
-
- * Get information about the last test that ran given a test ID.
- *
- * @param $test_id
- * The test ID to get the last test from.
- * @return
- * Array containing the last database prefix used and the last test class
- * that ran.
- */
- function simpletest_last_test_get($test_id) {
- $last_prefix = db_query_range('SELECT prefix FROM {simpletest_prefix} WHERE test_id = :test_id', 0, 1, array(':test_id' => $test_id))->fetchField();
- $last_test_class = db_query_range('SELECT test_class FROM {simpletest} WHERE test_id = :test_id ORDER BY message_id DESC', 0, 1, array(':test_id' => $test_id))->fetchField();
- return array($last_prefix, $last_test_class);
- }
-
- * Read the error log and report any errors as assertion failures.
- *
- * The errors in the log should only be fatal errors since any other errors
- * will have been recorded by the error handler.
- *
- * @param $test_id
- * The test ID to which the log relates.
- * @param $prefix
- * The database prefix to which the log relates.
- * @param $test_class
- * The test class to which the log relates.
- * @param $during_test
- * Indicates that the current file directory path is a temporary file
- * file directory used during testing.
- * @return
- * Found any entries in log.
- */
- function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALSE) {
- $log = 'public://' . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log';
- $found = FALSE;
- if (file_exists($log)) {
- foreach (file($log) as $line) {
- if (preg_match('/\[.*?\] (.*?): (.*?) in (.*) on line (\d+)/', $line, $match)) {
-
-
- $caller = array(
- 'line' => $match[4],
- 'file' => $match[3],
- );
- BackdropTestCase::insertAssert($test_id, $test_class, FALSE, $match[2], $match[1], $caller);
- }
- else {
-
- BackdropTestCase::insertAssert($test_id, $test_class, FALSE, $line, 'Fatal error');
- }
- $found = TRUE;
- }
- }
- return $found;
- }
-
- * Get a list of all of the tests provided by the system.
- *
- * The list of test classes is loaded from each module's *.tests.info file.
- * Once loaded, the test list is cached and stored in a static variable.
- *
- * @return
- * An array of tests keyed with the groups specified by each owning module's
- * .tests.info file and then keyed by the test class. An example of the array
- * structure is provided below.
- *
- * @code
- * $groups['Block'] => array(
- * 'BlockTestCase' => array(
- * 'name' => 'Block functionality',
- * 'description' => 'Add, edit and delete custom block...',
- * 'group' => 'Block',
- * 'file' => block.test,
- * 'file path' => 'core/modules/block',
- * ),
- * );
- * @endcode
- * @see simpletest_registry_files_alter()
- */
- function simpletest_test_get_all() {
- $groups = &backdrop_static(__FUNCTION__);
-
- if (!$groups) {
-
-
- if ($cache = cache()->get('simpletest')) {
- $groups = $cache->data;
- }
- else {
- $files = backdrop_system_listing('/^' . BACKDROP_PHP_FUNCTION_PATTERN . '\.tests\.info$/', 'modules', 'name', 0);
- $groups = array();
- $check_keys = array('name', 'description', 'group', 'file');
- foreach ($files as $file) {
- $classes = backdrop_parse_info_file($file->uri, TRUE);
-
-
-
-
-
- $error = '';
- $variables = array('@uri' => $file->uri);
- if (empty($classes)) {
- $error = t('File @uri contains no testing classes.');
- }
- else {
-
-
-
-
- foreach ($classes as $class => $info) {
- if (!is_array($info) || empty($info)) {
- $error = t('File @uri contains a definition for @class that is not an array of testing class fields.');
- $variables += array('@class' => $class);
- continue;
- }
- else {
- $missing_keys = array();
- foreach ($check_keys as $key) {
- if (!isset($info[$key])) {
- $missing_keys[] = $key;
- }
- }
- if (!empty($missing_keys)) {
- $keys = implode(', ', $missing_keys);
- $error = format_plural(count($missing_keys), 'File @uri contains a definition for @class that is missing field @keys.', 'File @uri contains a definition for @class that is missing fields @keys.');
- $variables += array('@class' => $class, '@keys' => $keys);
- continue;
- }
- }
-
-
- if (empty($error)) {
- $info['file path'] = dirname($file->uri);
- $test_file_full = BACKDROP_ROOT . '/' . $info['file path'] . '/' . $info['file'];
- if (!file_exists($test_file_full)) {
- $error = t('File @uri specifies a non-existent file @test_file_full. Please verify that the test file exists in the location specified in @uri.');
- $variables += array('@test_file_full' => $test_file_full);
- continue;
- }
- }
- if (empty($error)) {
-
-
- if (!empty($info['dependencies'])) {
- foreach ($info['dependencies'] as $module) {
- if (!backdrop_get_filename('module', $module)) {
- continue 2;
- }
- }
- }
- $groups[$info['group']][$class] = $info;
- }
- }
- }
- if (!empty($error)) {
- if (backdrop_is_cli()) {
-
-
- print format_string($error, $variables) . "\n";
- exit(1);
- }
- else {
-
-
- backdrop_set_message(format_string($error, $variables), 'error');
- watchdog('simpletest', $error, $variables, WATCHDOG_WARNING, l(t('See the Testing Framework documentation.'), 'https://docs.backdropcms.org/documentation/testing-framework'));
- continue;
- }
- }
- }
-
-
- uksort($groups, 'strnatcasecmp');
- foreach ($groups as &$tests) {
- uksort($tests, 'strnatcasecmp');
- }
-
-
- backdrop_alter('simpletest', $groups);
- cache()->set('simpletest', $groups);
- }
- }
- return $groups;
- }
-
- * Get information about a test by its class name.
- */
- function simpletest_test_get_by_class($class) {
- $groups = simpletest_test_get_all();
- foreach ($groups as $classes) {
- if (array_key_exists($class, $classes)) {
- return $classes[$class];
- }
- }
- return FALSE;
- }
-
- * Gets a list of the installation profiles that are available.
- *
- * @return array
- * A list of the available installation profiles.
- */
- function simpletest_get_profiles() {
- include_once BACKDROP_ROOT . '/core/includes/install.core.inc';
- return install_find_profiles();
- }
-
- * Generate test file.
- */
- function simpletest_generate_file($filename, $width, $lines, $type = 'binary-text') {
- $text = '';
- $line = '';
-
- for ($i = 0; $i < $lines; $i++) {
-
- if (empty($line)) {
- for ($j = 0; $j < $width - 1; $j++) {
- switch ($type) {
- case 'text':
- $line .= chr(rand(32, 126));
- break;
- case 'binary':
- $line .= chr(rand(0, 31));
- break;
- case 'binary-text':
- default:
- $line .= rand(0, 1);
- break;
- }
- }
- $line .= "\n";
- }
- $text .= $line;
- }
-
-
- file_put_contents('public://' . $filename . '.txt', $text);
- return $filename;
- }
-
- * Remove all temporary database tables and directories.
- */
- function simpletest_clean_environment() {
- simpletest_clean_database();
- simpletest_clean_temporary_directories();
- simpletest_clean_profile_cache_tables();
- simpletest_clean_profile_cache_folders();
- if (config_get('simpletest.settings', 'simpletest_clear_results')) {
- $count = simpletest_clean_results_table();
- backdrop_set_message(format_plural($count, 'Removed 1 test result.', 'Removed @count test results.'));
- }
- else {
- backdrop_set_message(t('Clear results is disabled and the test results table will not be cleared.'), 'warning');
- }
-
-
- cache()->delete('simpletest');
- }
-
- * Removed prefixed tables from the database that are left over from crashed tests.
- */
- function simpletest_clean_database() {
- $tables = db_find_tables(Database::getConnection()->prefixTables('{simpletest}') . '%');
- $schema = backdrop_get_schema_unprocessed('simpletest');
- $count = 0;
- foreach (array_diff_key($tables, $schema) as $table) {
-
-
- if (preg_match('/simpletest\d+.*/', $table, $matches)) {
- db_drop_table($matches[0]);
- $count++;
- }
- }
-
- if ($count > 0) {
- backdrop_set_message(format_plural($count, 'Removed 1 leftover table.', 'Removed @count leftover tables.'));
- }
- else {
- backdrop_set_message(t('No leftover tables to remove.'));
- }
- }
-
- * Find all leftover temporary directories and remove them.
- */
- function simpletest_clean_temporary_directories() {
- $count = 0;
- if (is_dir('public://simpletest')) {
- $files = scandir('public://simpletest');
- foreach ($files as $file) {
- $path = 'public://simpletest/' . $file;
- if (is_dir($path) && is_numeric($file)) {
- file_unmanaged_delete_recursive($path);
- $count++;
- }
- }
- }
-
-
- $verbose_directory = 'public://simpletest/verbose';
- if (is_dir($verbose_directory)) {
- file_unmanaged_delete_recursive($verbose_directory);
- }
-
- if ($count > 0) {
- backdrop_set_message(format_plural($count, 'Removed 1 temporary directory.', 'Removed @count temporary directories.'));
- }
- else {
- backdrop_set_message(t('No temporary directories to remove.'));
- }
- }
-
- * Clear the test result tables.
- *
- * @param $test_id
- * Test ID to remove results for, or NULL to remove all results.
- * @return
- * The number of results removed.
- */
- function simpletest_clean_results_table($test_id = NULL) {
- if (config_get('simpletest.settings', 'simpletest_clear_results')) {
- if ($test_id) {
- $count = db_query('SELECT COUNT(test_id) FROM {simpletest_prefix} WHERE test_id = :test_id', array(':test_id' => $test_id))->fetchField();
-
- db_delete('simpletest')
- ->condition('test_id', $test_id)
- ->execute();
- db_delete('simpletest_prefix')
- ->condition('test_id', $test_id)
- ->execute();
- }
- else {
- $count = db_query('SELECT COUNT(test_id) FROM {simpletest_prefix}')->fetchField();
-
-
- db_delete('simpletest')->execute();
- db_delete('simpletest_prefix')->execute();
- }
-
- return $count;
- }
- return 0;
- }
-
-
- * Removes cached profile tables from the database.
- */
- function simpletest_clean_profile_cache_tables() {
- $tables = db_find_tables(Database::getConnection()->prefixTables('{simpletest_cache_}') . '%');
- $count = count($tables);
- foreach ($tables as $table) {
- db_drop_table($table);
- }
-
- if ($count > 0) {
- backdrop_set_message(format_plural($count, 'Removed 1 profile cache table.', 'Removed @count profile cache tables.'));
- }
- else {
- backdrop_set_message(t('No profile cache tables to remove.'));
- }
- }
-
- * Removes cached profile folders from the files directory.
- */
- function simpletest_clean_profile_cache_folders() {
- $profiles = simpletest_get_profiles();
-
- $file_public_path = config_get('system.core', 'file_public_path', 'files');
-
- foreach ($profiles as $profile) {
-
- file_unmanaged_delete_recursive($file_public_path . '/simpletest/simpletest_cache_' . $profile->name);
- backdrop_set_message(t('Cleared cache folder for profile !profile.', array('!profile' => $profile->name)));
- }
- }
-
- * Implements hook_mail_alter().
- *
- * Aborts sending of messages with ID 'simpletest_cancel_test'.
- *
- * @see MailTestCase::testCancelMessage()
- */
- function simpletest_mail_alter(&$message) {
- if ($message['id'] == 'simpletest_cancel_test') {
- $message['send'] = FALSE;
- }
- }