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_class
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().
test_test_run_complete Command argument complete callback.

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. /**
  44. * Command argument complete callback.
  45. *
  46. * @return
  47. * Array of test classes and groups.
  48. */
  49. function test_test_run_complete() {
  50. if (drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_FULL) == DRUSH_BOOTSTRAP_DRUPAL_FULL && module_exists('simpletest')) {
  51. // Retrieve all tests and groups.
  52. list($groups, $all_tests) = drush_test_get_all_tests();
  53. return array('values' => array_keys($groups) + $all_tests);
  54. }
  55. }
  56. // Command callback
  57. function drush_test_clean() {
  58. return simpletest_clean_environment();
  59. }
  60. // Validate hook
  61. function drush_test_run_validate($specs = NULL) {
  62. if (!drush_get_option('uri')) {
  63. // No longer needed?
  64. // return drush_set_error(dt("You must specify this site's URL using the --uri parameter."));
  65. }
  66. }
  67. /**
  68. * Test-run command callback.
  69. *
  70. * @specs
  71. * A comma delimited string of test classes or group names.
  72. */
  73. function drush_test_run($specs = NULL) {
  74. if (drush_drupal_major_version() > 7) {
  75. cache()->delete('simpletest');
  76. }
  77. else {
  78. cache_clear_all('simpletest', 'cache');
  79. }
  80. // Retrieve all tests and groups.
  81. list($groups, $all_tests) = drush_test_get_all_tests();
  82. if (drush_get_option('all')) {
  83. // Run all tests.
  84. foreach (array_keys($groups) as $group) {
  85. foreach (array_keys($groups[$group]) as $class) {
  86. drush_invoke_process('@self', 'test-run', array($class));
  87. }
  88. }
  89. return;
  90. }
  91. elseif (empty($specs)) {
  92. // No argument so list all groups/classes.
  93. return drush_test_list($groups);
  94. }
  95. elseif (strpos($specs, ',') === FALSE && in_array($specs, $all_tests)) {
  96. // We got one, valid class. Good, now run it.
  97. simpletest_drush_run_test($specs);
  98. if (!drush_get_option('dirty')) {
  99. simpletest_clean_environment();
  100. }
  101. return;
  102. }
  103. // See if we got a list of specs.
  104. foreach (explode(',', $specs) as $spec) {
  105. $spec = trim($spec);
  106. if (in_array($spec, $all_tests)) {
  107. // Specific test class specified.
  108. drush_test_run_class($spec);
  109. }
  110. if (isset($groups[$spec])) {
  111. // Specific group specified.
  112. foreach (array_keys($groups[$spec]) as $class) {
  113. drush_test_run_class($class);
  114. }
  115. }
  116. }
  117. }
  118. /*
  119. * Run a test class via drush_invoke_process()
  120. * @param string $class
  121. * The test class to run.
  122. *
  123. * @return
  124. * If the command could not be completed successfully, FALSE.
  125. * If the command was completed, this will return an associative
  126. * array containing the results of the API call.
  127. * @see drush_backend_get_result()
  128. */
  129. function drush_test_run_class($class) {
  130. $backend_options = array('output-label' => "$class ", 'integrate' => TRUE, 'output' => TRUE);
  131. $return = drush_invoke_process('@self', 'test-run', array($class), drush_redispatch_get_options(), $backend_options);
  132. return $return;
  133. }
  134. /**
  135. * Run a single test and display any failure messages.
  136. */
  137. function simpletest_drush_run_test($class) {
  138. if (drush_drupal_major_version() >= 7) {
  139. $test_id = db_insert('simpletest_test_id')
  140. ->useDefaults(array('test_id'))
  141. ->execute();
  142. }
  143. else {
  144. db_query('INSERT INTO {simpletest_test_id} (test_id) VALUES (default)');
  145. $test_id = db_last_insert_id('simpletest_test_id', 'test_id');
  146. }
  147. $test = new $class($test_id);
  148. if ($methods_string = drush_get_option('methods')) {
  149. foreach (explode(',', $methods_string) as $method) {
  150. $methods[] = trim($method);
  151. }
  152. $test->run($methods);
  153. }
  154. else {
  155. $test->run();
  156. }
  157. $info = $test->getInfo();
  158. $status = ((isset($test->results['#fail']) && $test->results['#fail'] > 0)
  159. || (isset($test->results['#exception']) && $test->results['#exception'] > 0) ? 'error' : 'ok');
  160. drush_log($info['name'] . ' ' . _simpletest_format_summary_line($test->results), $status);
  161. if ($dir = drush_get_option('xml')) {
  162. drush_test_xml_results($test_id, $dir);
  163. }
  164. // If there were some failed tests show them.
  165. if ($status === 'error') {
  166. if (drush_drupal_major_version() >= 7) {
  167. $args = array(':test_id' => $test_id);
  168. $result = db_query("SELECT * FROM {simpletest} WHERE test_id = :test_id AND status IN ('exception', 'fail') ORDER BY test_class, message_id", $args);
  169. foreach($result as $record) {
  170. drush_set_error('DRUSH_TEST_FAIL', dt("Test !function failed: !message", array('!function' => $record->function, '!message' => $record->message)));
  171. }
  172. }
  173. else {
  174. $result = db_query("SELECT * FROM {simpletest} WHERE test_id = %d AND status IN ('exception', 'fail') ORDER BY test_class, message_id", $test_id);
  175. while ($row = db_fetch_object($result)) {
  176. drush_set_error('DRUSH_TEST_FAIL', dt("Test !function failed: !message", array('!function' => $row->function, '!message' => $row->message)));
  177. }
  178. }
  179. }
  180. }
  181. /**
  182. * Retrieve all test groups and sanitize their names to make them command-line
  183. * friendly.
  184. */
  185. function simpletest_drush_test_groups($tests) {
  186. $groups = array();
  187. foreach (simpletest_categorize_tests($tests) as $name => $group) {
  188. $sanitized = strtr($name, array(' ' => ''));
  189. $groups[$sanitized] = $group;
  190. }
  191. return $groups;
  192. }
  193. // Print a listing of all available tests
  194. function drush_test_list($groups) {
  195. $rows[] = array(dt('Command'), dt('Description'));
  196. $rows[] = array('-------', '-----------');
  197. foreach ($groups as $group_name => $group_tests) {
  198. foreach ($group_tests as $test_class => $test_info) {
  199. if (!isset($rows[$test_info['group']])) {
  200. $rows[$test_info['group']] = array($group_name, $test_info['group']);
  201. }
  202. $rows[] = array(" {$test_class}", " {$test_info['name']}");
  203. }
  204. }
  205. return drush_print_table($rows, TRUE);
  206. }
  207. function drush_test_get_all_tests() {
  208. if (function_exists('simpletest_get_all_tests')) {
  209. $all_tests = simpletest_get_all_tests();
  210. $groups = simpletest_drush_test_groups($all_tests);
  211. }
  212. else {
  213. $groups = simpletest_test_get_all();
  214. $all_tests = array();
  215. foreach ($groups as $group) {
  216. $all_tests = array_merge($all_tests, array_keys($group));
  217. }
  218. }
  219. return array($groups, $all_tests);
  220. }
  221. /*
  222. * Display test results.
  223. */
  224. function drush_test_xml_results($test_id, $dir) {
  225. $dir = is_string($dir) ? $dir : '.';
  226. $results_map = array(
  227. 'pass' => 'Pass',
  228. 'fail' => 'Fail',
  229. 'exception' => 'Exception',
  230. );
  231. if (drush_drupal_major_version() >= 7) {
  232. $results = db_query("SELECT * FROM {simpletest} WHERE test_id = :test_id ORDER BY test_class, message_id", array(':test_id' => $test_id));
  233. }
  234. else {
  235. $result = db_query("SELECT * FROM {simpletest} WHERE test_id = %d ORDER BY test_class, message_id", $test_id);
  236. $results = array();
  237. while ($row = db_fetch_object($result)) {
  238. $results[] = $row;
  239. }
  240. }
  241. $test_class = '';
  242. $xml_files = array();
  243. foreach ($results as $result) {
  244. if (isset($results_map[$result->status])) {
  245. if ($result->test_class != $test_class) {
  246. // Display test class every time results are for new test class.
  247. if (isset($xml_files[$test_class])) {
  248. file_put_contents($dir . '/' . $test_class . '.xml', $xml_files[$test_class]['doc']->saveXML());
  249. unset($xml_files[$test_class]);
  250. }
  251. $test_class = $result->test_class;
  252. if (!isset($xml_files[$test_class])) {
  253. $doc = new DomDocument('1.0');
  254. $root = $doc->createElement('testsuite');
  255. $root = $doc->appendChild($root);
  256. $xml_files[$test_class] = array('doc' => $doc, 'suite' => $root);
  257. }
  258. }
  259. // Save the result into the XML:
  260. $case = $xml_files[$test_class]['doc']->createElement('testcase');
  261. $case->setAttribute('classname', $test_class);
  262. list($class, $name) = explode('->', $result->function, 2);
  263. $case->setAttribute('name', $name);
  264. if ($result->status == 'fail') {
  265. $fail = $xml_files[$test_class]['doc']->createElement('failure');
  266. $fail->setAttribute('type', 'failure');
  267. $fail->setAttribute('message', $result->message_group);
  268. $text = $xml_files[$test_class]['doc']->createTextNode($result->message);
  269. $fail->appendChild($text);
  270. $case->appendChild($fail);
  271. }
  272. $xml_files[$test_class]['suite']->appendChild($case);
  273. }
  274. }
  275. // Save the last one:
  276. if (isset($xml_files[$test_class])) {
  277. file_put_contents($dir . '/' . $test_class . '.xml', $xml_files[$test_class]['doc']->saveXML());
  278. unset($xml_files[$test_class]);
  279. }
  280. }