test.drush.inc

  1. 6.x commands/core/test.drush.inc
  2. 4.x commands/core/test.drush.inc
  3. 5.x commands/core/test.drush.inc

Simpletest module drush integration.

Functions

Namesort descending Description
drush_test_clean
drush_test_get_all_tests
drush_test_list
drush_test_run Test-run command callback.
drush_test_run_validate
drush_test_xml_results
simpletest_drush_run_test Run a single test and display any failure messages.
simpletest_drush_test_groups Retrieve all test groups and sanitize their names to make them command-line friendly.
test_drush_command Implementation of hook_drush_command().

File

commands/core/test.drush.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Simpletest module drush integration.
  5. */
  6. /**
  7. * Implementation of hook_drush_command().
  8. */
  9. function test_drush_command() {
  10. $items = array();
  11. $items['test-run'] = array(
  12. 'description' => "Run tests. Note that you must use the --uri option.",
  13. 'arguments' => array(
  14. 'targets' => 'A test class, a test group. If omitted, a list of test classes and test groups is presented. Delimit multiple targets using commas.',
  15. ),
  16. 'examples' => array(
  17. 'test-run' => 'List all available classes and groups.',
  18. 'sudo -u apache test-run --all' => 'Run all available tests. Avoid permission related failures by running as web server user.',
  19. 'test-run XMLRPCBasicTestCase' => 'Run one test class.',
  20. 'test-run XML-RPC' => 'Run all classes in a XML-RPC group.',
  21. 'test-run XML-RPC,Filter' => 'Run all tests from multiple groups/classes.',
  22. 'test-run XMLRPCBasicTestCase --methods="testListMethods, testInvalidMessageParsing"' => 'Run particular methods in the specified class or group.',
  23. ),
  24. 'options' => array(
  25. 'all' => 'Run all available tests',
  26. 'methods' => 'A comma delimited list of methods that should be run within the test class. Defaults to all methods.',
  27. 'dirty' => 'Skip cleanup of temporary tables and files. Helpful for reading debug() messages and other post-mortem forensics.',
  28. 'xml' => 'Output verbose test results to a specified directory using the JUnit test reporting format. Useful for integrating with Jenkins.'
  29. ),
  30. 'drupal dependencies' => array('simpletest'),
  31. // If you DRUSH_BOOTSTRAP_DRUPAL_LOGIN, you fall victim to http://drupal.org/node/974768. We'd like
  32. // to not bootstrap at all but simpletest uses Drupal to discover test classes,
  33. // cache the lists of tests, file_prepare_directory(), variable lookup like
  34. // httpauth creds, copy pre-built registry table from testing side, etc.
  35. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
  36. );
  37. $items['test-clean'] = array(
  38. 'description' => "Clean temporary tables and files.",
  39. 'drupal dependencies' => array('simpletest'),
  40. );
  41. return $items;
  42. }
  43. // Command callback
  44. function drush_test_clean() {
  45. return simpletest_clean_environment();
  46. }
  47. // Validate hook
  48. function drush_test_run_validate($specs = NULL) {
  49. if (!drush_get_option('uri')) {
  50. // No longer needed?
  51. // return drush_set_error(dt("You must specify this site's URL using the --uri parameter."));
  52. }
  53. }
  54. /**
  55. * Test-run command callback.
  56. *
  57. * @specs
  58. * A comman delimited string of test classes or group names.
  59. */
  60. function drush_test_run($specs = NULL) {
  61. cache_clear_all('simpletest', 'cache');
  62. // Retrieve all tests and groups.
  63. list($groups, $all_tests) = drush_test_get_all_tests();
  64. if (drush_get_option('all')) {
  65. // Run all tests.
  66. foreach (array_keys($groups) as $group) {
  67. foreach (array_keys($groups[$group]) as $class) {
  68. drush_backend_invoke_args('test-run', array($class), array('--no-all'));
  69. }
  70. }
  71. return;
  72. }
  73. elseif (empty($specs)) {
  74. return drush_test_list($groups);
  75. }
  76. foreach (explode(',', $specs) as $spec) {
  77. $spec = trim($spec);
  78. // Specific test class specified.
  79. if (in_array($spec, $all_tests)) {
  80. simpletest_drush_run_test($spec);
  81. if (!drush_get_option('dirty')) {
  82. simpletest_clean_environment();
  83. }
  84. }
  85. // Specific group specified.
  86. else if (isset($groups[$spec])) {
  87. foreach (array_keys($groups[$spec]) as $class) {
  88. drush_do_command_redispatch('test-run', array($class));
  89. }
  90. }
  91. }
  92. }
  93. /**
  94. * Run a single test and display any failure messages.
  95. */
  96. function simpletest_drush_run_test($class) {
  97. if (drush_drupal_major_version() >= 7) {
  98. $test_id = db_insert('simpletest_test_id')
  99. ->useDefaults(array('test_id'))
  100. ->execute();
  101. }
  102. else {
  103. db_query('INSERT INTO {simpletest_test_id} (test_id) VALUES (default)');
  104. $test_id = db_last_insert_id('simpletest_test_id', 'test_id');
  105. }
  106. $test = new $class($test_id);
  107. if ($methods_string = drush_get_option('methods')) {
  108. foreach (explode(',', $methods_string) as $method) {
  109. $methods[] = trim($method);
  110. }
  111. $test->run($methods);
  112. }
  113. else {
  114. $test->run();
  115. }
  116. $info = $test->getInfo();
  117. $status = ((isset($test->results['#fail']) && $test->results['#fail'] > 0)
  118. || (isset($test->results['#exception']) && $test->results['#exception'] > 0) ? 'error' : 'ok');
  119. drush_log($info['name'] . ' ' . _simpletest_format_summary_line($test->results), $status);
  120. if ($dir = drush_get_option('xml')) {
  121. drush_test_xml_results($test_id, $dir);
  122. }
  123. // If there were some failed tests show them.
  124. if ($status === 'error') {
  125. if (drush_drupal_major_version() >= 7) {
  126. $args = array(':test_id' => $test_id);
  127. $result = db_query("SELECT * FROM {simpletest} WHERE test_id = :test_id AND status IN ('exception', 'fail') ORDER BY test_class, message_id", $args);
  128. foreach($result as $record) {
  129. drush_set_error('DRUSH_TEST_FAIL', dt("Test !function failed: !message", array('!function' => $record->function, '!message' => $record->message)));
  130. }
  131. }
  132. else {
  133. $result = db_query("SELECT * FROM {simpletest} WHERE test_id = %d AND status IN ('exception', 'fail') ORDER BY test_class, message_id", $test_id);
  134. while ($row = db_fetch_object($result)) {
  135. drush_set_error('DRUSH_TEST_FAIL', dt("Test !function failed: !message", array('!function' => $row->function, '!message' => $row->message)));
  136. }
  137. }
  138. }
  139. }
  140. /**
  141. * Retrieve all test groups and sanitize their names to make them command-line
  142. * friendly.
  143. */
  144. function simpletest_drush_test_groups($tests) {
  145. $groups = array();
  146. foreach (simpletest_categorize_tests($tests) as $name => $group) {
  147. $sanitized = strtr($name, array(' ' => ''));
  148. $groups[$sanitized] = $group;
  149. }
  150. return $groups;
  151. }
  152. // Print a listing of all available tests
  153. function drush_test_list($groups) {
  154. $rows[] = array(dt('Command'), dt('Description'));
  155. $rows[] = array('-------', '-----------');
  156. foreach ($groups as $group_name => $group_tests) {
  157. foreach ($group_tests as $test_class => $test_info) {
  158. if (!isset($rows[$test_info['group']])) {
  159. $rows[$test_info['group']] = array($group_name, $test_info['group']);
  160. }
  161. $rows[] = array(" {$test_class}", " {$test_info['name']}");
  162. }
  163. }
  164. return drush_print_table($rows, TRUE);
  165. }
  166. function drush_test_get_all_tests() {
  167. if (function_exists('simpletest_get_all_tests')) {
  168. $all_tests = simpletest_get_all_tests();
  169. $groups = simpletest_drush_test_groups($all_tests);
  170. }
  171. else {
  172. $groups = simpletest_test_get_all();
  173. $all_tests = array();
  174. foreach ($groups as $group) {
  175. $all_tests = array_merge($all_tests, array_keys($group));
  176. }
  177. }
  178. return array($groups, $all_tests);
  179. }
  180. /*
  181. * Display test results.
  182. */
  183. function drush_test_xml_results($test_id, $dir) {
  184. $dir = is_string($dir) ? $dir : '.';
  185. $results_map = array(
  186. 'pass' => 'Pass',
  187. 'fail' => 'Fail',
  188. 'exception' => 'Exception',
  189. );
  190. if (drush_drupal_major_version() >= 7) {
  191. $results = db_query("SELECT * FROM {simpletest} WHERE test_id = :test_id ORDER BY test_class, message_id", array(':test_id' => $test_id));
  192. }
  193. else {
  194. $result = db_query("SELECT * FROM {simpletest} WHERE test_id = %d ORDER BY test_class, message_id", $test_id);
  195. $results = array();
  196. while ($row = db_fetch_object($result)) {
  197. $results[] = $row;
  198. }
  199. }
  200. $test_class = '';
  201. $xml_files = array();
  202. foreach ($results as $result) {
  203. if (isset($results_map[$result->status])) {
  204. if ($result->test_class != $test_class) {
  205. // Display test class every time results are for new test class.
  206. if (isset($xml_files[$test_class])) {
  207. file_put_contents($dir . '/' . $test_class . '.xml', $xml_files[$test_class]['doc']->saveXML());
  208. unset($xml_files[$test_class]);
  209. }
  210. $test_class = $result->test_class;
  211. if (!isset($xml_files[$test_class])) {
  212. $doc = new DomDocument('1.0');
  213. $root = $doc->createElement('testsuite');
  214. $root = $doc->appendChild($root);
  215. $xml_files[$test_class] = array('doc' => $doc, 'suite' => $root);
  216. }
  217. }
  218. // Save the result into the XML:
  219. $case = $xml_files[$test_class]['doc']->createElement('testcase');
  220. $case->setAttribute('classname', $test_class);
  221. list($class, $name) = explode('->', $result->function, 2);
  222. $case->setAttribute('name', $name);
  223. if ($result->status == 'fail') {
  224. $fail = $xml_files[$test_class]['doc']->createElement('failure');
  225. $fail->setAttribute('type', 'failure');
  226. $fail->setAttribute('message', $result->message_group);
  227. $text = $xml_files[$test_class]['doc']->createTextNode($result->message);
  228. $fail->appendChild($text);
  229. $case->appendChild($fail);
  230. }
  231. $xml_files[$test_class]['suite']->appendChild($case);
  232. }
  233. }
  234. // Save the last one:
  235. if (isset($xml_files[$test_class])) {
  236. file_put_contents($dir . '/' . $test_class . '.xml', $xml_files[$test_class]['doc']->saveXML());
  237. unset($xml_files[$test_class]);
  238. }
  239. }