Index: includes/batch.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/batch.inc,v retrieving revision 1.20 diff -u -p -r1.20 batch.inc --- includes/batch.inc 24 Jun 2008 21:51:02 -0000 1.20 +++ includes/batch.inc 29 Jul 2008 04:45:15 -0000 @@ -22,11 +22,14 @@ function _batch_page() { // Register database update for end of processing. register_shutdown_function('_batch_shutdown'); - // Add batch-specific css. + // Add batch-specific css and javascript. foreach ($batch['sets'] as $batch_set) { foreach ($batch_set['css'] as $css) { drupal_add_css($css); } + foreach ($batch_set['js'] as $js) { + drupal_add_js($js); + } } $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; @@ -251,8 +254,11 @@ function _batch_process() { '@total' => $total, '@current' => floor($current), '@percentage' => $percentage, - ); - $message = strtr($progress_message, $values) . '
'; + ); + $message = strtr($progress_message, $values); + if (!empty($message)) { + $message .= '
'; + } $message .= $task_message ? $task_message : ' '; return array($percentage, $message); Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.277 diff -u -p -r1.277 form.inc --- includes/form.inc 18 Jul 2008 07:06:24 -0000 1.277 +++ includes/form.inc 26 Jul 2008 04:46:35 -0000 @@ -2421,6 +2421,7 @@ function batch_set($batch_definition) { 'progress_message' => $t('Remaining @remaining of @total.'), 'error_message' => $t('An error has occurred.'), 'css' => array(), + 'js' => array(), ); $batch_set = $init + $batch_definition + $defaults; Index: misc/batch.js =================================================================== RCS file: /cvs/drupal/drupal/misc/batch.js,v retrieving revision 1.4 diff -u -p -r1.4 batch.js --- misc/batch.js 21 Oct 2007 18:59:01 -0000 1.4 +++ misc/batch.js 4 Jul 2008 05:35:13 -0000 @@ -16,6 +16,9 @@ Drupal.behaviors.batch = function (conte // Success: redirect to the summary. var updateCallback = function (progress, status, pb) { + for (behavior in Drupal.batchUpdate) { + Drupal.batchUpdate[behavior](progress, status, pb); + } if (progress == 100) { pb.stopMonitoring(); window.location = uri+'&op=finished'; @@ -35,4 +38,4 @@ Drupal.behaviors.batch = function (conte $(holder).append(progress.element); progress.startMonitoring(uri+'&op=do', 10); }); -}; +}; \ No newline at end of file Index: misc/drupal.js =================================================================== RCS file: /cvs/drupal/drupal/misc/drupal.js,v retrieving revision 1.45 diff -u -p -r1.45 drupal.js --- misc/drupal.js 25 Jun 2008 07:45:03 -0000 1.45 +++ misc/drupal.js 26 Jul 2008 05:47:52 -0000 @@ -1,6 +1,6 @@ // $Id: drupal.js,v 1.45 2008/06/25 07:45:03 dries Exp $ -var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'themes': {}, 'locale': {} }; +var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'themes': {}, 'locale': {}, 'batchUpdate': {} }; /** * Set the variable that indicates if JavaScript behaviors should be applied Index: modules/contact/contact.test =================================================================== RCS file: /cvs/drupal/drupal/modules/contact/contact.test,v retrieving revision 1.7 diff -u -p -r1.7 contact.test --- modules/contact/contact.test 22 Jul 2008 20:08:44 -0000 1.7 +++ modules/contact/contact.test 28 Jul 2008 22:27:19 -0000 @@ -191,7 +191,7 @@ class ContactSitewideTestCase extends Dr // Get role id (rid) for specified role. $rid = db_result(db_query("SELECT rid FROM {role} WHERE name = '%s'", array($role))); if ($rid === FALSE) { - $this->fail(t(' [permission] Role "' . $role . '" not found.')); + $this->fail(t('Role "@role" not found.', array('@role' => $role)), t('Permission')); } // Create edit array from permission. @@ -201,7 +201,7 @@ class ContactSitewideTestCase extends Dr } $this->drupalPost('admin/user/permissions', $edit, t('Save permissions')); - $this->assertText(t('The changes have been saved.'), t(' [permission] Saved changes.')); + $this->assertText(t('The changes have been saved.'), t('Saved changes.'), t('Permission')); } } Index: modules/filter/filter.test =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.test,v retrieving revision 1.5 diff -u -p -r1.5 filter.test --- modules/filter/filter.test 6 Jun 2008 10:36:43 -0000 1.5 +++ modules/filter/filter.test 27 Jul 2008 05:48:43 -0000 @@ -292,15 +292,15 @@ END; $this->assertRaw('href="http://www.test17.com"', t('Parse multiple www-strings inside same paragraph.')); $this->assertRaw('href="http://www.test18.com"', t('Parse multiple www-strings inside same paragraph.')); $this->assertRaw('href="http://www.test19.com"', t('Parse multiple www-strings inside same paragraph.')); - $this->assertRaw('href="http://www.test20.com"', t('Parse multiple www-strings inside same paragraph limited with
.')); - $this->assertRaw('href="http://www.test21.com"', t('Parse multiple www-strings inside same paragraph limited with
.')); - $this->assertRaw('href="mailto:person@test22.com"', t('Parse email string with multiple www-strings inside same paragraph limited with
.')); - $this->assertRaw('href="http://www.test23.com"', t('Parse multiple www-strings inside same paragraph limited with
.')); - $this->assertRaw('href="http://www.test24.com"', t('Parse multiple www-strings inside same paragraph limited with
and .')); - $this->assertRaw('href="http://www.test25.com"', t('Parse multiple www-strings inside same paragraph limited with
and .')); - $this->assertRaw('href="http://www.test26.com"', t('Parse multiple www-strings inside same paragraph limited with
and .')); - $this->assertRaw('href="mailto:person@test27.com"', t('Parse email string with multiple www-strings inside same paragraph limited with
and .')); - $this->assertRaw('href="http://www.test28.com"', t('Parse multiple www-strings inside same paragraph limited with
and .')); + $this->assertRaw('href="http://www.test20.com"', t('Parse multiple www-strings inside same paragraph limited with <br>.')); + $this->assertRaw('href="http://www.test21.com"', t('Parse multiple www-strings inside same paragraph limited with <br>.')); + $this->assertRaw('href="mailto:person@test22.com"', t('Parse email string with multiple www-strings inside same paragraph limited with <br>.')); + $this->assertRaw('href="http://www.test23.com"', t('Parse multiple www-strings inside same paragraph limited with <br>.')); + $this->assertRaw('href="http://www.test24.com"', t('Parse multiple www-strings inside same paragraph limited with <br> and <img>.')); + $this->assertRaw('href="http://www.test25.com"', t('Parse multiple www-strings inside same paragraph limited with <br> and <img>.')); + $this->assertRaw('href="http://www.test26.com"', t('Parse multiple www-strings inside same paragraph limited with <br> and <img>.')); + $this->assertRaw('href="mailto:person@test27.com"', t('Parse email string with multiple www-strings inside same paragraph limited with <br> and <img>.')); + $this->assertRaw('href="http://www.test28.com"', t('Parse multiple www-strings inside same paragraph limited with <br> and <img>.')); $this->assertNoRaw('href="http://www.test29.com"', t('Do not parse URL inside a script element (part of javascript code).')); $this->assertNoRaw('href="http://www.test30.com"', t('Do not parse URL inside an a element.')); $this->assertRaw('href="http://www.test31.com"', t('Parse URL inside strong tag.')); Index: modules/simpletest/drupal_web_test_case.php =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v retrieving revision 1.27 diff -u -p -r1.27 drupal_web_test_case.php --- modules/simpletest/drupal_web_test_case.php 18 Jul 2008 07:30:34 -0000 1.27 +++ modules/simpletest/drupal_web_test_case.php 27 Jul 2008 04:01:49 -0000 @@ -62,6 +62,7 @@ class DrupalWebTestCase { if (substr($function['function'], 0, 6) != 'assert' && $function['function'] != 'pass' && $function['function'] != 'fail') { break; } + $previous_function = $function; } } else { @@ -75,8 +76,8 @@ class DrupalWebTestCase { 'message' => $message, 'group' => $group, 'function' => $function['function'], - 'line' => $function['line'], - 'file' => $function['file'], + 'line' => $previous_function['line'], + 'file' => $previous_function['file'], ); $db_prefix = $current_db_prefix; return $status; @@ -304,6 +305,7 @@ class DrupalWebTestCase { 'file' => $file, )); } + drupal_set_message(var_export($severity, TRUE)); return TRUE; } @@ -710,7 +712,6 @@ class DrupalWebTestCase { // Close the CURL handler. $this->curlClose(); - restore_error_handler(); } } @@ -785,7 +786,7 @@ class DrupalWebTestCase { // them. @$htmlDom = DOMDocument::loadHTML($this->_content); if ($htmlDom) { - $this->assertTrue(TRUE, t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser')); + $this->pass(t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser')); // It's much easier to work with simplexml than DOM, luckily enough // we can just simply import our DOM tree. $this->elements = simplexml_import_dom($htmlDom); @@ -1183,7 +1184,7 @@ class DrupalWebTestCase { * TRUE on pass, FALSE on fail. */ function assertText($text, $message = '', $group = 'Other') { - return $this->assertTextHelper($text, $message, $group = 'Other', FALSE); + return $this->assertTextHelper($text, $message, $group, FALSE); } /** Index: modules/simpletest/simpletest.css =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.css,v retrieving revision 1.2 diff -u -p -r1.2 simpletest.css --- modules/simpletest/simpletest.css 24 Jun 2008 21:51:02 -0000 1.2 +++ modules/simpletest/simpletest.css 30 Jul 2008 06:24:59 -0000 @@ -1,11 +1,9 @@ /* $Id: simpletest.css,v 1.2 2008/06/24 21:51:02 dries Exp $ */ -/* Addon for the simpletest module */ -#simpletest { -} /* Test Table */ th.simpletest_run { width: 50px; + padding-right: 0.5em; } th.simpletest_test { width: 160px; @@ -17,46 +15,55 @@ td.simpletest-select-all { } table#simpletest-form-table tr td { - background-color: white !important; + background-color: white; } table#simpletest-form-table tr.simpletest-group td { - background-color: #EDF5FA !important; + background-color: #EDF5FA; } -div.simpletest-pass { - color: #33a333; +table#simpletest-form-table thead .form-item { + margin-bottom:0pt; + margin-top:0pt; + white-space:nowrap; } -div.simpletest-fail { - color: #a30000; +div.simpletest-image { + display: inline; } -tr.simpletest-pass.odd { - background: #b6ffb6; +div.simpletest-image, label.simpletest-group-label { + cursor: pointer; } -tr.simpletest-pass.even { - background: #9bff9b; +.simpletest-pass { + color: #316d31; } -tr.simpletest-fail.odd { - background: #ffc9c9; +.simpletest-fail { + color: #981010; } -tr.simpletest-fail.even { - background: #ffacac; +.simpletest-exception { + color: #bda400; } -tr.simpletest-exception.odd { - background: #f4ea71; +div.message > div.item-list { + font-weight: normal; } -tr.simpletest-exception.even { - background: #f5e742; +li.simpletest-important { + font-weight: bold; } -div.simpletest-image { - display: inline; +ul.simpletest-results li div.item-list ul { + padding-left: 20px; +} + +ul.simpletest-results li span.simpletest-overview { cursor: pointer; } + +ul.simpletest-results li span.simpletest-overview:hover { + text-decoration: underline; +} \ No newline at end of file Index: modules/simpletest/simpletest.js =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.js,v retrieving revision 1.2 diff -u -p -r1.2 simpletest.js --- modules/simpletest/simpletest.js 23 Apr 2008 18:50:49 -0000 1.2 +++ modules/simpletest/simpletest.js 29 Jul 2008 04:33:26 -0000 @@ -1,12 +1,15 @@ // $Id: simpletest.js,v 1.2 2008/04/23 18:50:49 dries Exp $ +/** + * Add the cool table collapsing on the testing overview page. + */ Drupal.behaviors.simpleTestMenuCollapse = function() { // Adds expand-collapse functionality. $('div.simpletest-image').click(function() { // Toggle all of the trs. if (!Drupal.settings.simpleTest[$(this).attr('id')].clickActive) { Drupal.settings.simpleTest[$(this).attr('id')].clickActive = true; - var trs = $(this).parents('tbody').children().filter('.'+ Drupal.settings.simpleTest[$(this).attr('id')].testClass), trs_formatted = [], direction = Drupal.settings.simpleTest[$(this).attr('id')].imageDirection, self = $(this); + var trs = $(this).parents('tbody').children().filter('.' + Drupal.settings.simpleTest[$(this).attr('id')].testClass), trs_formatted = [], direction = Drupal.settings.simpleTest[$(this).attr('id')].imageDirection, self = $(this); for (var i = 0; i < trs.length; i++) { trs_formatted.push(trs[i]); } @@ -21,12 +24,21 @@ Drupal.behaviors.simpleTestMenuCollapse Drupal.settings.simpleTest[self.attr('id')].clickActive = false; } } - toggleTrs(trs_formatted, (direction? 'pop' : 'shift'), (direction? 'fadeOut' : 'fadeIn')); + toggleTrs(trs_formatted, (direction ? 'pop' : 'shift'), (direction ? 'fadeOut' : 'fadeIn')); Drupal.settings.simpleTest[$(this).attr('id')].imageDirection = !direction; $(this).html(Drupal.settings.simpleTest.images[(direction? 0 : 1)]); } }); + $('label.simpletest-group-label').click(function() { + $(this).siblings('div.simpletest-image').click(); + return false; + }); } + +/** + * Select/deselect all the inner checkboxes when the outer checkboxes are + * selected/deselected. + */ Drupal.behaviors.simpleTestSelectAll = function() { $('td.simpletest-select-all').each(function() { var checkboxes = Drupal.settings.simpleTest['simpletest-test-group-'+ $(this).attr('id')].testNames, totalCheckboxes = 0, @@ -35,11 +47,11 @@ Drupal.behaviors.simpleTestSelectAll = f for (var i = 0; i < checkboxes.length; i++) { $('#'+ checkboxes[i]).attr('checked', checked); } - self.data('simpletest-checked-tests', (checked? checkboxes.length : 0)); + self.data('simpletest-checked-tests', (checked ? checkboxes.length : 0)); }).data('simpletest-checked-tests', 0); var self = $(this); for (var i = 0; i < checkboxes.length; i++) { - if ($('#'+ checkboxes[i]).change(function() { + if ($('#' + checkboxes[i]).change(function() { if (checkbox.attr('checked') == 'checked') { checkbox.attr('checked', ''); } @@ -49,7 +61,7 @@ Drupal.behaviors.simpleTestSelectAll = f checkbox.attr('checked', 'checked'); } else { - checkbox.attr('checked', ''); + checkbox.removeAttr('checked'); } }).attr('checked') == 'checked') { totalCheckboxes++; @@ -60,4 +72,69 @@ Drupal.behaviors.simpleTestSelectAll = f } $(this).append(checkbox); }); + $('input.simpletest-master:checkbox').click(function() { + if ($(this).is(':checked')) { + $('#simpletest-form-table input:checkbox,input.simpletest-master:checkbox').attr('checked', 'checked'); + } + else { + $('#simpletest-form-table input:checkbox,input.simpletest-master:checkbox').removeAttr('checked'); + } + $('#simpletest-form-table input:checkbox:not(.simpletest-master)').change(function() { + if ($('#simpletest-form-table input:checkbox:not(:checked):not(.simpletest-master)').length) { + $('input.simpletest-master:checkbox').removeAttr('checked'); + } + else if ($('#simpletest-form-table input:checkbox:not(.simpletest-master)').length == $('#simpletest-form-table input:checkbox:checked:not(.simpletest-master)').length) { + $('input.simpletest-master:checkbox').attr('checked', 'checked'); + } + }); + }); +}; + +/** + * Filter the results based on their outcome. + */ +Drupal.behaviors.simpleTestFilterForm = function() { + var updateResults = function(type, checked) { + $('li.simpletest-' + type + ':not(.simpletest-container)')[checked ? 'show' : 'hide'](); + $('li.simpletest-container:not(:has(li.simpletest-container))').each(function() { + var children = $(this).find('li:visible:not(.simpletest-container)').is('.simpletest-pass,.simpletest-fail,.simpletest-exception'); + $(this)[children ? 'show' : 'hide'](); + }); + } + $('#edit-outcome-pass').change(function() { + updateResults('pass', $(this).is(':checked')); + }); + $('#edit-outcome-fail').change(function() { + updateResults('fail', $(this).is(':checked')); + }); + $('#edit-outcome-exception').change(function() { + updateResults('exception', $(this).is(':checked')); + }); + updateResults('pass', $('#edit-outcome-pass').is(':checked')); + updateResults('fail', $('#edit-outcome-fail').is(':checked')); + updateResults('exception', $('#edit-outcome-exception').is(':checked')); +}; + +/** + * The results should collapse when you click on them, of course. + */ +Drupal.behaviors.simpleTestResultsCollapse = function() { + $('li > span.simpletest-overview').click(function() { + $(this).siblings().children('ul').toggle().toggleClass('simpletest-hidden').parent().parent().toggleClass('collapsed').toggleClass('expanded'); + }); + $('li.collapsed > span.simpletest-overview').each(function() { + $(this).siblings().children('ul').hide().addClass('simpletest-hidden'); + }); +}; + +/** + * Batch update function - re-attaches javascript behaviors to the new elements + * now injected into the HTML. + */ +Drupal.batchUpdate.simpleTestResultsCollapse = function() { + $('li > span.simpletest-overview').click(function() { + $(this).siblings().children('ul').toggle().toggleClass('simpletest-hidden').parent().parent().toggleClass('collapsed').toggleClass('expanded'); + }).each(function() { + $(this).siblings().children('ul').hide().addClass('simpletest-hidden').parent().parent().removeClass('expanded').addClass('collapsed'); + }); }; \ No newline at end of file Index: modules/simpletest/simpletest.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.module,v retrieving revision 1.7 diff -u -p -r1.7 simpletest.module --- modules/simpletest/simpletest.module 16 Jul 2008 21:59:27 -0000 1.7 +++ modules/simpletest/simpletest.module 30 Jul 2008 06:46:18 -0000 @@ -50,6 +50,9 @@ function simpletest_theme() { 'simpletest_result_summary' => array( 'arguments' => array('form' => NULL) ), + 'simpletest_results' => array( + 'arguments' => array('results' => NULL, 'assertions' => NULL), + ), ); } @@ -62,85 +65,39 @@ function simpletest_test_form() { $uncategorized_tests = simpletest_get_all_tests(); $tests = simpletest_categorize_tests($uncategorized_tests); if (isset($_SESSION['test_id'])) { + $form['#test_id'] = $_SESSION['test_id']; $results = db_query("SELECT * FROM {simpletest} WHERE test_id = %d ORDER BY test_class, message_id", $_SESSION['test_id']); unset($_SESSION['test_id']); - $summary = array( - '#theme' => 'simpletest_result_summary', - '#pass' => 0, - '#fail' => 0, - '#exception' => 0, - '#weight' => -10, - ); - $form['summary'] = $summary; - $form['results'] = array(); - $group_summary = array(); - $map = array( - 'pass' => theme('image', 'misc/watchdog-ok.png'), - 'fail' => theme('image', 'misc/watchdog-error.png'), - 'exception' => theme('image', 'misc/watchdog-warning.png'), - ); $header = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), array('colspan' => 2, 'data' => t('Status'))); + $test_results = array(); + $test_assertions = array(); while ($result = db_fetch_object($results)) { - $class = $result->test_class; - $info = $uncategorized_tests[$class]->getInfo(); - $group = $info['group']; - if (!isset($group_summary[$group])) { - $group_summary[$group] = $summary; - } - $element = &$form['results'][$group][$class]; - if (!isset($element)) { - $element['summary'] = $summary; - } - $status = $result->status; - // This reporter can only handle pass, fail and exception. - if (isset($map[$status])) { - $element['#title'] = $info['name']; - $status_index = '#'. $status; - $form['summary'][$status_index]++; - $group_summary[$group][$status_index]++; - $element['summary'][$status_index]++; - $element['result_table']['#rows'][] = array( - 'data' => array( - $result->message, - $result->message_group, - basename($result->file), - $result->line, - $result->caller, - $map[$status], - ), - 'class' => "simpletest-$status", - ); - } - unset($element); - } - $all_ok = TRUE; - foreach ($form['results'] as $group => &$elements) { - $group_ok = TRUE; - foreach ($elements as $class => &$element) { - $info = $uncategorized_tests[$class]->getInfo(); - $ok = $element['summary']['#fail'] + $element['summary']['#exception'] == 0; - $element += array( - '#type' => 'fieldset', - '#collapsible' => TRUE, - '#collapsed' => $ok, - '#description' => $info['description'], - ); - $element['result_table']['#markup'] = theme('table', $header, $element['result_table']['#rows']); - $element['summary']['#ok'] = $ok; - $group_ok = $group_ok && $ok; + if (empty($test_results[$result->test_class])) { + $class = $result->test_class; + $test = new $class(); + $info = $test->getInfo(); + $test_results[$class] = array('#fail' => 0, '#pass' => 0, '#exception' => 0); + foreach ($info as $key => $value) { + $test_results[$class]['#' . $key] = $value; + } } - $elements += array( - '#type' => 'fieldset', - '#title' => $group, - '#collapsible' => TRUE, - '#collapsed' => $group_ok, - 'summary' => $group_summary[$group], - ); - $elements['summary']['#ok'] = $group_ok; - $all_ok = $group_ok && $all_ok; - } - $form['summary']['#ok'] = $all_ok; - } + $test_results[$result->test_class]['#' . $result->status]++; + $assertion = (array)$result; + $assertion['group'] = $assertion['message_group']; + unset($assertion['message_group']); + $assertion['function'] = $assertion['caller']; + unset($assertion['caller']); + $test_assertions[$result->test_class][] = $assertion; + $test_results[$class]['#ok'] = !($test_results[$class]['#fail'] + $test_results[$class]['#exception']); + } + $form['#results'] = $test_results; + $form['#assertions'] = $test_assertions; + } + $form['run_all_top'] = array( + '#type' => 'checkbox', + '#default_value' => 0, + '#attributes' => array('class' => 'simpletest-master'), + ); foreach ($tests as $group_name => $test_group) { foreach ($test_group as $test) { $test_info = $test->getInfo(); @@ -154,32 +111,15 @@ function simpletest_test_form() { } } - $form['run'] = array( - '#type' => 'fieldset', - '#collapsible' => FALSE, - '#collapsed' => FALSE, - '#title' => t('Run tests'), - ); - $form['run']['running_options'] = array( - '#type' => 'radios', - '#default_value' => 'selected_tests', - '#options' => array( - 'all_tests' => t('Run all tests (WARNING, this may take a long time)'), - 'selected_tests' => t('Run selected tests'), - ), + $form['submit'] = array( + '#prefix' => '
', + '#suffix' => '
', ); - $form['run']['op'] = array( + $form['submit']['run'] = array( '#type' => 'submit', '#value' => t('Run tests'), ); - $form['reset'] = array( - '#type' => 'fieldset', - '#collapsible' => FALSE, - '#collapsed' => FALSE, - '#title' => t('Clean test environment'), - '#description' => t('Remove tables with the prefix "simpletest" and temporary directories that are left over from tests that crashed.') - ); - $form['reset']['op'] = array( + $form['submit']['reset'] = array( '#type' => 'submit', '#value' => t('Clean environment'), '#submit' => array('simpletest_clean_environment') @@ -195,11 +135,13 @@ function simpletest_test_form() { function theme_simpletest_test_form($form) { drupal_add_css(drupal_get_path('module', 'simpletest') .'/simpletest.css', 'module'); drupal_add_js(drupal_get_path('module', 'simpletest') .'/simpletest.js', 'module'); + $header = array( - array('data' => t('Run'), 'class' => 'simpletest_run checkbox'), + array('data' => drupal_render($form['run_all_top']), 'class' => 'simpletest_run checkbox'), array('data' => t('Test'), 'class' => 'simpletest_test'), array('data' => t('Description'), 'class' => 'simpletest_description'), ); + unset($form['run_all_top']); $js = array( 'images' => array( theme('image', 'misc/menu-collapsed.png', 'Expand', 'Expand'), @@ -240,12 +182,21 @@ function theme_simpletest_test_form($for $js['simpletest-test-group-'. $test_class] = $current_js; } unset($form['tests']); + unset($form['run_all_bottom']); drupal_add_js(array('simpleTest' => $js), 'setting'); // Output test groups: $output = ''; - if (isset($form['results'])) { - $output .= drupal_render($form['summary']); - $output .= drupal_render($form['results']); + if (isset($form['#results'])) { + $output .= '

' . t('The assertions listed in bold text are those made by the test itself, rather than the implicit framework tests.') .'

'; + $output .= drupal_get_form('simpletest_filter_form'); + $output .= theme('simpletest_results', $form['#results'], $form['#assertions']); + $output .= drupal_get_form('simpletest_rerun_form', $form['#test_id']); + return $output; + } + else { + // This will forward us to the batch api if necessary. + drupal_get_form('simpletest_rerun_form', NULL); + $output .= '

' . t('Select the "Clean environment" button below to remove any database tables or files that might be left over from interrupted tests. Otherwise, select the tests you would like to run, and click "Run tests".') . '

'; } if (count($rows)) { $output .= theme('table', $header, $rows, array('id' => 'simpletest-form-table')); @@ -256,12 +207,8 @@ function theme_simpletest_test_form($for return $output; } -function theme_simpletest_result_summary($form, $text = NULL) { - return '
' . _simpletest_format_summary_line($form) . '
'; -} - function _simpletest_format_summary_line($summary) { - return t('@pass, @fail, @exception', array( + return t('@pass, @fail, and @exception', 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'), @@ -275,10 +222,9 @@ function simpletest_test_form_submit($fo $output = ''; $batch_mode = !preg_match("/^simpletest\d+$/", $_SERVER['HTTP_USER_AGENT']); $tests_list = array(); - $run_all_tests = $form_state['values']['running_options'] == 'all_tests'; simpletest_get_all_tests(); foreach ($form_state['values'] as $class_name => $value) { - if (class_exists($class_name) && ($value === 1 || $run_all_tests)) { + if (class_exists($class_name) && $value === 1) { $tests_list[] = $class_name; } } @@ -307,6 +253,10 @@ function simpletest_run_tests($test_list $test_id = db_last_insert_id('simpletest_test_id', 'test_id'); if ($batch_mode) { + $first_test = array_shift($test_list); + $first_instance = new $first_test(); + array_unshift($test_list, $first_test); + $info = $first_instance->getInfo(); $batch = array( 'title' => t('Running SimpleTests'), 'operations' => array( @@ -314,9 +264,10 @@ function simpletest_run_tests($test_list ), 'finished' => '_simpletest_batch_finished', 'redirect' => 'admin/build/testing', - 'progress_message' => t('Processing tests.'), + 'progress_message' => '', 'css' => array(drupal_get_path('module', 'simpletest') .'/simpletest.css'), - 'init_message' => t('SimpleTest is initializing...') . ' ' . format_plural(count($test_list), "one test case will run.", "@count test cases will run."), + 'js' => array(drupal_get_path('module', 'simpletest') .'/simpletest.js'), + 'init_message' => t('Processing test @num of @max - %test.', array('%test' => $info['name'], '@num' => '1', '@max' => count($test_list))), ); batch_set($batch); } @@ -348,6 +299,7 @@ function _simpletest_batch_operation($te // Nth iteration: get the current values where we last stored them. $test_list = $context['sandbox']['tests']; $test_results = $context['sandbox']['test_results']; + $test_assertions = $context['sandbox']['test_assertions']; } $max = $context['sandbox']['max']; @@ -363,19 +315,27 @@ function _simpletest_batch_operation($te 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) { - $items[] = '
' . t('@name: @summary', array('@name' => $test_results[$class]['#name'], '@summary' => _simpletest_format_summary_line($test_results[$class]))) . '
'; + $test_assertions[$test_class] = $test->_assertions; + foreach ($info as $key => $value) { + $test_results[$test_class]['#' . $key] = $value; + } + $test_results[$test_class]['#ok'] = !($test_results['#fail'] + $test_results['#exception']); + if (!empty($test_list)) { + $current_test_class = array_shift($test_list); + $instance = new $current_test_class(); + $current_info = $instance->getInfo(); + $context['message'] = t('Processing test @num of @max - %test.', array('%test' => $current_info['name'], '@num' => $max - count($test_list), '@max' => $max)); + array_unshift($test_list, $current_test_class); + } + else { + $context['message'] = t('Finished processing all tests.'); } - $message = t('Processed test @num of @max - %test.', array('%test' => $info['name'], '@num' => $max - $size, '@max' => $max)); - $message .= theme('item_list', $items); - $context['message'] = $message; - // TODO: Do we want a summary of all? + $context['message'] .= theme_simpletest_results($test_results, $test_assertions); // Save working values for the next iteration. $context['sandbox']['tests'] = $test_list; $context['sandbox']['test_results'] = $test_results; + $context['sandbox']['test_assertions'] = $test_assertions; // The test_id is the only thing we need to save for the report page. $context['results']['test_id'] = $test_id; @@ -384,7 +344,9 @@ function _simpletest_batch_operation($te } function _simpletest_batch_finished($success, $results, $operations) { - $_SESSION['test_id'] = $results['test_id']; + if (isset($results['test_id'])) { + $_SESSION['test_id'] = $results['test_id']; + } if ($success) { drupal_set_message(t('The tests have finished running.')); } @@ -461,6 +423,87 @@ function simpletest_categorize_tests($te } /** + * Theme simpletest results. + * + * @param $results + * A nested array of simpletest results. + * @param $assertions + * An array of assertions for each test. + * @return + * A nested list of simpletest items. + */ +function theme_simpletest_results($results, $assertions) { + $items = array(); + $group_data = array(); + $totals = array( + '#fail' => 0, + '#pass' => 0, + '#exception' => 0, + ); + foreach (element_children($results) as $class) { + $totals['#fail'] += $results[$class]['#fail']; + $totals['#pass'] += $results[$class]['#pass']; + $totals['#exception'] += $results[$class]['#exception']; + $item = array(); + foreach ($results[$class] as $key => $value) { + if ($key == '#ok') { + $group_data[$results[$class]['#group']][$key] = isset($group_data[$results[$class]['#group']][$key]) ? $group_data[$results[$class]['#group']][$key] && $value : $value; + } + else { + $group_data[$results[$class]['#group']][$key] = isset($group_data[$results[$class]['#group']][$key]) ? $group_data[$results[$class]['#group']][$key] + $value : $value; + } + } + $item['class'] = 'simpletest-container ' . ($results[$class]['#fail'] ? 'simpletest-fail expanded' : ($results[$class]['#exception'] ? 'simpletest-exception expanded' : 'simpletest-pass collapsed')); + $item['data'] = '' . t('@name: @summary', array('@name' => $results[$class]['#name'], '@summary' => _simpletest_format_summary_line($results[$class]))) . ''; + $assertion_items = array(); + foreach ($assertions[$class] as $assertion) { + // The idea of importancy rests upon the fact that the testing framework + // makes many assertions in its normal process of making GET and POST + // requests, as well as many other normal procedures. We will treat + // assertions actually initiated by the test itself as far more likely to + // be important, and thus highlight it in bold text. + $important = FALSE; + if ($assertion['group'] == 'Other') { + $assertion['group'] = $results[$class]['#name']; + $important = TRUE; + } + $assertion_items[] = array( + 'data' => '' . $assertion['message'] . ' ' . ($assertion['status'] == 'pass' ? t('(pass)') : ($assertion['status'] == 'fail' ? t('(FAIL)') : t('(EXCEPTION)'))), + 'class' => 'collapsed ' . ($assertion['status'] == 'pass' ? 'simpletest-pass' : ($assertion['status'] == 'fail' ? 'simpletest-fail' : 'simpletest-exception')) . ($important ? ' simpletest-important' : ''), + 'children' => array( + array( + 'data' => t('Group: @group', array('@group' => $assertion['group'])), + 'class' => 'leaf ' . ($assertion['status'] == 'pass' ? 'simpletest-pass' : ($assertion['status'] == 'fail' ? 'simpletest-fail' : 'simpletest-exception')) . ($important ? ' simpletest-important' : ''), + ), + array( + 'data' => t('File: @file', array('@file' => basename($assertion['file']))), + 'class' => 'leaf ' . ($assertion['status'] == 'pass' ? 'simpletest-pass' : ($assertion['status'] == 'fail' ? 'simpletest-fail' : 'simpletest-exception')) . ($important ? ' simpletest-important' : ''), + ), + array( + 'data' => t('Function: @function', array('@function' => $assertion['function'])), + 'class' => 'leaf ' . ($assertion['status'] == 'pass' ? 'simpletest-pass' : ($assertion['status'] == 'fail' ? 'simpletest-fail' : 'simpletest-exception')) . ($important ? ' simpletest-important' : ''), + ), + array( + 'data' => t('Line: @line', array('@line' => $assertion['line'])), + 'class' => 'leaf ' . ($assertion['status'] == 'pass' ? 'simpletest-pass' : ($assertion['status'] == 'fail' ? 'simpletest-fail' : 'simpletest-exception')) . ($important ? ' simpletest-important' : ''), + ), + ), + ); + } + $item['children'] = $assertion_items; + $items[$results[$class]['#group']]['children'][] = $item; + $items[$results[$class]['#group']]['data'] = '' . t('@name: @summary', array('@name' => $results[$class]['#group'], '@summary' => _simpletest_format_summary_line($group_data[$results[$class]['#group']]))) . ''; + $items[$results[$class]['#group']]['class'] = 'simpletest-container ' . ($group_data[$results[$class]['#group']]['#fail'] ? 'simpletest-fail expanded' : ($group_data[$results[$class]['#group']]['#exception'] ? 'simpletest-exception expanded' : 'simpletest-pass collapsed')); + } + $output = ''; + $output .= '

'; + $output .= t('Overall results: @summary', array('@summary' => _simpletest_format_summary_line($totals))); + $output .= '

'; + $output .= theme('item_list', $items, NULL, 'ul', array('class' => 'simpletest-results')); + return $output; +} + +/** * Remove all temporary database tables and directories. */ function simpletest_clean_environment() { @@ -537,6 +580,64 @@ function simpletest_clean_temporary_dire } /** + * FAPI callback for the filtering form. + */ +function simpletest_filter_form() { + $form = array(); + $form['outcome'] = array( + '#type' => 'checkboxes', + '#title' => t('Filter by outcome'), + '#options' => array(), + '#attributes' => array('class' => 'container-inline'), + '#default_value' => array(), + ); + $categories = array('pass' => t('Passes'), 'fail' => t('Fails'), 'exception' => t('Exceptions')); + foreach ($categories as $outcome => $readable) { + $form['outcome'][$outcome] = array( + '#type' => 'checkbox', + '#title' => $readable, + '#attributes' => array('class' => 'simpletest-filter'), + '#default_value' => TRUE, + ); + } + return $form; +} + +/** + * FAPI callback for the re-run form. + */ +function simpletest_rerun_form($form_state, $test_id) { + $form = array(); + $form['test_id'] = array( + '#type' => 'hidden', + '#default_value' => $test_id, + ); + $form['actions'] = array( + '#prefix' => '
', + '#suffix' => '
', + ); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Re-run same tests'), + ); + $form['actions']['cancel'] = array( + '#markup' => l(t('Return to testing overview page'), 'admin/build/testing'), + ); + return $form; +} + +/** + * FAPI submit handler. + */ +function simpletest_rerun_form_submit($form, &$form_state) { + $result = db_query('SELECT DISTINCT(test_class) FROM {simpletest} WHERE test_id = %d', $form_state['values']['test_id']); + while ($class = db_result($result)) { + $form_state['values'][$class] = 1; + } + simpletest_test_form_submit($form, $form_state); +} + +/** * Remove all files from specified firectory and then remove directory. * * @param string $path Directory path. Index: modules/simpletest/simpletest.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.test,v retrieving revision 1.4 diff -u -p -r1.4 simpletest.test --- modules/simpletest/simpletest.test 18 Jul 2008 07:24:29 -0000 1.4 +++ modules/simpletest/simpletest.test 27 Jul 2008 04:35:40 -0000 @@ -94,11 +94,13 @@ class SimpleTestTestCase extends DrupalW * Confirm that the stub test produced the desired results. */ function confirmStubTestResults() { - $this->assertAssertion($this->pass, 'Other', 'Pass'); - $this->assertAssertion($this->fail, 'Other', 'Fail'); + $info = $this->getInfo(); + $name = $info['name']; + $this->assertAssertion($this->pass, $name); + $this->assertAssertion($this->fail, $name); - $this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role', 'Pass'); - $this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role', 'Fail'); + $this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role'); + $this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role'); } /** @@ -110,18 +112,17 @@ class SimpleTestTestCase extends DrupalW * @param string $status Assertion status. * @return Assertion result. */ - function assertAssertion($message, $type, $status) { + function assertAssertion($message, $type) { $message = trim(strip_tags($message)); $found = FALSE; - foreach ($this->results['assertions'] as $assertion) { + foreach ($this->results as $assertion) { if ($assertion['message'] == $message && - $assertion['type'] == $type && - $assertion['status'] == $status) { + $assertion['type'] == $type) { $found = TRUE; break; } } - return $this->assertTrue($found, t('Found assertion {"@message", "@type", "@status"}.', array('@message' => $message, '@type' => $type, '@status' => $status))); + return $this->assertTrue($found, t('Found assertion {"@message", "@type"}.', array('@message' => $message, '@type' => $type))); } /** @@ -131,20 +132,13 @@ class SimpleTestTestCase extends DrupalW $results = array(); if ($this->parse()) { - if ($fieldset = $this->getResultFieldSet()) { - // Code assumes this is the only test in group. - $results['summary'] = $this->asText($fieldset->div); - $results['name'] = $this->asText($fieldset->fieldset->legend); - - $results['assertions'] = array(); - $tbody = $fieldset->fieldset->table->tbody; - foreach ($tbody->tr as $row) { + if ($list = $this->getResultList()) { + $results = array(); + foreach ($list->li as $li) { $assertion = array(); - $assertion['message'] = $this->asText($row->td[0]); - $assertion['type'] = $this->asText($row->td[1]); - $ok_url = (url('misc/watchdog-ok.png') == 'misc/watchdog-ok.png') ? 'misc/watchdog-ok.png' : (base_path() . 'misc/watchdog-ok.png'); - $assertion['status'] = ($row->td[5]->img['src'] == $ok_url) ? 'Pass' : 'Fail'; - $results['assertions'][] = $assertion; + $assertion['message'] = $this->asText($li->span); + $assertion['type'] = str_replace('Group: ', '', $this->asText($li->div->ul->li[0])); + $results[] = $assertion; } } } @@ -156,12 +150,11 @@ class SimpleTestTestCase extends DrupalW * * @return fieldset containing the results for group this test is in. */ - function getResultFieldSet() { - $fieldsets = $this->elements->xpath('//fieldset'); - $info = $this->getInfo(); - foreach ($fieldsets as $fieldset) { - if ($fieldset->legend == $info['group']) { - return $fieldset; + function getResultList() { + $lis = $this->elements->xpath('//li'); + foreach ($lis as $li) { + if (strpos($this->asText($li->span), t('SimpleTest: ')) === 0) { + return $li->div->ul->li->div->ul; } } return FALSE;